diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..27ce845 --- /dev/null +++ b/.gitignore @@ -0,0 +1,22 @@ + +# OS Stuff +*.DS_Store + +# Node Stuff +node_modules + +# IDE Stuff + +## phpStorm +.idea + +## SublimeText +*.sublime-workspace +*.sublime-project + +# Project Stuff +deploy.sh +deploy-old.sh +.idea +node_modules +/vendor/ \ No newline at end of file diff --git a/assets/css/builder.css b/assets/css/builder.css new file mode 100644 index 0000000..f26952e --- /dev/null +++ b/assets/css/builder.css @@ -0,0 +1,125 @@ +.nf-condition { + border: 1px solid #aaa; + margin-top: 20px; } + +.nf-condition-label { + margin-bottom: 10px; } + +.nf-first-when-container { + background: #D3D3D3; + padding: 10px; } + .nf-first-when-container::after { + clear: both; + content: ""; + display: block; } + +.nf-when-controls { + float: right; } + .nf-when-controls .fa { + color: #2BAAE7; + cursor: pointer; + font-size: 22px; } + .nf-when-controls .fa.nf-remove-condition { + color: red; } + +.nf-when-container::after, +.nf-then-container::after, +.nf-else-container::after { + clear: both; + content: ""; + display: block; } + +.nf-when-container .nf-one-fourth .fa, +.nf-then-container .nf-one-fourth .fa, +.nf-else-container .nf-one-fourth .fa { + font-size: 22px; + padding-top: 15px; } + +.nf-when-container .nf-one-fourth:first-child, +.nf-then-container .nf-one-fourth:first-child, +.nf-else-container .nf-one-fourth:first-child { + width: 7.5%; } + +.nf-when-container { + background: #D3D3D3; + padding: 0 10px 10px; } + .nf-when-container .nf-one-fourth { + width: 23%; } + .nf-when-container .nf-when::after { + clear: both; + content: ""; + display: block; } + +.nf-then-container { + background: #fefefe; + padding: 10px; } + .nf-then-container .nf-one-fourth { + width: 30.5%; } + .nf-then-container .nf-one-fourth:first-child { + padding-left: 12px; } + .nf-then-container .nf-then::after { + clear: both; + content: ""; + display: block; } + +.nf-else-container { + background: #ddd; + padding: 10px; } + .nf-else-container .nf-one-fourth { + width: 30.5%; } + .nf-else-container .nf-one-fourth:first-child { + padding-left: 12px; } + .nf-else-container .nf-else::after { + clear: both; + content: ""; + display: block; } + +.nf-add-when, +.nf-add-then, +.nf-add-else { + color: #2BAAE7; + cursor: pointer; + font-size: 22px; + margin-bottom: 5px; + padding-left: 12px; } + +.nf-remove-then, +.nf-remove-else, +.nf-remove-when { + color: red; + cursor: pointer; } + +.nf-one-fourth { + float: left; + margin-bottom: 15px; + padding: 0 2%; + width: 25%; } + .nf-one-fourth::after { + clear: both; + content: ""; + display: block; } + +.nf-condition.actions .nf-when-container { + padding: 20px; } + +.nf-condition.actions .nf-one-third { + width: 40%; } + .nf-condition.actions .nf-one-third:nth-child(2) { + width: 20%; } + .nf-condition.actions .nf-one-third.action-when-label { + padding-top: 16px; + text-align: center; + text-transform: uppercase; + font-weight: bold; } + +.nf-condition.actions .nf-one-fourth { + width: 30.8%; } + .nf-condition.actions .nf-one-fourth:first-child { + width: 7.5%; } + +.nf-condition.actions .nf-when::after { + clear: both; + content: ""; + display: block; } + +/*# sourceMappingURL=builder.css.map */ diff --git a/assets/css/builder.css.map b/assets/css/builder.css.map new file mode 100644 index 0000000..34b4107 --- /dev/null +++ b/assets/css/builder.css.map @@ -0,0 +1 @@ +{"version":3,"sources":["builder.scss"],"names":[],"mappings":"AAAA;EACI,uBAAuB;EACvB,iBAAiB,EACpB;;AACD;EACI,oBAAoB,EACvB;;AACD;EACI,oBAAoB;EACpB,cAAc,EAWjB;EAbD;IAIQ,YAAY;IACZ,YAAY;IACZ,eAAe,EAClB;;AAOL;EACI,aAAa,EAShB;EAVD;IAGQ,eAAe;IACf,gBAAgB;IAChB,gBAAgB,EAInB;IATL;MAOY,WAAW,EACd;;AAGT;;;EAIQ,YAAY;EACZ,YAAY;EACZ,eAAe,EAClB;;AAPL;;;EAUY,gBAAgB;EAChB,kBAAkB,EACrB;;AAZT;;;EAcY,YAAY,EACf;;AAGT;EACI,oBAAoB;EACpB,qBAAqB,EAWxB;EAbD;IAIQ,WAAW,EACd;EALL;IAQY,YAAY;IACZ,YAAY;IACZ,eAAe,EAClB;;AAGT;EACI,oBAAoB;EACpB,cAAc,EAcjB;EAhBD;IAIQ,aAAa,EAIhB;IARL;MAMY,mBAAmB,EACtB;EAPT;IAWY,YAAY;IACZ,YAAY;IACZ,eAAe,EAClB;;AAGT;EACI,iBAAiB;EACjB,cAAc,EAcjB;EAhBD;IAIQ,aAAa,EAIhB;IARL;MAMY,mBAAmB,EACtB;EAPT;IAWY,YAAY;IACZ,YAAY;IACZ,eAAe,EAClB;;AAGT;;;EAGI,eAAe;EACf,gBAAgB;EAChB,gBAAgB;EAChB,mBAAmB;EACnB,mBAAmB,EACtB;;AACD;;;EAGI,WAAW;EACX,gBAAgB,EACnB;;AAGD;EACI,YAAY;EACZ,oBAAoB;EACpB,cAAc;EACd,WAAW,EAMd;EAVD;IAMQ,YAAY;IACZ,YAAY;IACZ,eAAe,EAClB;;AAGL;EAEQ,cAAc,EACjB;;AAHL;EAKQ,WAAW,EAUd;EAfL;IAOY,WAAW,EACd;EART;IAUY,kBAAkB;IAClB,mBAAmB;IACnB,0BAA0B;IAC1B,kBAAkB,EACrB;;AAdT;EAiBQ,aAAa,EAIhB;EArBL;IAmBY,YAAY,EACf;;AApBT;EAwBY,YAAY;EACZ,YAAY;EACZ,eAAe,EAClB","file":"builder.css","sourcesContent":[".nf-condition {\n border: 1px solid #aaa;\n margin-top: 20px;\n}\n.nf-condition-label {\n margin-bottom: 10px;\n}\n.nf-first-when-container {\n background: #D3D3D3;\n padding: 10px;\n &::after {\n clear: both;\n content: \"\";\n display: block;\n }\n .nf-select,\n .nf-input {\n input {\n }\n }\n}\n.nf-when-controls {\n float: right;\n .fa {\n color: #2BAAE7;\n cursor: pointer;\n font-size: 22px;\n &.nf-remove-condition {\n color: red;\n }\n }\n}\n.nf-when-container,\n.nf-then-container,\n.nf-else-container {\n &::after {\n clear: both;\n content: \"\";\n display: block;\n }\n .nf-one-fourth {\n .fa {\n font-size: 22px;\n padding-top: 15px;\n }\n &:first-child {\n width: 7.5%;\n }\n }\n}\n.nf-when-container {\n background: #D3D3D3;\n padding: 0 10px 10px;\n .nf-one-fourth {\n width: 23%;\n }\n .nf-when {\n &::after {\n clear: both;\n content: \"\";\n display: block;\n }\n }\n}\n.nf-then-container {\n background: #fefefe;\n padding: 10px;\n .nf-one-fourth {\n width: 30.5%;\n &:first-child {\n padding-left: 12px;\n }\n }\n .nf-then {\n &::after {\n clear: both;\n content: \"\";\n display: block;\n }\n }\n}\n.nf-else-container {\n background: #ddd;\n padding: 10px;\n .nf-one-fourth {\n width: 30.5%;\n &:first-child {\n padding-left: 12px;\n }\n }\n .nf-else {\n &::after {\n clear: both;\n content: \"\";\n display: block;\n }\n }\n}\n.nf-add-when,\n.nf-add-then,\n.nf-add-else {\n color: #2BAAE7;\n cursor: pointer;\n font-size: 22px;\n margin-bottom: 5px;\n padding-left: 12px;\n}\n.nf-remove-then,\n.nf-remove-else,\n.nf-remove-when {\n color: red;\n cursor: pointer;\n}\n\n\n.nf-one-fourth {\n float: left;\n margin-bottom: 15px;\n padding: 0 2%;\n width: 25%;\n &::after {\n clear: both;\n content: \"\";\n display: block;\n }\n}\n\n.nf-condition.actions {\n .nf-when-container {\n padding: 20px;\n }\n .nf-one-third {\n width: 40%;\n &:nth-child(2) {\n width: 20%;\n }\n &.action-when-label {\n padding-top: 16px;\n text-align: center;\n text-transform: uppercase;\n font-weight: bold;\n }\n }\n .nf-one-fourth {\n width: 30.8%;\n &:first-child {\n width: 7.5%;\n }\n }\n .nf-when {\n &::after {\n clear: both;\n content: \"\";\n display: block;\n }\n }\n}\n"]} \ No newline at end of file diff --git a/assets/js/builder/controllers/clickControls.js b/assets/js/builder/controllers/clickControls.js new file mode 100644 index 0000000..6516556 --- /dev/null +++ b/assets/js/builder/controllers/clickControls.js @@ -0,0 +1,201 @@ +/** + * Listens for clicks on our different condition controls + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( [], function() { + var controller = Marionette.Object.extend( { + initialize: function() { + this.listenTo( nfRadio.channel( 'conditions' ), 'click:removeCondition', this.removeCondition ); + this.listenTo( nfRadio.channel( 'conditions' ), 'click:collapseCondition', this.collapseCondition ); + this.listenTo( nfRadio.channel( 'conditions' ), 'click:removeWhen', this.removeWhen ); + this.listenTo( nfRadio.channel( 'conditions' ), 'click:removeThen', this.removeThen ); + this.listenTo( nfRadio.channel( 'conditions' ), 'click:removeElse', this.removeElse ); + this.listenTo( nfRadio.channel( 'conditions' ), 'click:addWhen', this.addWhen ); + this.listenTo( nfRadio.channel( 'conditions' ), 'click:addThen', this.addThen ); + this.listenTo( nfRadio.channel( 'conditions' ), 'click:addElse', this.addElse ); + }, + + removeCondition: function( e, conditionModel ) { + var conditionCollection = conditionModel.collection; + conditionModel.collection.remove( conditionModel ); + + /* + * Register our remove condition event with our changes manager + */ + + var label = { + object: 'Condition', + label: nfcli18n.clickControlsConditionlabel, + change: 'Removed', + dashicon: 'dismiss' + }; + + var data = { + collection: conditionCollection + } + + nfRadio.channel( 'changes' ).request( 'register:change', 'removeCondition', conditionModel, null, label, data ); + + // Set our 'clean' status to false so that we get a notice to publish changes + nfRadio.channel( 'app' ).request( 'update:setting', 'clean', false ); + nfRadio.channel( 'app' ).request( 'update:db' ); + }, + + collapseCondition: function( e, conditionModel ) { + conditionModel.set( 'collapsed', ! conditionModel.get( 'collapsed' ) ); + }, + + removeWhen: function( e, whenModel ) { + var collection = whenModel.collection; + this.removeItem( whenModel ); + /* + * Register our remove when change. + */ + + var label = { + object: 'Condition - When', + label: nfcli18n.clickControlsConditionWhen, + change: 'Removed', + dashicon: 'dismiss' + }; + + var data = { + collection: collection + } + + nfRadio.channel( 'changes' ).request( 'register:change', 'removeWhen', whenModel, null, label, data ); + }, + + removeThen: function( e, thenModel ) { + var collection = thenModel.collection; + this.removeItem( thenModel ); + /* + * Register our remove then change. + */ + + var label = { + object: 'Condition - Then', + label: nfcli18n.clickControlsConditionThen, + change: 'Removed', + dashicon: 'dismiss' + }; + + var data = { + collection: collection + } + + nfRadio.channel( 'changes' ).request( 'register:change', 'removeThen', thenModel, null, label, data ); + }, + + removeElse: function( e, elseModel ) { + var collection = elseModel.collection; + this.removeItem( elseModel ); + /* + * Register our remove else change. + */ + + var label = { + object: 'Condition - Else', + label: nfcli18n.clickControlsConditionElse, + change: 'Removed', + dashicon: 'dismiss' + }; + + var data = { + collection: collection + } + + nfRadio.channel( 'changes' ).request( 'register:change', 'removeElse', elseModel, null, label, data ); + + }, + + removeItem: function( itemModel ) { + itemModel.collection.remove( itemModel ); + + // Set our 'clean' status to false so that we get a notice to publish changes + nfRadio.channel( 'app' ).request( 'update:setting', 'clean', false ); + nfRadio.channel( 'app' ).request( 'update:db' ); + }, + + addWhen: function( e, conditionModel ) { + var whenModel = conditionModel.get( 'when' ).add( {} ); + + /* + * Register our add when as a change. + */ + + var label = { + object: 'Condition - When Criteron', + label: nfcli18n.clickControlsConditionWhenCriteron, + change: 'Added', + dashicon: 'plus-alt' + }; + + var data = { + conditionModel: conditionModel + } + + nfRadio.channel( 'changes' ).request( 'register:change', 'addWhen', whenModel, null, label, data ); + + // Set our 'clean' status to false so that we get a notice to publish changes + nfRadio.channel( 'app' ).request( 'update:setting', 'clean', false ); + nfRadio.channel( 'app' ).request( 'update:db' ); + }, + + addThen: function( e, conditionModel ) { + var thenModel = conditionModel.get( 'then' ).add( {} ); + + /* + * Register our add then as a change. + */ + + var label = { + object: 'Condition - Do Item', + label: nfcli18n.clickControlsConditionDoItem, + change: 'Added', + dashicon: 'plus-alt' + }; + + var data = { + conditionModel: conditionModel + } + + nfRadio.channel( 'changes' ).request( 'register:change', 'addThen', thenModel, null, label, data ); + + // Set our 'clean' status to false so that we get a notice to publish changes + nfRadio.channel( 'app' ).request( 'update:setting', 'clean', false ); + nfRadio.channel( 'app' ).request( 'update:db' ); + }, + + addElse: function( e, conditionModel ) { + var elseModel = conditionModel.get( 'else' ).add( {} ); + + /* + * Register our add when as a change. + */ + + var label = { + object: 'Condition - Else Item', + label: nfcli18n.clickControlsConditionElseItem, + change: 'Added', + dashicon: 'plus-alt' + }; + + var data = { + conditionModel: conditionModel + } + + nfRadio.channel( 'changes' ).request( 'register:change', 'addElse', elseModel, null, label, data ); + + // Set our 'clean' status to false so that we get a notice to publish changes + nfRadio.channel( 'app' ).request( 'update:setting', 'clean', false ); + nfRadio.channel( 'app' ).request( 'update:db' ); + } + + }); + + return controller; +} ); diff --git a/assets/js/builder/controllers/coreComparators.js b/assets/js/builder/controllers/coreComparators.js new file mode 100644 index 0000000..4d31b16 --- /dev/null +++ b/assets/js/builder/controllers/coreComparators.js @@ -0,0 +1,116 @@ +/** + * Returns an object with each comparator we'd like to use. + * This covers all the core field types. + * + * Add-ons can copy this code structure in order to get custom "comparators" for conditions. + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( [], function() { + var controller = Marionette.Object.extend( { + initialize: function() { + nfRadio.channel( 'conditions-checkbox' ).reply( 'get:comparators', this.getCheckboxComparators ); + nfRadio.channel( 'conditions-listradio' ).reply( 'get:comparators', this.getListSingleComparators ); + nfRadio.channel( 'conditions-listselect' ).reply( 'get:comparators', this.getListSingleComparators ); + nfRadio.channel( 'conditions-list' ).reply( 'get:comparators', this.getListComparators ); + nfRadio.channel( 'conditions-date' ).reply( 'get:comparators', this.getDateComparators ); + }, + + getCheckboxComparators: function( defaultComparators ) { + return { + is: { + label: nfcli18n.coreComparatorsIs, + value: 'equal' + }, + + isnot: { + label: nfcli18n.coreComparatorsIsNot, + value: 'notequal' + } + } + }, + + getListComparators: function( defaultComparators ) { + return { + has: { + label: nfcli18n.coreComparatorsHasSelected, + value: 'contains' + }, + + hasnot: { + label: nfcli18n.coreComparatorsDoesNotHaveSelected, + value: 'notcontains' + } + } + }, + + getListSingleComparators: function( defaultComparators, currentComparator ) { + /* + * Radio and Select lists need to use equal and notequal. + * In previous versions, however, they used contains and notcontains. + * In order to keep forms working that were made in those previous versions, + * we check to see if the currentComparator is contains or notcontains. + * If it is, we return those values; else we return equal or not equal. + */ + if ( 'contains' == currentComparator || 'notcontains' == currentComparator ) { + return { + has: { + label: nfcli18n.coreComparatorsHasSelected, + value: 'contains' + }, + + hasnot: { + label: nfcli18n.coreComparatorsDoesNotHaveSelected, + value: 'notcontains' + } + } + } + + return { + has: { + label: nfcli18n.coreComparatorsHasSelected, + value: 'equal' + }, + + hasnot: { + label: nfcli18n.coreComparatorsDoesNotHaveSelected, + value: 'notequal' + } + } + }, + + getDateComparators: function( defaultComparators ) { + return { + before: { + label: nfcli18n.coreComparatorsBefore, + value: 'less' + }, + + onorbefore: { + label: nfcli18n.coreComparatorsOnOrBefore, + value: 'lessequal' + }, + + equal: { + label: nfcli18n.coreComparatorsIs, + value: 'equal' + }, + + onorafter: { + label: nfcli18n.coreComparatorsOnOrAfter, + value: 'greaterequal' + }, + + after: { + label: nfcli18n.coreComparatorsAfter, + value: 'greater' + } + } + }, + + }); + + return controller; +} ); diff --git a/assets/js/builder/controllers/coreTriggers.js b/assets/js/builder/controllers/coreTriggers.js new file mode 100644 index 0000000..2699b09 --- /dev/null +++ b/assets/js/builder/controllers/coreTriggers.js @@ -0,0 +1,68 @@ +/** + * Returns an object with each trigger we'd like to use. + * This covers all the core field types. + * + * Add-ons can copy this code structure in order to get custom "triggers" for conditions. + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( [], function() { + var controller = Marionette.Object.extend( { + initialize: function() { + nfRadio.channel( 'conditions-list' ).reply( 'get:triggers', this.getListTriggers ); + nfRadio.channel( 'conditions-submit' ).reply( 'get:triggers', this.getSubmitTriggers ); + nfRadio.channel( 'conditions-html' ).reply( 'get:triggers', this.getHTMLTriggers ); + nfRadio.channel( 'conditions-hr' ).reply( 'get:triggers', this.getDividerTriggers ); + nfRadio.channel( 'conditions-hidden' ).reply( 'get:triggers', this.getHiddenTriggers ); + }, + + getListTriggers: function( defaultTriggers ) { + var triggers = _.extend( defaultTriggers, { + select_option: { + label: nfcli18n.coreTriggersSelectOption, + value: 'select_option' + }, + + deselect_option: { + label: nfcli18n.coreTriggersDeselectOption, + value: 'deselect_option' + }, + + show_option: { + label: nfcli18n.coreTriggersShowOption, + value: 'show_option' + }, + + hide_option: { + label: nfcli18n.coreTriggersHideOption, + value: 'hide_option' + } + } ); + + var triggers = _.omit( defaultTriggers, 'change_value' ); + + return triggers; + }, + + getSubmitTriggers: function( defaultTriggers ) { + return _.omit( defaultTriggers, ['change_value', 'set_required', 'unset_required'] ); + }, + + getHTMLTriggers: function( defaultTriggers ) { + return _.omit( defaultTriggers, ['set_required', 'unset_required'] ); + }, + + getDividerTriggers: function( defaultTriggers ) { + return _.omit( defaultTriggers, ['change_value', 'set_required', 'unset_required'] ); + }, + + getHiddenTriggers: function( defaultTriggers ) { + return _.omit( defaultTriggers, ['set_required', 'unset_required'] ); + } + + }); + + return controller; +} ); \ No newline at end of file diff --git a/assets/js/builder/controllers/coreValues.js b/assets/js/builder/controllers/coreValues.js new file mode 100644 index 0000000..6c3fc39 --- /dev/null +++ b/assets/js/builder/controllers/coreValues.js @@ -0,0 +1,117 @@ +/** + * Returns the type of input value we'd like to use. + * This covers all the core field types. + * + * Add-ons can copy this code structure in order to get custom "values" for conditions. + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( [], function() { + var controller = Marionette.Object.extend( { + initialize: function() { + nfRadio.channel( 'conditions-checkbox' ).reply( 'get:valueInput', this.getCheckboxValue ); + nfRadio.channel( 'conditions-list' ).reply( 'get:valueInput', this.getListValue ); + nfRadio.channel( 'conditions-listcountry' ).reply( 'get:valueInput', this.getListCountryValue ); + nfRadio.channel( 'conditions-date' ).reply( 'get:valueInput', this.getDateValue ); + }, + + getCheckboxValue: function( key, trigger, value ) { + /* + * Checks our values ensures they've been converted to strings and + * sets the value. + */ + if( 1 == value && value.length > 1 ) { + value = 'checked'; + } else if( 0 == value && value.length > 1 ) { + value = 'unchecked'; + } else if( 0 == value.length ){ + value = ''; + } + + var template = Backbone.Radio.channel( 'app' ).request( 'get:template', '#tmpl-nf-cl-value-checkbox' ); + return template( { value: value } ); + }, + + getListValue: function( key, trigger, value ) { + var fieldModel = nfRadio.channel( 'fields' ).request( 'get:field', key ); + var options = fieldModel.get( 'options' ); + var template = Backbone.Radio.channel( 'app' ).request( 'get:template', '#tmpl-nf-cl-value-list' ); + return template( { options: options, value: value } ); + }, + + getListCountryValue: function( key, trigger, value ) { + var fieldModel = nfRadio.channel( 'fields' ).request( 'get:field', key ); + var options = fieldModel.get( 'options' ); + var template = Backbone.Radio.channel( 'app' ).request( 'get:template', '#tmpl-nf-cl-value-list' ); + + options.reset(); + _.each( nfListCountries, function( value, label ) { + options.add( { label: label, value: value } ); + }); + + return template( { options: options, value: value } ); + }, + + getDateValue: function( key, trigger, value ) { + let fieldModel = nfRadio.channel( 'fields' ).request( 'get:field', key ); + let dateMode = fieldModel.get( 'date_mode' ); + + if ( 'undefined' == typeof dateMode ) { + dateMode = 'date_only'; + } + + let timestamp = value * 1000; + let dateObject = new Date( timestamp ); + dateObject = new Date( dateObject.getUTCFullYear(), dateObject.getUTCMonth(), dateObject.getUTCDate(), dateObject.getUTCHours(), dateObject.getUTCMinutes() ); + + let selectedHour = dateObject.getHours(); + let selectedMinute = dateObject.getMinutes(); + + let hourSelect = ''; + + let minuteSelect = ''; + + let date = moment( dateObject.toUTCString() ).format( 'YYYY-MM-DD' ); + if ( '1970-01-01' == date ) { + date = ''; + } + + let template = Backbone.Radio.channel( 'app' ).request( 'get:template', '#tmpl-nf-cl-value-date-' + dateMode ); + return template( { value: value, date: date, hourSelect: hourSelect, minuteSelect: minuteSelect } ); + }, + + + }); + + return controller; +} ); diff --git a/assets/js/builder/controllers/fieldDate.js b/assets/js/builder/controllers/fieldDate.js new file mode 100644 index 0000000..3f2e613 --- /dev/null +++ b/assets/js/builder/controllers/fieldDate.js @@ -0,0 +1,50 @@ +/** + * Listens for changes in the "extra" settings in "when" settings. + * We use this for the date field to update the "value" to a timestamp when we change a date value setting. + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( [], function() { + var controller = Marionette.Object.extend( { + initialize: function() { + this.listenTo( nfRadio.channel( 'conditions' ), 'change:extra', this.maybeUpdateSetting ); + }, + + maybeUpdateSetting: function( e, dataModel ) { + let dateString = ''; + // Get our date + let date = jQuery( e.target ).parent().parent().find( "[data-type='date']" ).val(); + if ( 'undefined' == typeof date ) { + date = '1970-01-02'; + } + dateString += date + 'T'; + + // Get our hour + let hour = jQuery( e.target ).parent().parent().find( "[data-type='hour']" ).val(); + if ( 'undefined' == typeof hour ) { + hour = '00'; + } + dateString += hour + ':'; + + // Get our minute + let minute = jQuery( e.target ).parent().parent().find( "[data-type='minute']" ).val(); + if ( 'undefined' == typeof minute ) { + minute = '00'; + } + dateString += minute + 'Z'; + + // Build a timestamp + let dateObject = new Date( dateString ); + let timestamp = Math.floor( dateObject.getTime() / 1000 ); + + // Update our value with the timestamp + dataModel.set( 'value', timestamp ); + nfRadio.channel( 'app' ).request( 'update:setting', 'clean', false ); + }, + + }); + + return controller; +} ); \ No newline at end of file diff --git a/assets/js/builder/controllers/filters.js b/assets/js/builder/controllers/filters.js new file mode 100644 index 0000000..db32c02 --- /dev/null +++ b/assets/js/builder/controllers/filters.js @@ -0,0 +1,28 @@ +/** + * Register filters for our when/then key groups/settings. + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( [], function() { + var controller = Marionette.Object.extend( { + filters: [], + + initialize: function() { + nfRadio.channel( 'conditions' ).reply( 'add:groupFilter', this.addFilter, this ); + nfRadio.channel( 'conditions' ).reply( 'get:groupFilters', this.getFilters, this ); + }, + + addFilter: function( callback ) { + this.filters.push( callback ); + }, + + getFilters: function() { + return this.filters; + } + + }); + + return controller; +} ); diff --git a/assets/js/builder/controllers/getDrawerHeader.js b/assets/js/builder/controllers/getDrawerHeader.js new file mode 100644 index 0000000..fc4aabf --- /dev/null +++ b/assets/js/builder/controllers/getDrawerHeader.js @@ -0,0 +1,20 @@ +/** + * Returns the view to use in the drawer header. + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( [ 'views/drawerHeader' ], function( DrawerHeaderView ) { + var controller = Marionette.Object.extend( { + initialize: function() { + nfRadio.channel( 'conditional_logic' ).reply( 'get:drawerHeaderView', this.getDrawerHeaderView, this ); + }, + + getDrawerHeaderView: function() { + return DrawerHeaderView; + } + }); + + return controller; +} ); diff --git a/assets/js/builder/controllers/loadControllers.js b/assets/js/builder/controllers/loadControllers.js new file mode 100644 index 0000000..d43b6bf --- /dev/null +++ b/assets/js/builder/controllers/loadControllers.js @@ -0,0 +1,68 @@ +/** + * Loads all of our custom controllers. + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( [ + 'controllers/templateHelpers', + 'controllers/returnChildView', + 'models/conditionCollection', + 'views/drawerHeader', + 'controllers/newCondition', + 'controllers/updateSettings', + 'controllers/clickControls', + 'controllers/undo', + // 'controllers/maybeModifyElse', + 'controllers/coreValues', + 'controllers/coreComparators', + 'controllers/coreTriggers', + 'controllers/getDrawerHeader', + 'controllers/trackKeyChanges', + 'controllers/maybeConvertConditions', + 'controllers/filters', + 'controllers/fieldDate' + + ], function( + + TemplateHelpers, + ReturnChildView, + ConditionCollection, + DrawerHeaderView, + NewCondition, + UpdateSettings, + ClickControls, + Undo, + // MaybeModifyElse, + CoreValues, + CoreComparators, + CoreTriggers, + GetDrawerHeader, + TrackKeyChanges, + MaybeConvertConditions, + Filters, + FieldDate + ) { + var controller = Marionette.Object.extend( { + initialize: function() { + new TemplateHelpers(); + new ReturnChildView(); + new NewCondition(); + new UpdateSettings(); + new ClickControls(); + new Undo(); + // new MaybeModifyElse(); + new CoreValues(); + new CoreComparators(); + new CoreTriggers(); + new GetDrawerHeader(); + new TrackKeyChanges(); + new MaybeConvertConditions(); + new Filters(); + new FieldDate(); + } + }); + + return controller; +} ); diff --git a/assets/js/builder/controllers/maybeConvertConditions.js b/assets/js/builder/controllers/maybeConvertConditions.js new file mode 100644 index 0000000..d06d7bd --- /dev/null +++ b/assets/js/builder/controllers/maybeConvertConditions.js @@ -0,0 +1,26 @@ +/** + * When we init our action model, check to see if we have a 'conditions' setting that needs to be converted into a collection. + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( [ 'models/conditionModel' ], function( ConditionModel ) { + var controller = Marionette.Object.extend( { + initialize: function() { + this.listenTo( nfRadio.channel( 'actions' ), 'init:actionModel', this.maybeConvertConditions ); + }, + + maybeConvertConditions: function( actionModel ) { + var conditions = actionModel.get( 'conditions' ); + if ( ! conditions ) { + actionModel.set( 'conditions', new ConditionModel() ); + } else if ( false === conditions instanceof Backbone.Model ) { + actionModel.set( 'conditions', new ConditionModel( conditions ) ); + } + } + + }); + + return controller; +} ); diff --git a/assets/js/builder/controllers/maybeModifyElse.js b/assets/js/builder/controllers/maybeModifyElse.js new file mode 100644 index 0000000..81f0c18 --- /dev/null +++ b/assets/js/builder/controllers/maybeModifyElse.js @@ -0,0 +1,48 @@ +/** + * Listen to changes of any "then" conditions. + * + * If the value is 'show_field' or 'hide_field' and we have not yet added an "opposite," set an "opposite" action in the "else" section. + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( [], function() { + var controller = Marionette.Object.extend( { + initialize: function() { + this.listenTo( nfRadio.channel( 'conditions' ), 'change:then', this.maybeAddElse ); + }, + + maybeAddElse: function( e, thenModel ) { + var opposite = false; + /* + * TODO: Make this more dynamic. + * Currently, show, hide, show option, and hide option are hard-coded here. + */ + var trigger = jQuery( e.target ).val(); + switch( trigger ) { + case 'show_field': + opposite = 'hide_field'; + break; + case 'hide_field': + opposite = 'show_field'; + break; + case 'show_option': + // opposite = 'hide_option'; + break; + case 'hide_option': + // opposite = 'show_option'; + break; + } + + if ( opposite ) { + var conditionModel = thenModel.collection.options.conditionModel + if( 'undefined' == typeof conditionModel.get( 'else' ).findWhere( { 'key': thenModel.get( 'key' ), 'trigger': opposite } ) ) { + conditionModel.get( 'else' ).add( { type: thenModel.get( 'type' ), key: thenModel.get( 'key' ), trigger: opposite } ); + } + } + } + }); + + return controller; +} ); diff --git a/assets/js/builder/controllers/newCondition.js b/assets/js/builder/controllers/newCondition.js new file mode 100644 index 0000000..771b37a --- /dev/null +++ b/assets/js/builder/controllers/newCondition.js @@ -0,0 +1,40 @@ +/** + * Adds a new condition when the add new button is clicked. + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( [ 'models/whenCollection', 'models/whenModel' ], function( WhenCollection, WhenModel ) { + var controller = Marionette.Object.extend( { + initialize: function() { + this.listenTo( nfRadio.channel( 'conditions' ), 'click:addNew', this.addNew ); + }, + + addNew: function( e ) { + var conditionCollection = nfRadio.channel( 'settings' ).request( 'get:setting', 'conditions' ); + var conditionModel = conditionCollection.add( {} ); + + // Add our condition addition to our change log. + var label = { + object: 'Condition', + label: nfcli18n.newConditionCondition, + change: 'Added', + dashicon: 'plus-alt' + }; + + var data = { + collection: conditionCollection + } + + nfRadio.channel( 'changes' ).request( 'register:change', 'addCondition', conditionModel, null, label, data ); + + // Set our 'clean' status to false so that we get a notice to publish changes + nfRadio.channel( 'app' ).request( 'update:setting', 'clean', false ); + nfRadio.channel( 'app' ).request( 'update:db' ); + } + + }); + + return controller; +} ); diff --git a/assets/js/builder/controllers/returnChildView.js b/assets/js/builder/controllers/returnChildView.js new file mode 100644 index 0000000..7baf81e --- /dev/null +++ b/assets/js/builder/controllers/returnChildView.js @@ -0,0 +1,26 @@ +/** + * Returns the childview we need to use for our conditional logic form settings. + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( [ 'views/advanced/conditionCollection', 'views/actions/conditionLayout' ], function( AdvancedView, ActionsView ) { + var controller = Marionette.Object.extend( { + initialize: function() { + nfRadio.channel( 'advanced_conditions' ).reply( 'get:settingChildView', this.getAdvancedChildView ); + nfRadio.channel( 'action_conditions' ).reply( 'get:settingChildView', this.getActionChildView ); + }, + + getAdvancedChildView: function( settingModel ) { + return AdvancedView; + }, + + getActionChildView: function( settingModel ) { + return ActionsView; + } + + }); + + return controller; +} ); diff --git a/assets/js/builder/controllers/templateHelpers.js b/assets/js/builder/controllers/templateHelpers.js new file mode 100644 index 0000000..2b46416 --- /dev/null +++ b/assets/js/builder/controllers/templateHelpers.js @@ -0,0 +1,322 @@ +/** + * Adds template helpers for the fields conditional logic setting type + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( [], function() { + var controller = Marionette.Object.extend( { + initialize: function() { + this.listenTo( nfRadio.channel( 'conditions' ), 'init:model', this.addTemplateHelpers ); + this.listenTo( nfRadio.channel( 'conditions' ), 'init:thenModel', this.addTemplateHelpers ); + this.listenTo( nfRadio.channel( 'conditions' ), 'init:whenModel', this.addTemplateHelpers ); + this.listenTo( nfRadio.channel( 'conditions' ), 'init:elseModel', this.addTemplateHelpers ); + + }, + + addTemplateHelpers: function( model ) { + model.set( 'renderKeySelect', this.renderKeySelect ); + model.set( 'renderComparators', this.renderComparators ); + model.set( 'renderTriggers', this.renderTriggers ); + model.set( 'renderWhenValue', this.renderWhenValue ); + model.set( 'renderItemValue', this.renderItemValue ); + }, + + renderKeySelect: function( currentValue, modelType ) { + + var groups = [] + + var fieldCollection = nfRadio.channel( 'fields' ).request( 'get:collection' ); + var fieldOptions = _.chain( fieldCollection.models ) + .filter( function( field ) { return ! nfRadio.channel( 'conditions-key-select-field-' + field.get( 'type' ) ).request( 'hide', modelType ) || false; }) + .filter( function( field ) { + + // filter out these fields for the when condition + var notForWhen = [ 'submit', 'hr', 'html', 'save', 'file-upload', 'password', 'passwordconfirm', 'product' ]; + + if( field.get( 'key' ) === currentValue ) { + notForWhen = notForWhen.splice( notForWhen.indexOf( field.get( 'type' ), 1) ); + } + + if( notForWhen.includes( field.get( 'type' ) ) && 'when' === modelType ) { + return false; + } + + return true; + }) + .map( function( field ) { + var label = field.get( 'label' ) + if( 'undefined' !== typeof field.get( 'admin_label' ) && 0 < field.get( 'admin_label' ).length ){ + label = field.get( 'admin_label' ); + } + return { key: field.get( 'key' ), label: label }; } + ) + .sortBy( function( field ){ + return field.label.toLowerCase(); + } ) + .value(); + + groups.push( { label: 'Fields', type: 'field', options: fieldOptions } ); + + var calcCollection = nfRadio.channel( 'settings' ).request( 'get:setting', 'calculations' ); + + /* + * If we are working on a 'when' model and we have calculations, add them to our select options. + */ + if ( 'when' == modelType && 0 < calcCollection.length ) { + var calcOptions = calcCollection.map( function( calc ) { + return { key: calc.get( 'name' ), label: calc.get( 'name' ) }; + } ); + + groups.push( { label: 'Calculations', type: 'calc', options: calcOptions } ); + } + + /* + * Pass our groups through any 'when/then' group filters we have. + */ + var filters = nfRadio.channel( 'conditions' ).request( 'get:groupFilters' ); + _.each( filters, function( filter ) { + groups = filter( groups, modelType ); + } ); + + /* + * Use a template to get our field select + */ + var template = Backbone.Radio.channel( 'app' ).request( 'get:template', '#tmpl-nf-cl-key-select' ); + + var tmp = template( { groups: groups, currentValue: currentValue } ); + return tmp; + }, + + renderComparators: function( type, key, currentComparator ) { + var defaultComparators = { + equal: { + label: nfcli18n.templateHelperEquals, + value: 'equal' + }, + + notequal: { + label: nfcli18n.templateHelperDoesNotEqual, + value: 'notequal' + }, + + contains: { + label: nfcli18n.templateHelperContains, + value: 'contains' + }, + + notcontains: { + label: nfcli18n.templateHelperDoesNotContain, + value: 'notcontains' + }, + + greater: { + label: nfcli18n.templateHelperGreaterThan, + value: 'greater' + }, + + less: { + label: nfcli18n.templateHelperLessThan, + value: 'less' + } + }; + + if ( key ) { + /* + * This could be a field or a calculation key. If it's a calc key, get the calc model. + */ + if ( 'calc' == type ) { + var comparators = _.omit( defaultComparators, 'contains', 'notcontains' ); + _.extend( comparators, { + lessequal: { + label: nfcli18n.templateHelperLessThanOrEqual, + value: 'lessequal' + }, + + greaterequal: { + label: nfcli18n.templateHelperGreaterThanOrEqual, + value: 'greaterequal' + } + } ); + } else { + /* + * Send out a radio request for an html value on a channel based upon the field type. + * + * Get our field by key + * Get our field type model + * + * Send out a message on the type channel + * If we don't get a response, send a message out on the parent type channel + */ + var fieldModel = nfRadio.channel( 'fields' ).request( 'get:field', key ); + + if( fieldModel ) { + var comparators = nfRadio.channel('conditions-' + fieldModel.get('type')).request('get:comparators', defaultComparators, currentComparator ); + if (!comparators) { + var typeModel = nfRadio.channel('fields').request('get:type', fieldModel.get('type')); + comparators = nfRadio.channel('conditions-' + typeModel.get('parentType')).request('get:comparators', defaultComparators, currentComparator ) || defaultComparators; + } + } else { + var comparators = defaultComparators; + } + } + } else { + var comparators = defaultComparators; + } + + /* + * Use a template to get our comparator select + */ + var template = Backbone.Radio.channel( 'app' ).request( 'get:template', '#tmpl-nf-cl-comparators' ); + return template( { comparators: comparators, currentComparator: currentComparator } ); + }, + + renderTriggers: function( type, key, currentTrigger, value ) { + var defaultTriggers = { + show_field: { + label: nfcli18n.templateHelperShowField, + value: 'show_field' + }, + + hide_field: { + label: nfcli18n.templateHelperHideField, + value: 'hide_field' + }, + + change_value: { + label: nfcli18n.templateHelperChangeValue, + value: 'change_value' + }, + + set_required: { + label: nfcli18n.templateHelperSetRequired, + value: 'set_required' + }, + + unset_required: { + label: nfcli18n.templateHelperUnsetRequired, + value: 'unset_required' + } + }; + + if ( key && 'field' == type ) { + /* + * Send out a radio request for an html value on a channel based upon the field type. + * + * Get our field by key + * Get our field type model + * + * Send out a message on the type channel + * If we don't get a response, send a message out on the parent type channel + */ + var fieldModel = nfRadio.channel( 'fields' ).request( 'get:field', key ); + + if( 'undefined' != typeof fieldModel ) { + var typeModel = nfRadio.channel('fields').request('get:type', fieldModel.get('type')); + + var triggers = nfRadio.channel('conditions-' + fieldModel.get('type')).request('get:triggers', defaultTriggers); + if (!triggers) { + triggers = nfRadio.channel('conditions-' + typeModel.get('parentType')).request('get:triggers', defaultTriggers) || defaultTriggers; + } + } else { + var triggers = nfRadio.channel( 'conditions-' + type ).request( 'get:triggers', defaultTriggers ) || defaultTriggers; + } + } else { + var triggers = nfRadio.channel( 'conditions-' + type ).request( 'get:triggers', defaultTriggers ) || defaultTriggers; + } + + + /* + * Use a template to get our comparator select + */ + var template = Backbone.Radio.channel( 'app' ).request( 'get:template', '#tmpl-nf-cl-triggers' ); + return template( { triggers: triggers, currentTrigger: currentTrigger } ); + }, + + renderWhenValue: function( type, key, comparator, value ) { + /* + * Use a template to get our value + */ + var template = Backbone.Radio.channel( 'app' ).request( 'get:template', '#tmpl-nf-cl-value-default' ); + var defaultHTML = template( { value: value } ); + + /* + * If we have a key and it's not a calc, get our field type based HTML. + */ + if ( key && 'calc' != type ) { + /* + * Send out a radio request for an html value on a channel based upon the field type. + * + * Get our field by key + * Get our field type model + * + * Send out a message on the type channel + * If we don't get a response, send a message out on the parent type channel + */ + var fieldModel = nfRadio.channel( 'fields' ).request( 'get:field', key ); + + if( fieldModel ) { + var html = nfRadio.channel('conditions-' + fieldModel.get('type')).request('get:valueInput', key, comparator, value); + if (!html) { + var typeModel = nfRadio.channel('fields').request('get:type', fieldModel.get('type')); + html = nfRadio.channel('conditions-' + typeModel.get('parentType')).request('get:valueInput', key, comparator, value) || defaultHTML; + } + } else { + html = defaultHTML; + } + } else { + var html = defaultHTML; + } + + return html; + }, + + renderItemValue: function( key, trigger, value ) { + /* + * Use a template to get our value + * + * TODO: This should be much more dynamic. + * At the moment, we manually check to see if we are doing a "change_value" or similar trigger. + */ + if ( trigger != 'change_value' + && trigger != 'select_option' + && trigger != 'deselect_option' + && trigger != 'show_option' + && trigger != 'hide_option' + ) { + return ''; + } + + var template = Backbone.Radio.channel( 'app' ).request( 'get:template', '#tmpl-nf-cl-value-default' ); + var defaultHTML = template( { value: value } ); + + if ( key ) { + /* + * Send out a radio request for an html value on a channel based upon the field type. + * + * Get our field by key + * Get our field type model + * + * Send out a message on the type channel + * If we don't get a response, send a message out on the parent type channel + */ + var fieldModel = nfRadio.channel( 'fields' ).request( 'get:field', key ); + + if( 'undefined' != typeof fieldModel ) { + var typeModel = nfRadio.channel('fields').request('get:type', fieldModel.get('type')); + var html = nfRadio.channel('conditions-' + fieldModel.get('type')).request('get:valueInput', key, trigger, value); + if (!html) { + html = nfRadio.channel('conditions-' + typeModel.get('parentType')).request('get:valueInput', key, trigger, value) || defaultHTML; + } + } + } else { + var html = defaultHTML; + } + + return html; + } + }); + + return controller; +} ); diff --git a/assets/js/builder/controllers/trackKeyChanges.js b/assets/js/builder/controllers/trackKeyChanges.js new file mode 100644 index 0000000..5265d1e --- /dev/null +++ b/assets/js/builder/controllers/trackKeyChanges.js @@ -0,0 +1,32 @@ +/** + * Tracks key changes and updates when/then/else models + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( [], function() { + var controller = Marionette.Object.extend( { + initialize: function() { + this.listenTo( nfRadio.channel( 'conditions' ), 'init:whenModel', this.registerKeyChangeTracker ); + this.listenTo( nfRadio.channel( 'conditions' ), 'init:thenModel', this.registerKeyChangeTracker ); + this.listenTo( nfRadio.channel( 'conditions' ), 'init:elseModel', this.registerKeyChangeTracker ); + }, + + registerKeyChangeTracker: function( itemModel ) { + // Update selected field if the selected field's key changes. + itemModel.listenTo( nfRadio.channel( 'app' ), 'replace:fieldKey', this.updateKey, itemModel ); + }, + + updateKey: function( fieldModel, keyModel, settingModel ) { + var oldKey = keyModel._previousAttributes[ 'key' ]; + var newKey = keyModel.get( 'key' ); + + if( this.get( 'key' ) == oldKey ) { + this.set( 'key', newKey ); + } + } + }); + + return controller; +} ); \ No newline at end of file diff --git a/assets/js/builder/controllers/undo.js b/assets/js/builder/controllers/undo.js new file mode 100644 index 0000000..99230ff --- /dev/null +++ b/assets/js/builder/controllers/undo.js @@ -0,0 +1,134 @@ +/** + * Handles undoing everything for conditions. + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( [], function() { + var controller = Marionette.Object.extend( { + initialize: function() { + nfRadio.channel( 'changes' ).reply( 'undo:addCondition', this.undoAddCondition, this ); + nfRadio.channel( 'changes' ).reply( 'undo:removeCondition', this.undoRemoveCondition, this ); + nfRadio.channel( 'changes' ).reply( 'undo:addWhen', this.undoAddWhen, this ); + nfRadio.channel( 'changes' ).reply( 'undo:addThen', this.undoAddThen, this ); + nfRadio.channel( 'changes' ).reply( 'undo:addElse', this.undoAddElse, this ); + nfRadio.channel( 'changes' ).reply( 'undo:removeWhen', this.undoRemoveWhen, this ); + nfRadio.channel( 'changes' ).reply( 'undo:removeThen', this.undoRemoveThen, this ); + nfRadio.channel( 'changes' ).reply( 'undo:removeElse', this.undoRemoveElse, this ); + }, + + undoAddCondition: function( change, undoAll ) { + var dataModel = change.get( 'model' ); + var data = change.get( 'data' ); + + data.collection.remove( dataModel ); + + /* + * Loop through our change collection and remove any setting changes that belong to the condition we've added. + */ + var changeCollection = nfRadio.channel( 'changes' ).request( 'get:collection' ); + var results = changeCollection.where( function( changeModel ) { + if ( ( changeModel = dataModel ) || 'undefined' != typeof changeModel.get( 'data' ).conditionModel && changeModel.get( 'data' ).conditionModel == dataModel ) { + return true; + } else { + return false; + } + } ); + + _.each( results, function( model ) { + changeCollection.remove( model ); + } ); + + this.maybeRemoveChange( change, undoAll ); + }, + + undoRemoveCondition: function( change, undoAll ) { + var dataModel = change.get( 'model' ); + var data = change.get( 'data' ); + + data.collection.add( dataModel ); + + this.maybeRemoveChange( change, undoAll ); + }, + + undoAddWhen: function( change, undoAll ) { + var whenModel = change.get( 'model' ); + var data = change.get( 'data' ); + + data.conditionModel.get( 'when' ).remove( whenModel ); + + this.maybeRemoveChange( change, undoAll ); + }, + + undoAddThen: function( change, undoAll ) { + var thenModel = change.get( 'model' ); + var data = change.get( 'data' ); + + data.conditionModel.get( 'then' ).remove( thenModel ); + + this.maybeRemoveChange( change, undoAll ); + }, + + undoAddElse: function( change, undoAll ) { + var elseModel = change.get( 'model' ); + var data = change.get( 'data' ); + + data.conditionModel.get( 'else' ).remove( elseModel ); + + this.maybeRemoveChange( change, undoAll ); + }, + + undoRemoveWhen: function( change, undoAll ) { + var whenModel = change.get( 'model' ); + var data = change.get( 'data' ); + + data.collection.add( whenModel ); + + this.maybeRemoveChange( change, undoAll ); + }, + + undoRemoveThen: function( change, undoAll ) { + var thenModel = change.get( 'model' ); + var data = change.get( 'data' ); + + data.collection.add( thenModel ); + + this.maybeRemoveChange( change, undoAll ); + }, + + undoRemoveElse: function( change, undoAll ) { + var elseModel = change.get( 'model' ); + var data = change.get( 'data' ); + + data.collection.add( elseModel ); + + this.maybeRemoveChange( change, undoAll ); + }, + + /** + * If our undo action was requested to 'remove' the change from the collection, remove it. + * + * @since 3.0 + * @param backbone.model change model of our change + * @param boolean remove should we remove this item from our change collection + * @return void + */ + maybeRemoveChange: function( change, undoAll ) { + var undoAll = typeof undoAll !== 'undefined' ? undoAll : false; + if ( ! undoAll ) { + // Update preview. + nfRadio.channel( 'app' ).request( 'update:db' ); + var changeCollection = nfRadio.channel( 'changes' ).request( 'get:collection' ); + changeCollection.remove( change ); + if ( 0 == changeCollection.length ) { + nfRadio.channel( 'app' ).request( 'update:setting', 'clean', true ); + nfRadio.channel( 'app' ).request( 'close:drawer' ); + } + } + } + + }); + + return controller; +} ); diff --git a/assets/js/builder/controllers/updateSettings.js b/assets/js/builder/controllers/updateSettings.js new file mode 100644 index 0000000..fc2567e --- /dev/null +++ b/assets/js/builder/controllers/updateSettings.js @@ -0,0 +1,59 @@ +/** + * Updates condition settings on field change or drawer close + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( [], function() { + var controller = Marionette.Object.extend( { + initialize: function() { + this.listenTo( nfRadio.channel( 'conditions' ), 'change:setting', this.updateSetting ); + }, + + updateSetting: function( e, dataModel ) { + var value = jQuery( e.target ).val(); + var id = jQuery( e.target ).data( 'id' ); + var before = dataModel.get( id ); + + if ( jQuery( e.target ).find( ':selected' ).data( 'type' ) ) { + dataModel.set( 'type', jQuery( e.target ).find( ':selected' ).data( 'type' ) ); + } + + dataModel.set( id, value ); + + var after = value; + + var changes = { + attr: id, + before: before, + after: after + }; + + /* + * The "Advanced" domain uses a collection of conditions, while the "Actions" domain uses a single collection. + * Here, if we don't have a collection property, then dataModel must be our conditionModel. + */ + var conditionModel = ( 'undefined' == typeof dataModel.collection ) ? dataModel : dataModel.collection.options.conditionModel; + + var data = { + conditionModel: conditionModel + } + + var label = { + object: 'Condition', + label: 'Condition', + change: 'Changed ' + id + ' from ' + before + ' to ' + after + }; + + nfRadio.channel( 'changes' ).request( 'register:change', 'changeSetting', dataModel, changes, label, data ); + + // Set our 'clean' status to false so that we get a notice to publish changes + nfRadio.channel( 'app' ).request( 'update:setting', 'clean', false ); + nfRadio.channel( 'app' ).request( 'update:db' ); + } + + }); + + return controller; +} ); \ No newline at end of file diff --git a/assets/js/builder/main.js b/assets/js/builder/main.js new file mode 100644 index 0000000..4a869d9 --- /dev/null +++ b/assets/js/builder/main.js @@ -0,0 +1,30 @@ +var nfRadio = Backbone.Radio; + +require( [ 'controllers/loadControllers', 'models/conditionCollection' ], function( LoadControllers, ConditionCollection ) { + + var NFConditionalLogic = Marionette.Application.extend( { + + initialize: function( options ) { + this.listenTo( nfRadio.channel( 'app' ), 'after:appStart', this.afterNFLoad ); + }, + + onStart: function() { + new LoadControllers(); + }, + + afterNFLoad: function( app ) { + /* + * Convert our form's "condition" setting into a collection. + */ + var conditions = nfRadio.channel( 'settings' ).request( 'get:setting', 'conditions' ); + + if ( false === conditions instanceof Backbone.Collection ) { + conditions = new ConditionCollection( conditions ); + nfRadio.channel( 'settings' ).request( 'update:setting', 'conditions', conditions, true ); + } + } + } ); + + var nfConditionalLogic = new NFConditionalLogic(); + nfConditionalLogic.start(); +} ); \ No newline at end of file diff --git a/assets/js/builder/models/conditionCollection.js b/assets/js/builder/models/conditionCollection.js new file mode 100644 index 0000000..d5d5d02 --- /dev/null +++ b/assets/js/builder/models/conditionCollection.js @@ -0,0 +1,17 @@ +/** + * Conditon Collection + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( ['models/conditionModel'], function( ConditionModel ) { + var collection = Backbone.Collection.extend( { + model: ConditionModel, + + initialize: function( models, options ) { + this.options = options; + } + } ); + return collection; +} ); \ No newline at end of file diff --git a/assets/js/builder/models/conditionModel.js b/assets/js/builder/models/conditionModel.js new file mode 100644 index 0000000..b90cfa6 --- /dev/null +++ b/assets/js/builder/models/conditionModel.js @@ -0,0 +1,29 @@ +/** + * Conditon Model + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( [ 'models/whenCollection', 'models/thenCollection', 'models/elseCollection' ], function( WhenCollection, ThenCollection, ElseCollection ) { + var model = Backbone.Model.extend( { + defaults: { + collapsed: false, + process: 1, + connector: 'all', + when: [ {} ], + then: [ {} ], + else: [] + }, + + initialize: function() { + this.set( 'when', new WhenCollection( this.get( 'when' ), { conditionModel: this } ) ); + this.set( 'then', new ThenCollection( this.get( 'then' ), { conditionModel: this } ) ); + this.set( 'else', new ElseCollection( this.get( 'else' ), { conditionModel: this } ) ); + + nfRadio.channel( 'conditions' ).trigger( 'init:model', this ); + } + } ); + + return model; +} ); \ No newline at end of file diff --git a/assets/js/builder/models/elseCollection.js b/assets/js/builder/models/elseCollection.js new file mode 100644 index 0000000..ba6b323 --- /dev/null +++ b/assets/js/builder/models/elseCollection.js @@ -0,0 +1,17 @@ +/** + * Else Collection + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( ['models/elseModel'], function( ElseModel ) { + var collection = Backbone.Collection.extend( { + model: ElseModel, + + initialize: function( models, options ) { + this.options = options; + } + } ); + return collection; +} ); \ No newline at end of file diff --git a/assets/js/builder/models/elseModel.js b/assets/js/builder/models/elseModel.js new file mode 100644 index 0000000..aded814 --- /dev/null +++ b/assets/js/builder/models/elseModel.js @@ -0,0 +1,24 @@ +/** + * Else Model + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( [], function() { + var model = Backbone.Model.extend( { + defaults: { + key: '', + trigger: '', + value: '', + type: 'field', + modelType: 'else' + }, + + initialize: function() { + nfRadio.channel( 'conditions' ).trigger( 'init:elseModel', this ); + } + } ); + + return model; +} ); \ No newline at end of file diff --git a/assets/js/builder/models/thenCollection.js b/assets/js/builder/models/thenCollection.js new file mode 100644 index 0000000..2baa130 --- /dev/null +++ b/assets/js/builder/models/thenCollection.js @@ -0,0 +1,17 @@ +/** + * Then Collection + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( ['models/thenModel'], function( ThenModel ) { + var collection = Backbone.Collection.extend( { + model: ThenModel, + + initialize: function( models, options ) { + this.options = options; + } + } ); + return collection; +} ); \ No newline at end of file diff --git a/assets/js/builder/models/thenModel.js b/assets/js/builder/models/thenModel.js new file mode 100644 index 0000000..192b3ca --- /dev/null +++ b/assets/js/builder/models/thenModel.js @@ -0,0 +1,24 @@ +/** + * Then Model + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( [], function() { + var model = Backbone.Model.extend( { + defaults: { + key: '', + trigger: '', + value: '', + type: 'field', + modelType: 'then' + }, + + initialize: function() { + nfRadio.channel( 'conditions' ).trigger( 'init:thenModel', this ); + } + } ); + + return model; +} ); \ No newline at end of file diff --git a/assets/js/builder/models/whenCollection.js b/assets/js/builder/models/whenCollection.js new file mode 100644 index 0000000..dabb97c --- /dev/null +++ b/assets/js/builder/models/whenCollection.js @@ -0,0 +1,17 @@ +/** + * When Collection + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( ['models/whenModel'], function( WhenModel ) { + var collection = Backbone.Collection.extend( { + model: WhenModel, + + initialize: function( models, options ) { + this.options = options; + } + } ); + return collection; +} ); \ No newline at end of file diff --git a/assets/js/builder/models/whenModel.js b/assets/js/builder/models/whenModel.js new file mode 100644 index 0000000..c5c31f8 --- /dev/null +++ b/assets/js/builder/models/whenModel.js @@ -0,0 +1,25 @@ +/** + * When Model + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( [], function() { + var model = Backbone.Model.extend( { + defaults: { + connector: 'AND', + key: '', + comparator: '', + value: '', + type: 'field', + modelType: 'when' + }, + + initialize: function() { + nfRadio.channel( 'conditions' ).trigger( 'init:whenModel', this ); + } + } ); + + return model; +} ); \ No newline at end of file diff --git a/assets/js/builder/views/actions/conditionLayout.js b/assets/js/builder/views/actions/conditionLayout.js new file mode 100644 index 0000000..5eaf333 --- /dev/null +++ b/assets/js/builder/views/actions/conditionLayout.js @@ -0,0 +1,48 @@ +/** + * Layout view for our Action condition + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( [ 'views/actions/whenCollection' ], function( WhenCollection ) { + var view = Marionette.LayoutView.extend( { + template: '#tmpl-nf-cl-actions-condition-layout', + + regions: { + 'when': '.nf-when' + }, + + initialize: function( options ) { + this.model = options.dataModel.get( 'conditions' ); + if ( ! options.dataModel.get( 'conditions' ) ) return; + + this.collection = options.dataModel.get( 'conditions' ).get( 'when' ); + this.conditionModel = options.dataModel.get( 'conditions' ); + }, + + onRender: function() { + if ( ! this.collection ) return; + /* + * Show our "when" collection in the "when" area. + */ + this.when.show( new WhenCollection( { collection: this.collection } ) ); + }, + + events: { + 'change .condition-setting': 'changeSetting', + 'click .nf-add-when': 'clickAddWhen' + }, + + clickAddWhen: function( e ) { + nfRadio.channel( 'conditions' ).trigger( 'click:addWhen', e, this.model ); + }, + + changeSetting: function( e ) { + nfRadio.channel( 'conditions' ).trigger( 'change:setting', e, this.model ) + } + + }); + + return view; +} ); \ No newline at end of file diff --git a/assets/js/builder/views/actions/whenCollection.js b/assets/js/builder/views/actions/whenCollection.js new file mode 100644 index 0000000..c6fece9 --- /dev/null +++ b/assets/js/builder/views/actions/whenCollection.js @@ -0,0 +1,42 @@ +/** + * Collection view for our when collection + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( [ 'views/actions/whenItem' ], function( WhenItem ) { + var view = Marionette.CollectionView.extend({ + childView: WhenItem, + + initialize: function( options ) { + + }, + + onShow: function() { + /* + * If we don't have any conditions, add an empty one as we render. + */ + if ( 0 == this.collection.length ) { + this.collection.add( {} ); + } + }, + + onBeforeDestroy: function() { + /* + * If we don't have any conditions or we have more than one, just return. + */ + if ( 0 == this.collection.length || 1 < this.collection.length ) return; + /* + * If we only have one condition, and we didn't change the "key" attribute, reset our collection. + * This empties it. + */ + if ( '' == this.collection.models[0].get( 'key' ) ) { + this.collection.reset(); + } + } + + } ); + + return view; +} ); \ No newline at end of file diff --git a/assets/js/builder/views/actions/whenItem.js b/assets/js/builder/views/actions/whenItem.js new file mode 100644 index 0000000..4474c0f --- /dev/null +++ b/assets/js/builder/views/actions/whenItem.js @@ -0,0 +1,31 @@ +/** + * Item view for our condition and + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( [], function( ) { + var view = Marionette.ItemView.extend({ + template: "#tmpl-nf-cl-actions-condition-when", + + initialize: function() { + this.listenTo( this.model, 'change', this.render ); + }, + + events: { + 'change .setting': 'changeSetting', + 'click .nf-remove-when': 'clickRemove' + }, + + changeSetting: function( e ) { + nfRadio.channel( 'conditions' ).trigger( 'change:setting', e, this.model ) + }, + + clickRemove: function( e ) { + nfRadio.channel( 'conditions' ).trigger( 'click:removeWhen', e, this.model ); + } + }); + + return view; +} ); \ No newline at end of file diff --git a/assets/js/builder/views/advanced/conditionCollection.js b/assets/js/builder/views/advanced/conditionCollection.js new file mode 100644 index 0000000..39174e4 --- /dev/null +++ b/assets/js/builder/views/advanced/conditionCollection.js @@ -0,0 +1,41 @@ +/** + * Collection view for our conditions + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( [ 'views/advanced/conditionItem' ], function( conditionItem ) { + var view = Marionette.CollectionView.extend({ + childView: conditionItem, + + initialize: function( options ) { + this.collection = options.dataModel.get( 'conditions' ); + }, + + onShow: function() { + /* + * If we don't have any conditions, add an empty one as we render. + */ + if ( 0 == this.collection.length ) { + this.collection.add( {} ); + } + }, + + onBeforeDestroy: function() { + /* + * If we don't have any conditions or we have more than one, just return. + */ + if ( 0 == this.collection.length || 1 < this.collection.length ) return; + /* + * If we only have one condition, and we didn't change the "key" attribute, reset our collection. + * This empties it. + */ + if ( '' == this.collection.models[0].get( 'when' ).models[0].get( 'key' ) ) { + this.collection.reset(); + } + } + }); + + return view; +} ); diff --git a/assets/js/builder/views/advanced/conditionItem.js b/assets/js/builder/views/advanced/conditionItem.js new file mode 100644 index 0000000..c60f5fa --- /dev/null +++ b/assets/js/builder/views/advanced/conditionItem.js @@ -0,0 +1,68 @@ +/** + * Layout view for our conditions + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( [ 'views/advanced/whenCollection', 'views/advanced/thenCollection', 'views/advanced/elseCollection' ], function( WhenCollectionView, ThenCollectionView, ElseCollectionView ) { + var view = Marionette.LayoutView.extend({ + template: "#tmpl-nf-cl-advanced-condition", + regions: { + 'when': '.nf-when-region', + 'then': '.nf-then-region', + 'else': '.nf-else-region' + }, + + initialize: function() { + /* + * When we change the "collapsed" attribute of our model, re-render. + */ + this.listenTo( this.model, 'change:collapsed', this.render ); + + /* + * When our drawer closes, send out a radio message on our setting type channel. + */ + this.listenTo( nfRadio.channel( 'drawer' ), 'closed', this.drawerClosed ); + }, + + onRender: function() { + var firstWhenDiv = jQuery( this.el ).find( '.nf-first-when' ); + this.when.show( new WhenCollectionView( { collection: this.model.get( 'when' ), firstWhenDiv: firstWhenDiv, conditionModel: this.model } ) ); + if ( ! this.model.get( 'collapsed' ) ) { + this.then.show( new ThenCollectionView( { collection: this.model.get( 'then' ) } ) ); + this.else.show( new ElseCollectionView( { collection: this.model.get( 'else' ) } ) ); + } + }, + + events: { + 'click .nf-remove-condition': 'clickRemove', + 'click .nf-collapse-condition': 'clickCollapse', + 'click .nf-add-when': 'clickAddWhen', + 'click .nf-add-then': 'clickAddThen', + 'click .nf-add-else': 'clickAddElse' + }, + + clickRemove: function( e ) { + nfRadio.channel( 'conditions' ).trigger( 'click:removeCondition', e, this.model ); + }, + + clickCollapse: function( e ) { + nfRadio.channel( 'conditions' ).trigger( 'click:collapseCondition', e, this.model ); + }, + + clickAddWhen: function( e ) { + nfRadio.channel( 'conditions' ).trigger( 'click:addWhen', e, this.model ); + }, + + clickAddThen: function( e ) { + nfRadio.channel( 'conditions' ).trigger( 'click:addThen', e, this.model ); + }, + + clickAddElse: function( e ) { + nfRadio.channel( 'conditions' ).trigger( 'click:addElse', e, this.model ); + } + }); + + return view; +} ); \ No newline at end of file diff --git a/assets/js/builder/views/advanced/elseCollection.js b/assets/js/builder/views/advanced/elseCollection.js new file mode 100644 index 0000000..c8bdf13 --- /dev/null +++ b/assets/js/builder/views/advanced/elseCollection.js @@ -0,0 +1,18 @@ +/** + * Collection view for our else statements + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( [ 'views/advanced/elseItem' ], function( ElseItem ) { + var view = Marionette.CollectionView.extend({ + childView: ElseItem, + + initialize: function( options ) { + + } + }); + + return view; +} ); \ No newline at end of file diff --git a/assets/js/builder/views/advanced/elseItem.js b/assets/js/builder/views/advanced/elseItem.js new file mode 100644 index 0000000..d0a7087 --- /dev/null +++ b/assets/js/builder/views/advanced/elseItem.js @@ -0,0 +1,31 @@ +/** + * Item view for our condition then + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( [], function( ) { + var view = Marionette.ItemView.extend({ + template: "#tmpl-nf-cl-trigger-item", + + initialize: function() { + this.listenTo( this.model, 'change', this.render ); + }, + + events: { + 'change .setting': 'changeSetting', + 'click .nf-remove-else': 'clickRemove' + }, + + changeSetting: function( e ) { + nfRadio.channel( 'conditions' ).trigger( 'change:setting', e, this.model ) + }, + + clickRemove: function( e ) { + nfRadio.channel( 'conditions' ).trigger( 'click:removeElse', e, this.model ); + } + }); + + return view; +} ); \ No newline at end of file diff --git a/assets/js/builder/views/advanced/firstWhenItem.js b/assets/js/builder/views/advanced/firstWhenItem.js new file mode 100644 index 0000000..f3f0276 --- /dev/null +++ b/assets/js/builder/views/advanced/firstWhenItem.js @@ -0,0 +1,36 @@ +/** + * Item view for our condition's first when + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( [], function( ) { + var view = Marionette.ItemView.extend({ + template: "#tmpl-nf-cl-advanced-first-when-item", + + initialize: function() { + this.listenTo( this.model, 'change', this.render ); + }, + + onRender: function() { + let el = jQuery( this.el ).find( '[data-type="date"]' ); + jQuery( el ).mask( '9999-99-99' ); + }, + + events: { + 'change .setting': 'changeSetting', + 'change .extra': 'changeExtra', + }, + + changeSetting: function( e ) { + nfRadio.channel( 'conditions' ).trigger( 'change:setting', e, this.model ); + }, + + changeExtra: function( e ) { + nfRadio.channel( 'conditions' ).trigger( 'change:extra', e, this.model ); + } + }); + + return view; +} ); \ No newline at end of file diff --git a/assets/js/builder/views/advanced/thenCollection.js b/assets/js/builder/views/advanced/thenCollection.js new file mode 100644 index 0000000..9b7b423 --- /dev/null +++ b/assets/js/builder/views/advanced/thenCollection.js @@ -0,0 +1,18 @@ +/** + * Collection view for our then statements + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( [ 'views/advanced/thenItem' ], function( ThenItem ) { + var view = Marionette.CollectionView.extend({ + childView: ThenItem, + + initialize: function( options ) { + + } + }); + + return view; +} ); \ No newline at end of file diff --git a/assets/js/builder/views/advanced/thenItem.js b/assets/js/builder/views/advanced/thenItem.js new file mode 100644 index 0000000..8c2db14 --- /dev/null +++ b/assets/js/builder/views/advanced/thenItem.js @@ -0,0 +1,32 @@ +/** + * Item view for our condition then + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( [], function( ) { + var view = Marionette.ItemView.extend({ + template: "#tmpl-nf-cl-trigger-item", + + initialize: function() { + this.listenTo( this.model, 'change', this.render ); + }, + + events: { + 'change .setting': 'changeSetting', + 'click .nf-remove-then': 'clickRemove' + }, + + changeSetting: function( e ) { + nfRadio.channel( 'conditions' ).trigger( 'change:setting', e, this.model ); + nfRadio.channel( 'conditions' ).trigger( 'change:then', e, this.model ); + }, + + clickRemove: function( e ) { + nfRadio.channel( 'conditions' ).trigger( 'click:removeThen', e, this.model ); + } + }); + + return view; +} ); \ No newline at end of file diff --git a/assets/js/builder/views/advanced/whenCollection.js b/assets/js/builder/views/advanced/whenCollection.js new file mode 100644 index 0000000..386358b --- /dev/null +++ b/assets/js/builder/views/advanced/whenCollection.js @@ -0,0 +1,50 @@ +/** + * Collection view for our when collection + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( [ 'views/advanced/whenItem', 'views/advanced/firstWhenItem' ], function( WhenItem, FirstWhenItem ) { + var view = Marionette.CollectionView.extend({ + getChildView: function( item ) { + if ( item.collection.first() == item ) { + return FirstWhenItem; + } else { + return WhenItem; + } + + }, + + initialize: function( options ) { + this.firstWhenDiv = options.firstWhenDiv; + this.conditionModel = options.conditionModel; + }, + + // The default implementation: + attachHtml: function( collectionView, childView, index ) { + if ( 0 == index ) { + this.firstWhenDiv.append( childView.el ); + } else { + if ( ! this.conditionModel.get( 'collapsed' ) ) { + if (collectionView.isBuffering) { + // buffering happens on reset events and initial renders + // in order to reduce the number of inserts into the + // document, which are expensive. + collectionView._bufferedChildren.splice(index, 0, childView); + } else { + // If we've already rendered the main collection, append + // the new child into the correct order if we need to. Otherwise + // append to the end. + if (!collectionView._insertBefore(childView, index)){ + collectionView._insertAfter(childView); + } + } + } + } + }, + + } ); + + return view; +} ); \ No newline at end of file diff --git a/assets/js/builder/views/advanced/whenItem.js b/assets/js/builder/views/advanced/whenItem.js new file mode 100644 index 0000000..2832304 --- /dev/null +++ b/assets/js/builder/views/advanced/whenItem.js @@ -0,0 +1,36 @@ +/** + * Item view for our condition and + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( [], function( ) { + var view = Marionette.ItemView.extend({ + template: "#tmpl-nf-cl-advanced-when-item", + + initialize: function() { + this.listenTo( this.model, 'change', this.render ); + }, + + onRender: function() { + let el = jQuery( this.el ).find( '[data-type="date"]' ); + jQuery( el ).mask( '9999-99-99' ); + }, + + events: { + 'change .setting': 'changeSetting', + 'click .nf-remove-when': 'clickRemove' + }, + + changeSetting: function( e ) { + nfRadio.channel( 'conditions' ).trigger( 'change:setting', e, this.model ) + }, + + clickRemove: function( e ) { + nfRadio.channel( 'conditions' ).trigger( 'click:removeWhen', e, this.model ); + } + }); + + return view; +} ); \ No newline at end of file diff --git a/assets/js/builder/views/drawerHeader.js b/assets/js/builder/views/drawerHeader.js new file mode 100644 index 0000000..4836932 --- /dev/null +++ b/assets/js/builder/views/drawerHeader.js @@ -0,0 +1,22 @@ +/** + * Item view for our drawer header + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( [], function( ) { + var view = Marionette.ItemView.extend({ + template: "#tmpl-nf-cl-advanced-drawer-header", + + events: { + 'click .nf-add-new': 'clickAddNew' + }, + + clickAddNew: function( e ) { + nfRadio.channel( 'conditions' ).trigger( 'click:addNew', e ); + } + }); + + return view; +} ); \ No newline at end of file diff --git a/assets/js/front-end/controllers/actions.js b/assets/js/front-end/controllers/actions.js new file mode 100644 index 0000000..8b9105f --- /dev/null +++ b/assets/js/front-end/controllers/actions.js @@ -0,0 +1,41 @@ +/** + * Keep an internal record for which actions are active and deactive. + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( [], function() { + var controller = Marionette.Object.extend( { + actions: {}, + + initialize: function() { + /* + * Listen for activate/deactivate action messages. + */ + nfRadio.channel( 'condition:trigger' ).reply( 'activate_action', this.activateAction, this ); + nfRadio.channel( 'condition:trigger' ).reply( 'deactivate_action', this.deactivateAction, this ); + + /* + * Reply to requests for action status. + */ + nfRadio.channel( 'actions' ).reply( 'get:status', this.getStatus, this ); + }, + + activateAction: function( conditionModel, thenObject ) { + this.actions[ thenObject.key ] = true; + console.log( 'activate action' ); + }, + + deactivateAction: function( conditionModel, thenObject ) { + console.log( 'deactivate action' ); + this.actions[ thenObject.key ] = false; + }, + + getStatus: function( $id ) { + return this.actions[ $id ]; + } + }); + + return controller; +} ); \ No newline at end of file diff --git a/assets/js/front-end/controllers/changeRequired.js b/assets/js/front-end/controllers/changeRequired.js new file mode 100644 index 0000000..b15d170 --- /dev/null +++ b/assets/js/front-end/controllers/changeRequired.js @@ -0,0 +1,36 @@ +/** + * Setting/unsetting required. + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2019 WP Ninjas + * @since 3.0 + */ +define( [], function() { + var controller = Marionette.Object.extend( { + initialize: function() { + nfRadio.channel( 'condition:trigger' ).reply( 'set_required', this.setRequired, this ); + nfRadio.channel( 'condition:trigger' ).reply( 'unset_required', this.unsetRequired, this ); + }, + + setRequired: function( conditionModel, then ) { + var targetFieldModel = nfRadio.channel( 'form-' + conditionModel.collection.formModel.get( 'id' ) ).request( 'get:fieldByKey', then.key ); + + if( 'undefined' == typeof targetFieldModel ) return; + targetFieldModel.set( 'required', 1 ); + targetFieldModel.trigger( 'reRender', targetFieldModel ); + }, + + unsetRequired: function( conditionModel, then ) { + var targetFieldModel = nfRadio.channel( 'form-' + conditionModel.collection.formModel.get( 'id' ) ).request( 'get:fieldByKey', then.key ); + + if( 'undefined' == typeof targetFieldModel ) return; + targetFieldModel.set( 'required', 0 ); + targetFieldModel.trigger( 'reRender', targetFieldModel ); + // Ensure we resolve any errors when the field is no longer required. + nfRadio.channel( 'fields' ).request( 'remove:error', targetFieldModel.get( 'id' ), 'required-error' ); + } + + }); + + return controller; +} ); \ No newline at end of file diff --git a/assets/js/front-end/controllers/changeValue.js b/assets/js/front-end/controllers/changeValue.js new file mode 100644 index 0000000..945927e --- /dev/null +++ b/assets/js/front-end/controllers/changeValue.js @@ -0,0 +1,38 @@ +/** + * Handle changing a field's value + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( [], function() { + var controller = Marionette.Object.extend( { + initialize: function() { + nfRadio.channel( 'condition:trigger' ).reply( 'change_value', this.changeValue, this ); + }, + + changeValue: function( conditionModel, then ) { + var targetFieldModel = nfRadio.channel( 'form-' + conditionModel.collection.formModel.get( 'id' ) ).request( 'get:fieldByKey', then.key ); + /* + * If we have a checkbox then we need to change the value that is set + * of the then variable to a 1 or 0 to re-render on the front end when + * the condition is met. + */ + if( 'checkbox' == targetFieldModel.get( 'type' ) ) { + // We also need to do the opposite of the value that is in the changed model. + if( 'unchecked' == targetFieldModel.changed.value ) { + then.value = 1; + } else if( 'checked' == targetFieldModel ) { + then.value = 0; + } + } + /* + * Change the value of our field model, and then trigger a re-render of its view. + */ + targetFieldModel.set( 'value', then.value ); + targetFieldModel.trigger( 'reRender', targetFieldModel ); + }, + + }); + return controller; +} ); \ No newline at end of file diff --git a/assets/js/front-end/controllers/initCollection.js b/assets/js/front-end/controllers/initCollection.js new file mode 100644 index 0000000..b45ddce --- /dev/null +++ b/assets/js/front-end/controllers/initCollection.js @@ -0,0 +1,21 @@ +/** + * Initialise condition collection + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( [ 'models/conditionCollection' ], function( ConditionCollection ) { + var controller = Marionette.Object.extend( { + initialize: function( formModel ) { + this.collection = new ConditionCollection( formModel.get( 'conditions' ), { formModel: formModel } ); + this.listenTo(nfRadio.channel('fields'), 'reset:collection', this.resetCollection); + }, + resetCollection: function( fieldsCollection ) { + var formModel = fieldsCollection.options.formModel; + this.collection = new ConditionCollection( formModel.get( 'conditions' ), { formModel: formModel } ); + } + }); + + return controller; +} ); \ No newline at end of file diff --git a/assets/js/front-end/controllers/selectDeselect.js b/assets/js/front-end/controllers/selectDeselect.js new file mode 100644 index 0000000..e0129fc --- /dev/null +++ b/assets/js/front-end/controllers/selectDeselect.js @@ -0,0 +1,91 @@ +/** + * Handle selecting/deselecting list options + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( [], function() { + var controller = Marionette.Object.extend( { + initialize: function() { + nfRadio.channel( 'condition:trigger' ).reply( 'select_option', this.selectOption, this ); + + nfRadio.channel( 'condition:trigger' ).reply( 'deselect_option', this.deselectOption, this ); + }, + + selectOption: function( conditionModel, then ) { + /* + * Get our field model and set this option's "selected" property to 1 + */ + var targetFieldModel = nfRadio.channel( 'form-' + conditionModel.collection.formModel.get( 'id' ) ).request( 'get:fieldByKey', then.key ); + + + if( _.contains( [ 'listselect', 'listcountry', 'liststate' ], targetFieldModel.get( 'type' ) ) ) { // TODO: Make this more dynamic. + targetFieldModel.set('clean', false); // Allows for changes to default values. + } + + var options = targetFieldModel.get( 'options' ); + + var option = _.find( options, { value: then.value } ); + option.selected = 1; + + targetFieldModel.set( 'options', options ); + + if( _.contains( [ 'listselect', 'listcountry', 'liststate' ], targetFieldModel.get( 'type' ) ) ) { // TODO: Make this more dynamic. + targetFieldModel.set( 'value', option.value ); // Propagate the selected option to the model's value. + } else { + var value = targetFieldModel.get( 'value' ); + if ( _.isArray( value ) ) { + if ( 0 > value.indexOf( option.value ) ) { + value.push( option.value ); + // Set the value to nothing so it knows that something has changed. + targetFieldModel.set( 'value', '' ); + } + } else { + value = option.value; + } + + targetFieldModel.set( 'value', value ); // Propagate the selected option to the model's value. + } + + /* + * Re render our field + */ + targetFieldModel.trigger( 'reRender', targetFieldModel ); + }, + + deselectOption: function( conditionModel, then ) { + /* + * When we are trying to deselect our option, we need to change it's "selected" property to 0 AND change its value. + */ + var targetFieldModel = nfRadio.channel( 'form-' + conditionModel.collection.formModel.get( 'id' ) ).request( 'get:fieldByKey', then.key ); + + /* + * Set "selected" to 0. + */ + var options = targetFieldModel.get( 'options' ); + var option = _.find( options, { value: then.value } ); + option.selected = 0; + targetFieldModel.set( 'options', options ); + + /* + * Update our value + */ + var currentValue = targetFieldModel.get( 'value' ); + if ( _.isArray( currentValue ) ) { + currentValue = _.without( currentValue, then.value ); + } else { + currentValue = ''; + } + targetFieldModel.set( 'value', currentValue ); + + /* + * Re render our field + */ + targetFieldModel.trigger( 'reRender', targetFieldModel ); + }, + + }); + + return controller; +} ); \ No newline at end of file diff --git a/assets/js/front-end/controllers/showHide.js b/assets/js/front-end/controllers/showHide.js new file mode 100644 index 0000000..a14597d --- /dev/null +++ b/assets/js/front-end/controllers/showHide.js @@ -0,0 +1,63 @@ +/** + * Handle showing/hiding fields + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( [], function() { + var controller = Marionette.Object.extend( { + initialize: function() { + nfRadio.channel( 'condition:trigger' ).reply( 'hide_field', this.hideField, this ); + nfRadio.channel( 'condition:trigger' ).reply( 'show_field', this.showField, this ); + }, + + hideField: function( conditionModel, then ) { + var targetFieldModel = nfRadio.channel( 'form-' + conditionModel.collection.formModel.get( 'id' ) ).request( 'get:fieldByKey', then.key ); + + if( 'undefined' == typeof targetFieldModel ) return; + targetFieldModel.set( 'visible', false ); + if ( ! targetFieldModel.get( 'clean' ) ) { + targetFieldModel.trigger( 'change:value', targetFieldModel ); + } + + nfRadio.channel( 'fields' ).request( 'remove:error', targetFieldModel.get( 'id' ), 'required-error' ); + }, + + showField: function( conditionModel, then ) { + var targetFieldModel = nfRadio.channel( 'form-' + conditionModel.collection.formModel.get( 'id' ) ).request( 'get:fieldByKey', then.key ); + //TODO: Add an error to let the user know the show/hide field is empty. + if( 'undefined' == typeof targetFieldModel ) return; + targetFieldModel.set( 'visible', true ); + if ( ! targetFieldModel.get( 'clean' ) ) { + targetFieldModel.trigger( 'change:value', targetFieldModel ); + } + if ( 'recaptcha' === targetFieldModel.get( 'type' ) ) { + this.renderRecaptcha(); + } + var viewEl = { el: nfRadio.channel( 'form-' + conditionModel.collection.formModel.get( 'id' ) ).request( 'get:el' ) }; + nfRadio.channel( 'form' ).request( 'init:help', viewEl ); + }, + + renderRecaptcha: function() { + jQuery( '.g-recaptcha' ).each( function() { + var callback = jQuery( this ).data( 'callback' ); + var fieldID = jQuery( this ).data( 'fieldid' ); + if ( typeof window[ callback ] !== 'function' ){ + window[ callback ] = function( response ) { + nfRadio.channel( 'recaptcha' ).request( 'update:response', response, fieldID ); + }; + } + var opts = { + theme: jQuery( this ).data( 'theme' ), + sitekey: jQuery( this ).data( 'sitekey' ), + callback: callback + }; + + grecaptcha.render( jQuery( this )[0], opts ); + } ); + } + }); + + return controller; +} ); \ No newline at end of file diff --git a/assets/js/front-end/controllers/showHideOption.js b/assets/js/front-end/controllers/showHideOption.js new file mode 100644 index 0000000..24bffab --- /dev/null +++ b/assets/js/front-end/controllers/showHideOption.js @@ -0,0 +1,47 @@ +/** + * Handle adding or removing an option from our list + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( [], function() { + var controller = Marionette.Object.extend( { + initialize: function() { + nfRadio.channel( 'condition:trigger' ).reply( 'show_option', this.showOption, this ); + + nfRadio.channel( 'condition:trigger' ).reply( 'hide_option', this.hideOption, this ); + }, + + showOption: function( conditionModel, then ) { + var option = this.getOption( conditionModel, then ); + option.visible = true; + this.updateFieldModel( conditionModel, then ); + }, + + hideOption: function( conditionModel, then ) { + var option = this.getOption( conditionModel, then ); + option.visible = false; + this.updateFieldModel( conditionModel, then ); + }, + + getFieldModel: function( conditionModel, then ) { + return nfRadio.channel( 'form-' + conditionModel.collection.formModel.get( 'id' ) ).request( 'get:fieldByKey', then.key ); + }, + + getOption: function( conditionModel, then ) { + var targetFieldModel = this.getFieldModel( conditionModel, then ); + var options = targetFieldModel.get( 'options' ); + return _.find( options, function( option ) { return option.value == then.value } ); + }, + + updateFieldModel: function( conditionModel, then ) { + var targetFieldModel = this.getFieldModel( conditionModel, then ); + var options = targetFieldModel.get( 'options' ); + targetFieldModel.set( 'options', options ); + targetFieldModel.trigger( 'reRender' ); + } + }); + + return controller; +} ); \ No newline at end of file diff --git a/assets/js/front-end/main.js b/assets/js/front-end/main.js new file mode 100644 index 0000000..c95ee38 --- /dev/null +++ b/assets/js/front-end/main.js @@ -0,0 +1,28 @@ +var nfRadio = Backbone.Radio; + +require( [ 'controllers/initCollection', 'controllers/showHide', 'controllers/changeRequired', 'controllers/showHideOption', 'controllers/changeValue', 'controllers/selectDeselect', 'controllers/actions' ], function( InitCollection, ShowHide, ChangeRequired, ShowHideOption, ChangeValue, SelectDeselect, Actions ) { + + var NFConditionalLogic = Marionette.Application.extend( { + + initialize: function( options ) { + this.listenTo( nfRadio.channel( 'form' ), 'after:loaded', this.loadControllers ); + }, + + loadControllers: function( formModel ) { + new ShowHide(); + new ChangeRequired(); + new ShowHideOption(); + new ChangeValue(); + new SelectDeselect(); + new Actions(); + new InitCollection( formModel ); + }, + + onStart: function() { + + } + } ); + + var nfConditionalLogic = new NFConditionalLogic(); + nfConditionalLogic.start(); +} ); \ No newline at end of file diff --git a/assets/js/front-end/models/conditionCollection.js b/assets/js/front-end/models/conditionCollection.js new file mode 100644 index 0000000..ce1c77d --- /dev/null +++ b/assets/js/front-end/models/conditionCollection.js @@ -0,0 +1,10 @@ +define( ['models/conditionModel'], function( ConditionModel ) { + var collection = Backbone.Collection.extend( { + model: ConditionModel, + + initialize: function( models, options ) { + this.formModel = options.formModel; + } + } ); + return collection; +} ); \ No newline at end of file diff --git a/assets/js/front-end/models/conditionModel.js b/assets/js/front-end/models/conditionModel.js new file mode 100644 index 0000000..c5a37c2 --- /dev/null +++ b/assets/js/front-end/models/conditionModel.js @@ -0,0 +1,56 @@ +define( [ 'models/whenCollection' ], function( WhenCollection ) { + var model = Backbone.Model.extend( { + initialize: function( options ) { + /* + * Our "when" statement will be like: + * When field1 == value + * AND field2 == value + * + * We need to create a collection out of this when statement, with each row as a model. + */ + this.set( 'when', new WhenCollection( this.get( 'when' ), { condition: this } ) ); + /* + * When we update any of our "when" models' status, check to see if we should send a message. + */ + this.get( 'when' ).on( 'change:status', this.checkWhen, this ); + /* + * Check our initial status; + */ + this.checkWhen(); + }, + + checkWhen: function() { + /* + * If we have any OR connectors, then any status being true should trigger pass. + * Otherwise, we need every status to be true. + */ + var statusResults = this.get( 'when' ).pluck( 'status' ); + + var connectors = this.get( 'when' ).pluck( 'connector' ); + var allAND = _.every( _.values( connectors ), function( v ) { return v == 'AND' }, this ); + if ( allAND ) { + var status = _.every( _.values( statusResults ), function(v) { return v; }, this ); + } else { + var status = _.some( _.values( statusResults ), function(v) { return v; }, this ); + } + + if ( status ) { + /* + * Send out a request for each of our "then" statements. + */ + _.each( this.get( 'then' ), function( then, index ) { + nfRadio.channel( 'condition:trigger' ).request( then.trigger, this, then ); + }, this ); + } else { + /* + * Send out a request for each of our "else" statements. + */ + _.each( this.get( 'else' ), function( elseData, index ) { + nfRadio.channel( 'condition:trigger' ).request( elseData.trigger, this, elseData ); + }, this ); + } + } + } ); + + return model; +} ); \ No newline at end of file diff --git a/assets/js/front-end/models/whenCollection.js b/assets/js/front-end/models/whenCollection.js new file mode 100644 index 0000000..933d2d2 --- /dev/null +++ b/assets/js/front-end/models/whenCollection.js @@ -0,0 +1,10 @@ +define( ['models/whenModel'], function( WhenModel ) { + var collection = Backbone.Collection.extend( { + model: WhenModel, + + initialize: function( models, options ) { + this.options = options; + } + } ); + return collection; +} ); \ No newline at end of file diff --git a/assets/js/front-end/models/whenModel.js b/assets/js/front-end/models/whenModel.js new file mode 100644 index 0000000..13fedcf --- /dev/null +++ b/assets/js/front-end/models/whenModel.js @@ -0,0 +1,252 @@ +define( [], function() { + var model = Backbone.Model.extend( { + initialize: function( models, options ) { + /* + * If our key or comparator is empty, don't do anything else. + */ + if ( ! this.get( 'key' ) || ! this.get( 'comparator' ) ) return; + + /* + * Our key could be a field or a calc. + * We need to setup a listener on either the field or calc model for changes. + */ + if ( 'calc' == this.get( 'type' ) ) { // We have a calculation key + /* + * Get our calc model + */ + var calcModel = nfRadio.channel( 'form-' + this.collection.options.condition.collection.formModel.get( 'id' ) ).request( 'get:calc', this.get( 'key' ) ); + /* + * When we update our calculation, update our compare + */ + this.listenTo( calcModel, 'change:value', this.updateCalcCompare ); + /* + * Update our compare status. + */ + this.updateCalcCompare( calcModel ); + } else { // We have a field key + // Get our field model + var fieldModel = nfRadio.channel( 'form-' + options.condition.collection.formModel.get( 'id' ) ).request( 'get:fieldByKey', this.get( 'key' ) ); + + if( 'undefined' == typeof fieldModel ) return; + + // When we change the value of our field, update our compare status. + fieldModel.on( 'change:value', this.updateFieldCompare, this ); + // When we keyup in our field, maybe update our compare status. + this.listenTo( nfRadio.channel( 'field-' + fieldModel.get( 'id' ) ), 'keyup:field', this.maybeupdateFieldCompare ); + // Update our compare status. + this.updateFieldCompare( fieldModel ); + + /* + * TODO: This should be moved to the show_field/hide_field file because it is specific to showing and hiding. + * Create a radio message here so that the specific JS file can hook into whenModel init. + */ + fieldModel.on( 'change:visible', this.updateFieldCompare, this ); + } + }, + + updateCalcCompare: function( calcModel ) { + this.updateCompare( calcModel.get( 'value' ) ); + }, + + maybeupdateFieldCompare: function( el, fieldModel, keyCode ) { + if( 'checkbox' == fieldModel.get( 'type' ) ){ + var fieldValue = ( 'checked' == jQuery( el ).attr( 'checked' ) ) ? 1 : 0; + } else if( 'listcheckbox' == fieldModel.get( 'type' ) ) { + // This field isn't a single element, so we need to reference the fieldModel, instead of the DOM. + var fieldValue = fieldModel.get( 'value' ).join(); + } else if ( 'date' == fieldModel.get ('type' ) ) { + var fieldValue = fieldModel.get( 'value' ); + + if ( _.isEmpty( fieldValue ) ) { + fieldValue = '1970/01/01'; + } + + let date_mode = fieldModel.get( 'date_mode' ); + if ( 'undefined' == typeof date_mode ) { // If 'date_mode' is undefined, then we assume it's date_only. + date_mode = 'date_only'; + } + let date = 0; + // If we're in time_only mode, then we need to use 1970-01-01 as our date. + if ( 'time_only' == fieldModel.get( 'date_mode' ) ) { + date = '1970/01/01'; + } else { + date = fieldValue; + } + + // Convert field value into a timestamp + let hour = fieldModel.get( 'selected_hour' ); + if ( 'undefined' == typeof hour ) { + hour = '00'; + } + + let minute = fieldModel.get( 'selected_minute' ); + if ( 'undefined' == typeof minute ) { + minute = '00'; + } + + // If we have a date_and_time field, but we haven't selected a date yet, we don't need to compare. + if ( 'date_and_time' == date_mode && '1970/01/01' == date ) { + fieldValue = false; + } else { + fieldValue = date + ' ' + hour + ':' + minute + ' UT'; + + let dateObject = new Date( fieldValue ); + fieldValue = Math.floor( dateObject.getTime() / 1000 ); + } + } else { + var fieldValue = jQuery( el ).val(); + } + + this.updateFieldCompare( fieldModel, null, fieldValue ); + }, + + updateCompare: function( value ) { + var this_val = this.get( 'value' ); + + // if this is a calcModel then let's convert to number for comparison + if ( 'calc' === this.get( 'type' ) ) { + this_val = Number( this_val ); + value = Number( value ); + } + // Check to see if the value of the field model value COMPARATOR the value of our when condition is true. + var status = this.compareValues[ this.get( 'comparator' ) ]( value, this_val ); + this.set( 'status', status ); + }, + + updateFieldCompare: function( fieldModel, val, fieldValue ) { + if ( _.isEmpty( fieldValue ) ) { + fieldValue = fieldModel.get( 'value' ); + } + + // Change the value of checkboxes to match the new convention. + if( 'checkbox' == fieldModel.get( 'type' ) ) { + if( 0 == fieldValue ) { + fieldValue = 'unchecked'; + } else { + fieldValue = 'checked'; + } + } else if ( 'date' == fieldModel.get( 'type' ) ) { + if ( _.isEmpty( fieldValue ) ) { + fieldValue = '1970/01/01'; + } + + let date_mode = fieldModel.get( 'date_mode' ); + if ( 'undefined' == typeof date_mode ) { // If 'date_mode' is undefined, then we assume it's date_only. + date_mode = 'date_only'; + } + let date = 0; + // If we're in time_only mode, then we need to use 1970-01-01 as our date. + if ( 'time_only' == fieldModel.get( 'date_mode' ) ) { + date = '1970/01/01'; + } else { + date = fieldValue; + } + + // Convert field value into a timestamp + let hour = fieldModel.get( 'selected_hour' ); + if ( 'undefined' == typeof hour ) { + hour = '00'; + } + + let ampm = fieldModel.get( 'selected_ampm' ); + if ( 'undefined' != typeof ampm ) { + // Convert our hour into 24 hr format. + if ( 'pm' == ampm && '12' != hour ) { + hour = parseInt( hour ) + 12; + } else if ( 'am' == ampm && '12' == hour ) { + hour = '00'; + } + } + + let minute = fieldModel.get( 'selected_minute' ); + if ( 'undefined' == typeof minute ) { + minute = '00'; + } + + // If we have a date_and_time field, but we haven't selected a date yet, we don't need to compare. + if ( 'date_and_time' == date_mode && '1970/01/01' == date ) { + fieldValue = false; + } else { + fieldValue = date + ' ' + hour + ':' + minute + ' UT'; + + let dateObject = new Date( fieldValue ); + fieldValue = Math.floor( dateObject.getTime() / 1000 ); + } + } + + this.updateCompare( fieldValue ); + + /* + * TODO: This should be moved to the show_field/hide_field file because it is specific to showing and hiding. + */ + if ( ! fieldModel.get( 'visible' ) ) { + this.set( 'status', false ); + } + }, + + compareValues: { + 'equal': function( a, b ) { + return a == b; + }, + 'notequal': function( a, b ) { + return a != b; + }, + 'contains': function( a, b ) { + if ( jQuery.isArray( a ) ) { + /* + * If a is an array, then we're searching for an index. + */ + return a.indexOf( b ) >= 0; + } else { + /* + * If a is a string, then we're searching for a string position. + * + * If our b value has quotes in it, we want to find that exact word or phrase. + */ + if ( b.indexOf( '"' ) >= 0 ) { + b = b.replace( /['"]+/g, '' ); + return new RegExp("\\b" + b + "\\b").test( a ); + } + return a.toLowerCase().indexOf( b.toLowerCase() ) >= 0; + } + }, + 'notcontains': function( a, b ) { + return ! this.contains( a, b ); + }, + 'greater': function( a, b ) { + /* + * In 2.9.x, you could use the greater and less like string count. + * i.e. if textbox > (empty string) do something. + * This recreates that ability. + */ + if ( jQuery.isNumeric( b ) ) { + return parseFloat( a ) > parseFloat( b ); + } else if ( 'string' == typeof a ) { + return 0 < a.length; + } + + }, + 'less': function( a, b ) { + /* + * In 2.9.x, you could use the greater and less like string count. + * i.e. if textbox > (empty string) do something. + * This recreates that ability. + */ + if ( jQuery.isNumeric( b ) ) { + return parseFloat( a ) < parseFloat( b ); + } else if ( 'string' == typeof a ) { + return 0 >= a.length; + } + + }, + 'greaterequal': function( a, b ) { + return parseFloat( a ) > parseFloat( b ) || parseFloat( a ) == parseFloat( b ); + }, + 'lessequal': function( a, b ) { + return parseFloat( a ) < parseFloat( b ) || parseFloat( a ) == parseFloat( b ); + } + } + } ); + + return model; +} ); \ No newline at end of file diff --git a/assets/js/lib/almond.js b/assets/js/lib/almond.js new file mode 100644 index 0000000..2a06588 --- /dev/null +++ b/assets/js/lib/almond.js @@ -0,0 +1,430 @@ +/** + * @license almond 0.3.1 Copyright (c) 2011-2014, The Dojo Foundation All Rights Reserved. + * Available via the MIT or new BSD license. + * see: http://github.com/jrburke/almond for details + */ +//Going sloppy to avoid 'use strict' string cost, but strict practices should +//be followed. +/*jslint sloppy: true */ +/*global setTimeout: false */ + +var requirejs, require, define; +(function (undef) { + var main, req, makeMap, handlers, + defined = {}, + waiting = {}, + config = {}, + defining = {}, + hasOwn = Object.prototype.hasOwnProperty, + aps = [].slice, + jsSuffixRegExp = /\.js$/; + + function hasProp(obj, prop) { + return hasOwn.call(obj, prop); + } + + /** + * Given a relative module name, like ./something, normalize it to + * a real name that can be mapped to a path. + * @param {String} name the relative name + * @param {String} baseName a real name that the name arg is relative + * to. + * @returns {String} normalized name + */ + function normalize(name, baseName) { + var nameParts, nameSegment, mapValue, foundMap, lastIndex, + foundI, foundStarMap, starI, i, j, part, + baseParts = baseName && baseName.split("/"), + map = config.map, + starMap = (map && map['*']) || {}; + + //Adjust any relative paths. + if (name && name.charAt(0) === ".") { + //If have a base name, try to normalize against it, + //otherwise, assume it is a top-level require that will + //be relative to baseUrl in the end. + if (baseName) { + name = name.split('/'); + lastIndex = name.length - 1; + + // Node .js allowance: + if (config.nodeIdCompat && jsSuffixRegExp.test(name[lastIndex])) { + name[lastIndex] = name[lastIndex].replace(jsSuffixRegExp, ''); + } + + //Lop off the last part of baseParts, so that . matches the + //"directory" and not name of the baseName's module. For instance, + //baseName of "one/two/three", maps to "one/two/three.js", but we + //want the directory, "one/two" for this normalization. + name = baseParts.slice(0, baseParts.length - 1).concat(name); + + //start trimDots + for (i = 0; i < name.length; i += 1) { + part = name[i]; + if (part === ".") { + name.splice(i, 1); + i -= 1; + } else if (part === "..") { + if (i === 1 && (name[2] === '..' || name[0] === '..')) { + //End of the line. Keep at least one non-dot + //path segment at the front so it can be mapped + //correctly to disk. Otherwise, there is likely + //no path mapping for a path starting with '..'. + //This can still fail, but catches the most reasonable + //uses of .. + break; + } else if (i > 0) { + name.splice(i - 1, 2); + i -= 2; + } + } + } + //end trimDots + + name = name.join("/"); + } else if (name.indexOf('./') === 0) { + // No baseName, so this is ID is resolved relative + // to baseUrl, pull off the leading dot. + name = name.substring(2); + } + } + + //Apply map config if available. + if ((baseParts || starMap) && map) { + nameParts = name.split('/'); + + for (i = nameParts.length; i > 0; i -= 1) { + nameSegment = nameParts.slice(0, i).join("/"); + + if (baseParts) { + //Find the longest baseName segment match in the config. + //So, do joins on the biggest to smallest lengths of baseParts. + for (j = baseParts.length; j > 0; j -= 1) { + mapValue = map[baseParts.slice(0, j).join('/')]; + + //baseName segment has config, find if it has one for + //this name. + if (mapValue) { + mapValue = mapValue[nameSegment]; + if (mapValue) { + //Match, update name to the new value. + foundMap = mapValue; + foundI = i; + break; + } + } + } + } + + if (foundMap) { + break; + } + + //Check for a star map match, but just hold on to it, + //if there is a shorter segment match later in a matching + //config, then favor over this star map. + if (!foundStarMap && starMap && starMap[nameSegment]) { + foundStarMap = starMap[nameSegment]; + starI = i; + } + } + + if (!foundMap && foundStarMap) { + foundMap = foundStarMap; + foundI = starI; + } + + if (foundMap) { + nameParts.splice(0, foundI, foundMap); + name = nameParts.join('/'); + } + } + + return name; + } + + function makeRequire(relName, forceSync) { + return function () { + //A version of a require function that passes a moduleName + //value for items that may need to + //look up paths relative to the moduleName + var args = aps.call(arguments, 0); + + //If first arg is not require('string'), and there is only + //one arg, it is the array form without a callback. Insert + //a null so that the following concat is correct. + if (typeof args[0] !== 'string' && args.length === 1) { + args.push(null); + } + return req.apply(undef, args.concat([relName, forceSync])); + }; + } + + function makeNormalize(relName) { + return function (name) { + return normalize(name, relName); + }; + } + + function makeLoad(depName) { + return function (value) { + defined[depName] = value; + }; + } + + function callDep(name) { + if (hasProp(waiting, name)) { + var args = waiting[name]; + delete waiting[name]; + defining[name] = true; + main.apply(undef, args); + } + + if (!hasProp(defined, name) && !hasProp(defining, name)) { + throw new Error('No ' + name); + } + return defined[name]; + } + + //Turns a plugin!resource to [plugin, resource] + //with the plugin being undefined if the name + //did not have a plugin prefix. + function splitPrefix(name) { + var prefix, + index = name ? name.indexOf('!') : -1; + if (index > -1) { + prefix = name.substring(0, index); + name = name.substring(index + 1, name.length); + } + return [prefix, name]; + } + + /** + * Makes a name map, normalizing the name, and using a plugin + * for normalization if necessary. Grabs a ref to plugin + * too, as an optimization. + */ + makeMap = function (name, relName) { + var plugin, + parts = splitPrefix(name), + prefix = parts[0]; + + name = parts[1]; + + if (prefix) { + prefix = normalize(prefix, relName); + plugin = callDep(prefix); + } + + //Normalize according + if (prefix) { + if (plugin && plugin.normalize) { + name = plugin.normalize(name, makeNormalize(relName)); + } else { + name = normalize(name, relName); + } + } else { + name = normalize(name, relName); + parts = splitPrefix(name); + prefix = parts[0]; + name = parts[1]; + if (prefix) { + plugin = callDep(prefix); + } + } + + //Using ridiculous property names for space reasons + return { + f: prefix ? prefix + '!' + name : name, //fullName + n: name, + pr: prefix, + p: plugin + }; + }; + + function makeConfig(name) { + return function () { + return (config && config.config && config.config[name]) || {}; + }; + } + + handlers = { + require: function (name) { + return makeRequire(name); + }, + exports: function (name) { + var e = defined[name]; + if (typeof e !== 'undefined') { + return e; + } else { + return (defined[name] = {}); + } + }, + module: function (name) { + return { + id: name, + uri: '', + exports: defined[name], + config: makeConfig(name) + }; + } + }; + + main = function (name, deps, callback, relName) { + var cjsModule, depName, ret, map, i, + args = [], + callbackType = typeof callback, + usingExports; + + //Use name if no relName + relName = relName || name; + + //Call the callback to define the module, if necessary. + if (callbackType === 'undefined' || callbackType === 'function') { + //Pull out the defined dependencies and pass the ordered + //values to the callback. + //Default to [require, exports, module] if no deps + deps = !deps.length && callback.length ? ['require', 'exports', 'module'] : deps; + for (i = 0; i < deps.length; i += 1) { + map = makeMap(deps[i], relName); + depName = map.f; + + //Fast path CommonJS standard dependencies. + if (depName === "require") { + args[i] = handlers.require(name); + } else if (depName === "exports") { + //CommonJS module spec 1.1 + args[i] = handlers.exports(name); + usingExports = true; + } else if (depName === "module") { + //CommonJS module spec 1.1 + cjsModule = args[i] = handlers.module(name); + } else if (hasProp(defined, depName) || + hasProp(waiting, depName) || + hasProp(defining, depName)) { + args[i] = callDep(depName); + } else if (map.p) { + map.p.load(map.n, makeRequire(relName, true), makeLoad(depName), {}); + args[i] = defined[depName]; + } else { + throw new Error(name + ' missing ' + depName); + } + } + + ret = callback ? callback.apply(defined[name], args) : undefined; + + if (name) { + //If setting exports via "module" is in play, + //favor that over return value and exports. After that, + //favor a non-undefined return value over exports use. + if (cjsModule && cjsModule.exports !== undef && + cjsModule.exports !== defined[name]) { + defined[name] = cjsModule.exports; + } else if (ret !== undef || !usingExports) { + //Use the return value from the function. + defined[name] = ret; + } + } + } else if (name) { + //May just be an object definition for the module. Only + //worry about defining if have a module name. + defined[name] = callback; + } + }; + + requirejs = require = req = function (deps, callback, relName, forceSync, alt) { + if (typeof deps === "string") { + if (handlers[deps]) { + //callback in this case is really relName + return handlers[deps](callback); + } + //Just return the module wanted. In this scenario, the + //deps arg is the module name, and second arg (if passed) + //is just the relName. + //Normalize module name, if it contains . or .. + return callDep(makeMap(deps, callback).f); + } else if (!deps.splice) { + //deps is a config object, not an array. + config = deps; + if (config.deps) { + req(config.deps, config.callback); + } + if (!callback) { + return; + } + + if (callback.splice) { + //callback is an array, which means it is a dependency list. + //Adjust args if there are dependencies + deps = callback; + callback = relName; + relName = null; + } else { + deps = undef; + } + } + + //Support require(['a']) + callback = callback || function () {}; + + //If relName is a function, it is an errback handler, + //so remove it. + if (typeof relName === 'function') { + relName = forceSync; + forceSync = alt; + } + + //Simulate async callback; + if (forceSync) { + main(undef, deps, callback, relName); + } else { + //Using a non-zero value because of concern for what old browsers + //do, and latest browsers "upgrade" to 4 if lower value is used: + //http://www.whatwg.org/specs/web-apps/current-work/multipage/timers.html#dom-windowtimers-settimeout: + //If want a value immediately, use require('id') instead -- something + //that works in almond on the global level, but not guaranteed and + //unlikely to work in other AMD implementations. + setTimeout(function () { + main(undef, deps, callback, relName); + }, 4); + } + + return req; + }; + + /** + * Just drops the config on the floor, but returns req in case + * the config return value is used. + */ + req.config = function (cfg) { + return req(cfg); + }; + + /** + * Expose module registry for debugging and tooling + */ + requirejs._defined = defined; + + define = function (name, deps, callback) { + if (typeof name !== 'string') { + throw new Error('See almond README: incorrect module build, no module name'); + } + + //This module may not have dependencies + if (!deps.splice) { + //deps is not an array, so probably means + //an object literal or factory function for + //the value. Adjust args. + callback = deps; + deps = []; + } + + if (!hasProp(defined, name) && !hasProp(waiting, name)) { + waiting[name] = [name, deps, callback]; + } + }; + + define.amd = { + jQuery: true + }; +}()); diff --git a/assets/js/min/builder.js b/assets/js/min/builder.js new file mode 100644 index 0000000..61da0e9 --- /dev/null +++ b/assets/js/min/builder.js @@ -0,0 +1,2430 @@ +(function () { +/** + * @license almond 0.3.1 Copyright (c) 2011-2014, The Dojo Foundation All Rights Reserved. + * Available via the MIT or new BSD license. + * see: http://github.com/jrburke/almond for details + */ +//Going sloppy to avoid 'use strict' string cost, but strict practices should +//be followed. +/*jslint sloppy: true */ +/*global setTimeout: false */ + +var requirejs, require, define; +(function (undef) { + var main, req, makeMap, handlers, + defined = {}, + waiting = {}, + config = {}, + defining = {}, + hasOwn = Object.prototype.hasOwnProperty, + aps = [].slice, + jsSuffixRegExp = /\.js$/; + + function hasProp(obj, prop) { + return hasOwn.call(obj, prop); + } + + /** + * Given a relative module name, like ./something, normalize it to + * a real name that can be mapped to a path. + * @param {String} name the relative name + * @param {String} baseName a real name that the name arg is relative + * to. + * @returns {String} normalized name + */ + function normalize(name, baseName) { + var nameParts, nameSegment, mapValue, foundMap, lastIndex, + foundI, foundStarMap, starI, i, j, part, + baseParts = baseName && baseName.split("/"), + map = config.map, + starMap = (map && map['*']) || {}; + + //Adjust any relative paths. + if (name && name.charAt(0) === ".") { + //If have a base name, try to normalize against it, + //otherwise, assume it is a top-level require that will + //be relative to baseUrl in the end. + if (baseName) { + name = name.split('/'); + lastIndex = name.length - 1; + + // Node .js allowance: + if (config.nodeIdCompat && jsSuffixRegExp.test(name[lastIndex])) { + name[lastIndex] = name[lastIndex].replace(jsSuffixRegExp, ''); + } + + //Lop off the last part of baseParts, so that . matches the + //"directory" and not name of the baseName's module. For instance, + //baseName of "one/two/three", maps to "one/two/three.js", but we + //want the directory, "one/two" for this normalization. + name = baseParts.slice(0, baseParts.length - 1).concat(name); + + //start trimDots + for (i = 0; i < name.length; i += 1) { + part = name[i]; + if (part === ".") { + name.splice(i, 1); + i -= 1; + } else if (part === "..") { + if (i === 1 && (name[2] === '..' || name[0] === '..')) { + //End of the line. Keep at least one non-dot + //path segment at the front so it can be mapped + //correctly to disk. Otherwise, there is likely + //no path mapping for a path starting with '..'. + //This can still fail, but catches the most reasonable + //uses of .. + break; + } else if (i > 0) { + name.splice(i - 1, 2); + i -= 2; + } + } + } + //end trimDots + + name = name.join("/"); + } else if (name.indexOf('./') === 0) { + // No baseName, so this is ID is resolved relative + // to baseUrl, pull off the leading dot. + name = name.substring(2); + } + } + + //Apply map config if available. + if ((baseParts || starMap) && map) { + nameParts = name.split('/'); + + for (i = nameParts.length; i > 0; i -= 1) { + nameSegment = nameParts.slice(0, i).join("/"); + + if (baseParts) { + //Find the longest baseName segment match in the config. + //So, do joins on the biggest to smallest lengths of baseParts. + for (j = baseParts.length; j > 0; j -= 1) { + mapValue = map[baseParts.slice(0, j).join('/')]; + + //baseName segment has config, find if it has one for + //this name. + if (mapValue) { + mapValue = mapValue[nameSegment]; + if (mapValue) { + //Match, update name to the new value. + foundMap = mapValue; + foundI = i; + break; + } + } + } + } + + if (foundMap) { + break; + } + + //Check for a star map match, but just hold on to it, + //if there is a shorter segment match later in a matching + //config, then favor over this star map. + if (!foundStarMap && starMap && starMap[nameSegment]) { + foundStarMap = starMap[nameSegment]; + starI = i; + } + } + + if (!foundMap && foundStarMap) { + foundMap = foundStarMap; + foundI = starI; + } + + if (foundMap) { + nameParts.splice(0, foundI, foundMap); + name = nameParts.join('/'); + } + } + + return name; + } + + function makeRequire(relName, forceSync) { + return function () { + //A version of a require function that passes a moduleName + //value for items that may need to + //look up paths relative to the moduleName + var args = aps.call(arguments, 0); + + //If first arg is not require('string'), and there is only + //one arg, it is the array form without a callback. Insert + //a null so that the following concat is correct. + if (typeof args[0] !== 'string' && args.length === 1) { + args.push(null); + } + return req.apply(undef, args.concat([relName, forceSync])); + }; + } + + function makeNormalize(relName) { + return function (name) { + return normalize(name, relName); + }; + } + + function makeLoad(depName) { + return function (value) { + defined[depName] = value; + }; + } + + function callDep(name) { + if (hasProp(waiting, name)) { + var args = waiting[name]; + delete waiting[name]; + defining[name] = true; + main.apply(undef, args); + } + + if (!hasProp(defined, name) && !hasProp(defining, name)) { + throw new Error('No ' + name); + } + return defined[name]; + } + + //Turns a plugin!resource to [plugin, resource] + //with the plugin being undefined if the name + //did not have a plugin prefix. + function splitPrefix(name) { + var prefix, + index = name ? name.indexOf('!') : -1; + if (index > -1) { + prefix = name.substring(0, index); + name = name.substring(index + 1, name.length); + } + return [prefix, name]; + } + + /** + * Makes a name map, normalizing the name, and using a plugin + * for normalization if necessary. Grabs a ref to plugin + * too, as an optimization. + */ + makeMap = function (name, relName) { + var plugin, + parts = splitPrefix(name), + prefix = parts[0]; + + name = parts[1]; + + if (prefix) { + prefix = normalize(prefix, relName); + plugin = callDep(prefix); + } + + //Normalize according + if (prefix) { + if (plugin && plugin.normalize) { + name = plugin.normalize(name, makeNormalize(relName)); + } else { + name = normalize(name, relName); + } + } else { + name = normalize(name, relName); + parts = splitPrefix(name); + prefix = parts[0]; + name = parts[1]; + if (prefix) { + plugin = callDep(prefix); + } + } + + //Using ridiculous property names for space reasons + return { + f: prefix ? prefix + '!' + name : name, //fullName + n: name, + pr: prefix, + p: plugin + }; + }; + + function makeConfig(name) { + return function () { + return (config && config.config && config.config[name]) || {}; + }; + } + + handlers = { + require: function (name) { + return makeRequire(name); + }, + exports: function (name) { + var e = defined[name]; + if (typeof e !== 'undefined') { + return e; + } else { + return (defined[name] = {}); + } + }, + module: function (name) { + return { + id: name, + uri: '', + exports: defined[name], + config: makeConfig(name) + }; + } + }; + + main = function (name, deps, callback, relName) { + var cjsModule, depName, ret, map, i, + args = [], + callbackType = typeof callback, + usingExports; + + //Use name if no relName + relName = relName || name; + + //Call the callback to define the module, if necessary. + if (callbackType === 'undefined' || callbackType === 'function') { + //Pull out the defined dependencies and pass the ordered + //values to the callback. + //Default to [require, exports, module] if no deps + deps = !deps.length && callback.length ? ['require', 'exports', 'module'] : deps; + for (i = 0; i < deps.length; i += 1) { + map = makeMap(deps[i], relName); + depName = map.f; + + //Fast path CommonJS standard dependencies. + if (depName === "require") { + args[i] = handlers.require(name); + } else if (depName === "exports") { + //CommonJS module spec 1.1 + args[i] = handlers.exports(name); + usingExports = true; + } else if (depName === "module") { + //CommonJS module spec 1.1 + cjsModule = args[i] = handlers.module(name); + } else if (hasProp(defined, depName) || + hasProp(waiting, depName) || + hasProp(defining, depName)) { + args[i] = callDep(depName); + } else if (map.p) { + map.p.load(map.n, makeRequire(relName, true), makeLoad(depName), {}); + args[i] = defined[depName]; + } else { + throw new Error(name + ' missing ' + depName); + } + } + + ret = callback ? callback.apply(defined[name], args) : undefined; + + if (name) { + //If setting exports via "module" is in play, + //favor that over return value and exports. After that, + //favor a non-undefined return value over exports use. + if (cjsModule && cjsModule.exports !== undef && + cjsModule.exports !== defined[name]) { + defined[name] = cjsModule.exports; + } else if (ret !== undef || !usingExports) { + //Use the return value from the function. + defined[name] = ret; + } + } + } else if (name) { + //May just be an object definition for the module. Only + //worry about defining if have a module name. + defined[name] = callback; + } + }; + + requirejs = require = req = function (deps, callback, relName, forceSync, alt) { + if (typeof deps === "string") { + if (handlers[deps]) { + //callback in this case is really relName + return handlers[deps](callback); + } + //Just return the module wanted. In this scenario, the + //deps arg is the module name, and second arg (if passed) + //is just the relName. + //Normalize module name, if it contains . or .. + return callDep(makeMap(deps, callback).f); + } else if (!deps.splice) { + //deps is a config object, not an array. + config = deps; + if (config.deps) { + req(config.deps, config.callback); + } + if (!callback) { + return; + } + + if (callback.splice) { + //callback is an array, which means it is a dependency list. + //Adjust args if there are dependencies + deps = callback; + callback = relName; + relName = null; + } else { + deps = undef; + } + } + + //Support require(['a']) + callback = callback || function () {}; + + //If relName is a function, it is an errback handler, + //so remove it. + if (typeof relName === 'function') { + relName = forceSync; + forceSync = alt; + } + + //Simulate async callback; + if (forceSync) { + main(undef, deps, callback, relName); + } else { + //Using a non-zero value because of concern for what old browsers + //do, and latest browsers "upgrade" to 4 if lower value is used: + //http://www.whatwg.org/specs/web-apps/current-work/multipage/timers.html#dom-windowtimers-settimeout: + //If want a value immediately, use require('id') instead -- something + //that works in almond on the global level, but not guaranteed and + //unlikely to work in other AMD implementations. + setTimeout(function () { + main(undef, deps, callback, relName); + }, 4); + } + + return req; + }; + + /** + * Just drops the config on the floor, but returns req in case + * the config return value is used. + */ + req.config = function (cfg) { + return req(cfg); + }; + + /** + * Expose module registry for debugging and tooling + */ + requirejs._defined = defined; + + define = function (name, deps, callback) { + if (typeof name !== 'string') { + throw new Error('See almond README: incorrect module build, no module name'); + } + + //This module may not have dependencies + if (!deps.splice) { + //deps is not an array, so probably means + //an object literal or factory function for + //the value. Adjust args. + callback = deps; + deps = []; + } + + if (!hasProp(defined, name) && !hasProp(waiting, name)) { + waiting[name] = [name, deps, callback]; + } + }; + + define.amd = { + jQuery: true + }; +}()); + +define("../lib/almond", function(){}); + +/** + * Adds template helpers for the fields conditional logic setting type + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( 'controllers/templateHelpers',[], function() { + var controller = Marionette.Object.extend( { + initialize: function() { + this.listenTo( nfRadio.channel( 'conditions' ), 'init:model', this.addTemplateHelpers ); + this.listenTo( nfRadio.channel( 'conditions' ), 'init:thenModel', this.addTemplateHelpers ); + this.listenTo( nfRadio.channel( 'conditions' ), 'init:whenModel', this.addTemplateHelpers ); + this.listenTo( nfRadio.channel( 'conditions' ), 'init:elseModel', this.addTemplateHelpers ); + + }, + + addTemplateHelpers: function( model ) { + model.set( 'renderKeySelect', this.renderKeySelect ); + model.set( 'renderComparators', this.renderComparators ); + model.set( 'renderTriggers', this.renderTriggers ); + model.set( 'renderWhenValue', this.renderWhenValue ); + model.set( 'renderItemValue', this.renderItemValue ); + }, + + renderKeySelect: function( currentValue, modelType ) { + + var groups = [] + + var fieldCollection = nfRadio.channel( 'fields' ).request( 'get:collection' ); + var fieldOptions = _.chain( fieldCollection.models ) + .filter( function( field ) { return ! nfRadio.channel( 'conditions-key-select-field-' + field.get( 'type' ) ).request( 'hide', modelType ) || false; }) + .filter( function( field ) { + + // filter out these fields for the when condition + var notForWhen = [ 'submit', 'hr', 'html', 'save', 'file-upload', 'password', 'passwordconfirm', 'product' ]; + + if( field.get( 'key' ) === currentValue ) { + notForWhen = notForWhen.splice( notForWhen.indexOf( field.get( 'type' ), 1) ); + } + + if( notForWhen.includes( field.get( 'type' ) ) && 'when' === modelType ) { + return false; + } + + return true; + }) + .map( function( field ) { + var label = field.get( 'label' ) + if( 'undefined' !== typeof field.get( 'admin_label' ) && 0 < field.get( 'admin_label' ).length ){ + label = field.get( 'admin_label' ); + } + return { key: field.get( 'key' ), label: label }; } + ) + .sortBy( function( field ){ + return field.label.toLowerCase(); + } ) + .value(); + + groups.push( { label: 'Fields', type: 'field', options: fieldOptions } ); + + var calcCollection = nfRadio.channel( 'settings' ).request( 'get:setting', 'calculations' ); + + /* + * If we are working on a 'when' model and we have calculations, add them to our select options. + */ + if ( 'when' == modelType && 0 < calcCollection.length ) { + var calcOptions = calcCollection.map( function( calc ) { + return { key: calc.get( 'name' ), label: calc.get( 'name' ) }; + } ); + + groups.push( { label: 'Calculations', type: 'calc', options: calcOptions } ); + } + + /* + * Pass our groups through any 'when/then' group filters we have. + */ + var filters = nfRadio.channel( 'conditions' ).request( 'get:groupFilters' ); + _.each( filters, function( filter ) { + groups = filter( groups, modelType ); + } ); + + /* + * Use a template to get our field select + */ + var template = Backbone.Radio.channel( 'app' ).request( 'get:template', '#tmpl-nf-cl-key-select' ); + + var tmp = template( { groups: groups, currentValue: currentValue } ); + return tmp; + }, + + renderComparators: function( type, key, currentComparator ) { + var defaultComparators = { + equal: { + label: nfcli18n.templateHelperEquals, + value: 'equal' + }, + + notequal: { + label: nfcli18n.templateHelperDoesNotEqual, + value: 'notequal' + }, + + contains: { + label: nfcli18n.templateHelperContains, + value: 'contains' + }, + + notcontains: { + label: nfcli18n.templateHelperDoesNotContain, + value: 'notcontains' + }, + + greater: { + label: nfcli18n.templateHelperGreaterThan, + value: 'greater' + }, + + less: { + label: nfcli18n.templateHelperLessThan, + value: 'less' + } + }; + + if ( key ) { + /* + * This could be a field or a calculation key. If it's a calc key, get the calc model. + */ + if ( 'calc' == type ) { + var comparators = _.omit( defaultComparators, 'contains', 'notcontains' ); + _.extend( comparators, { + lessequal: { + label: nfcli18n.templateHelperLessThanOrEqual, + value: 'lessequal' + }, + + greaterequal: { + label: nfcli18n.templateHelperGreaterThanOrEqual, + value: 'greaterequal' + } + } ); + } else { + /* + * Send out a radio request for an html value on a channel based upon the field type. + * + * Get our field by key + * Get our field type model + * + * Send out a message on the type channel + * If we don't get a response, send a message out on the parent type channel + */ + var fieldModel = nfRadio.channel( 'fields' ).request( 'get:field', key ); + + if( fieldModel ) { + var comparators = nfRadio.channel('conditions-' + fieldModel.get('type')).request('get:comparators', defaultComparators, currentComparator ); + if (!comparators) { + var typeModel = nfRadio.channel('fields').request('get:type', fieldModel.get('type')); + comparators = nfRadio.channel('conditions-' + typeModel.get('parentType')).request('get:comparators', defaultComparators, currentComparator ) || defaultComparators; + } + } else { + var comparators = defaultComparators; + } + } + } else { + var comparators = defaultComparators; + } + + /* + * Use a template to get our comparator select + */ + var template = Backbone.Radio.channel( 'app' ).request( 'get:template', '#tmpl-nf-cl-comparators' ); + return template( { comparators: comparators, currentComparator: currentComparator } ); + }, + + renderTriggers: function( type, key, currentTrigger, value ) { + var defaultTriggers = { + show_field: { + label: nfcli18n.templateHelperShowField, + value: 'show_field' + }, + + hide_field: { + label: nfcli18n.templateHelperHideField, + value: 'hide_field' + }, + + change_value: { + label: nfcli18n.templateHelperChangeValue, + value: 'change_value' + }, + + set_required: { + label: nfcli18n.templateHelperSetRequired, + value: 'set_required' + }, + + unset_required: { + label: nfcli18n.templateHelperUnsetRequired, + value: 'unset_required' + } + }; + + if ( key && 'field' == type ) { + /* + * Send out a radio request for an html value on a channel based upon the field type. + * + * Get our field by key + * Get our field type model + * + * Send out a message on the type channel + * If we don't get a response, send a message out on the parent type channel + */ + var fieldModel = nfRadio.channel( 'fields' ).request( 'get:field', key ); + + if( 'undefined' != typeof fieldModel ) { + var typeModel = nfRadio.channel('fields').request('get:type', fieldModel.get('type')); + + var triggers = nfRadio.channel('conditions-' + fieldModel.get('type')).request('get:triggers', defaultTriggers); + if (!triggers) { + triggers = nfRadio.channel('conditions-' + typeModel.get('parentType')).request('get:triggers', defaultTriggers) || defaultTriggers; + } + } else { + var triggers = nfRadio.channel( 'conditions-' + type ).request( 'get:triggers', defaultTriggers ) || defaultTriggers; + } + } else { + var triggers = nfRadio.channel( 'conditions-' + type ).request( 'get:triggers', defaultTriggers ) || defaultTriggers; + } + + + /* + * Use a template to get our comparator select + */ + var template = Backbone.Radio.channel( 'app' ).request( 'get:template', '#tmpl-nf-cl-triggers' ); + return template( { triggers: triggers, currentTrigger: currentTrigger } ); + }, + + renderWhenValue: function( type, key, comparator, value ) { + /* + * Use a template to get our value + */ + var template = Backbone.Radio.channel( 'app' ).request( 'get:template', '#tmpl-nf-cl-value-default' ); + var defaultHTML = template( { value: value } ); + + /* + * If we have a key and it's not a calc, get our field type based HTML. + */ + if ( key && 'calc' != type ) { + /* + * Send out a radio request for an html value on a channel based upon the field type. + * + * Get our field by key + * Get our field type model + * + * Send out a message on the type channel + * If we don't get a response, send a message out on the parent type channel + */ + var fieldModel = nfRadio.channel( 'fields' ).request( 'get:field', key ); + + if( fieldModel ) { + var html = nfRadio.channel('conditions-' + fieldModel.get('type')).request('get:valueInput', key, comparator, value); + if (!html) { + var typeModel = nfRadio.channel('fields').request('get:type', fieldModel.get('type')); + html = nfRadio.channel('conditions-' + typeModel.get('parentType')).request('get:valueInput', key, comparator, value) || defaultHTML; + } + } else { + html = defaultHTML; + } + } else { + var html = defaultHTML; + } + + return html; + }, + + renderItemValue: function( key, trigger, value ) { + /* + * Use a template to get our value + * + * TODO: This should be much more dynamic. + * At the moment, we manually check to see if we are doing a "change_value" or similar trigger. + */ + if ( trigger != 'change_value' + && trigger != 'select_option' + && trigger != 'deselect_option' + && trigger != 'show_option' + && trigger != 'hide_option' + ) { + return ''; + } + + var template = Backbone.Radio.channel( 'app' ).request( 'get:template', '#tmpl-nf-cl-value-default' ); + var defaultHTML = template( { value: value } ); + + if ( key ) { + /* + * Send out a radio request for an html value on a channel based upon the field type. + * + * Get our field by key + * Get our field type model + * + * Send out a message on the type channel + * If we don't get a response, send a message out on the parent type channel + */ + var fieldModel = nfRadio.channel( 'fields' ).request( 'get:field', key ); + + if( 'undefined' != typeof fieldModel ) { + var typeModel = nfRadio.channel('fields').request('get:type', fieldModel.get('type')); + var html = nfRadio.channel('conditions-' + fieldModel.get('type')).request('get:valueInput', key, trigger, value); + if (!html) { + html = nfRadio.channel('conditions-' + typeModel.get('parentType')).request('get:valueInput', key, trigger, value) || defaultHTML; + } + } + } else { + var html = defaultHTML; + } + + return html; + } + }); + + return controller; +} ); + +/** + * Item view for our condition and + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( 'views/advanced/whenItem',[], function( ) { + var view = Marionette.ItemView.extend({ + template: "#tmpl-nf-cl-advanced-when-item", + + initialize: function() { + this.listenTo( this.model, 'change', this.render ); + }, + + onRender: function() { + let el = jQuery( this.el ).find( '[data-type="date"]' ); + jQuery( el ).mask( '9999-99-99' ); + }, + + events: { + 'change .setting': 'changeSetting', + 'click .nf-remove-when': 'clickRemove' + }, + + changeSetting: function( e ) { + nfRadio.channel( 'conditions' ).trigger( 'change:setting', e, this.model ) + }, + + clickRemove: function( e ) { + nfRadio.channel( 'conditions' ).trigger( 'click:removeWhen', e, this.model ); + } + }); + + return view; +} ); +/** + * Item view for our condition's first when + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( 'views/advanced/firstWhenItem',[], function( ) { + var view = Marionette.ItemView.extend({ + template: "#tmpl-nf-cl-advanced-first-when-item", + + initialize: function() { + this.listenTo( this.model, 'change', this.render ); + }, + + onRender: function() { + let el = jQuery( this.el ).find( '[data-type="date"]' ); + jQuery( el ).mask( '9999-99-99' ); + }, + + events: { + 'change .setting': 'changeSetting', + 'change .extra': 'changeExtra', + }, + + changeSetting: function( e ) { + nfRadio.channel( 'conditions' ).trigger( 'change:setting', e, this.model ); + }, + + changeExtra: function( e ) { + nfRadio.channel( 'conditions' ).trigger( 'change:extra', e, this.model ); + } + }); + + return view; +} ); +/** + * Collection view for our when collection + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( 'views/advanced/whenCollection',[ 'views/advanced/whenItem', 'views/advanced/firstWhenItem' ], function( WhenItem, FirstWhenItem ) { + var view = Marionette.CollectionView.extend({ + getChildView: function( item ) { + if ( item.collection.first() == item ) { + return FirstWhenItem; + } else { + return WhenItem; + } + + }, + + initialize: function( options ) { + this.firstWhenDiv = options.firstWhenDiv; + this.conditionModel = options.conditionModel; + }, + + // The default implementation: + attachHtml: function( collectionView, childView, index ) { + if ( 0 == index ) { + this.firstWhenDiv.append( childView.el ); + } else { + if ( ! this.conditionModel.get( 'collapsed' ) ) { + if (collectionView.isBuffering) { + // buffering happens on reset events and initial renders + // in order to reduce the number of inserts into the + // document, which are expensive. + collectionView._bufferedChildren.splice(index, 0, childView); + } else { + // If we've already rendered the main collection, append + // the new child into the correct order if we need to. Otherwise + // append to the end. + if (!collectionView._insertBefore(childView, index)){ + collectionView._insertAfter(childView); + } + } + } + } + }, + + } ); + + return view; +} ); +/** + * Item view for our condition then + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( 'views/advanced/thenItem',[], function( ) { + var view = Marionette.ItemView.extend({ + template: "#tmpl-nf-cl-trigger-item", + + initialize: function() { + this.listenTo( this.model, 'change', this.render ); + }, + + events: { + 'change .setting': 'changeSetting', + 'click .nf-remove-then': 'clickRemove' + }, + + changeSetting: function( e ) { + nfRadio.channel( 'conditions' ).trigger( 'change:setting', e, this.model ); + nfRadio.channel( 'conditions' ).trigger( 'change:then', e, this.model ); + }, + + clickRemove: function( e ) { + nfRadio.channel( 'conditions' ).trigger( 'click:removeThen', e, this.model ); + } + }); + + return view; +} ); +/** + * Collection view for our then statements + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( 'views/advanced/thenCollection',[ 'views/advanced/thenItem' ], function( ThenItem ) { + var view = Marionette.CollectionView.extend({ + childView: ThenItem, + + initialize: function( options ) { + + } + }); + + return view; +} ); +/** + * Item view for our condition then + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( 'views/advanced/elseItem',[], function( ) { + var view = Marionette.ItemView.extend({ + template: "#tmpl-nf-cl-trigger-item", + + initialize: function() { + this.listenTo( this.model, 'change', this.render ); + }, + + events: { + 'change .setting': 'changeSetting', + 'click .nf-remove-else': 'clickRemove' + }, + + changeSetting: function( e ) { + nfRadio.channel( 'conditions' ).trigger( 'change:setting', e, this.model ) + }, + + clickRemove: function( e ) { + nfRadio.channel( 'conditions' ).trigger( 'click:removeElse', e, this.model ); + } + }); + + return view; +} ); +/** + * Collection view for our else statements + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( 'views/advanced/elseCollection',[ 'views/advanced/elseItem' ], function( ElseItem ) { + var view = Marionette.CollectionView.extend({ + childView: ElseItem, + + initialize: function( options ) { + + } + }); + + return view; +} ); +/** + * Layout view for our conditions + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( 'views/advanced/conditionItem',[ 'views/advanced/whenCollection', 'views/advanced/thenCollection', 'views/advanced/elseCollection' ], function( WhenCollectionView, ThenCollectionView, ElseCollectionView ) { + var view = Marionette.LayoutView.extend({ + template: "#tmpl-nf-cl-advanced-condition", + regions: { + 'when': '.nf-when-region', + 'then': '.nf-then-region', + 'else': '.nf-else-region' + }, + + initialize: function() { + /* + * When we change the "collapsed" attribute of our model, re-render. + */ + this.listenTo( this.model, 'change:collapsed', this.render ); + + /* + * When our drawer closes, send out a radio message on our setting type channel. + */ + this.listenTo( nfRadio.channel( 'drawer' ), 'closed', this.drawerClosed ); + }, + + onRender: function() { + var firstWhenDiv = jQuery( this.el ).find( '.nf-first-when' ); + this.when.show( new WhenCollectionView( { collection: this.model.get( 'when' ), firstWhenDiv: firstWhenDiv, conditionModel: this.model } ) ); + if ( ! this.model.get( 'collapsed' ) ) { + this.then.show( new ThenCollectionView( { collection: this.model.get( 'then' ) } ) ); + this.else.show( new ElseCollectionView( { collection: this.model.get( 'else' ) } ) ); + } + }, + + events: { + 'click .nf-remove-condition': 'clickRemove', + 'click .nf-collapse-condition': 'clickCollapse', + 'click .nf-add-when': 'clickAddWhen', + 'click .nf-add-then': 'clickAddThen', + 'click .nf-add-else': 'clickAddElse' + }, + + clickRemove: function( e ) { + nfRadio.channel( 'conditions' ).trigger( 'click:removeCondition', e, this.model ); + }, + + clickCollapse: function( e ) { + nfRadio.channel( 'conditions' ).trigger( 'click:collapseCondition', e, this.model ); + }, + + clickAddWhen: function( e ) { + nfRadio.channel( 'conditions' ).trigger( 'click:addWhen', e, this.model ); + }, + + clickAddThen: function( e ) { + nfRadio.channel( 'conditions' ).trigger( 'click:addThen', e, this.model ); + }, + + clickAddElse: function( e ) { + nfRadio.channel( 'conditions' ).trigger( 'click:addElse', e, this.model ); + } + }); + + return view; +} ); +/** + * Collection view for our conditions + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( 'views/advanced/conditionCollection',[ 'views/advanced/conditionItem' ], function( conditionItem ) { + var view = Marionette.CollectionView.extend({ + childView: conditionItem, + + initialize: function( options ) { + this.collection = options.dataModel.get( 'conditions' ); + }, + + onShow: function() { + /* + * If we don't have any conditions, add an empty one as we render. + */ + if ( 0 == this.collection.length ) { + this.collection.add( {} ); + } + }, + + onBeforeDestroy: function() { + /* + * If we don't have any conditions or we have more than one, just return. + */ + if ( 0 == this.collection.length || 1 < this.collection.length ) return; + /* + * If we only have one condition, and we didn't change the "key" attribute, reset our collection. + * This empties it. + */ + if ( '' == this.collection.models[0].get( 'when' ).models[0].get( 'key' ) ) { + this.collection.reset(); + } + } + }); + + return view; +} ); + +/** + * Item view for our condition and + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( 'views/actions/whenItem',[], function( ) { + var view = Marionette.ItemView.extend({ + template: "#tmpl-nf-cl-actions-condition-when", + + initialize: function() { + this.listenTo( this.model, 'change', this.render ); + }, + + events: { + 'change .setting': 'changeSetting', + 'click .nf-remove-when': 'clickRemove' + }, + + changeSetting: function( e ) { + nfRadio.channel( 'conditions' ).trigger( 'change:setting', e, this.model ) + }, + + clickRemove: function( e ) { + nfRadio.channel( 'conditions' ).trigger( 'click:removeWhen', e, this.model ); + } + }); + + return view; +} ); +/** + * Collection view for our when collection + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( 'views/actions/whenCollection',[ 'views/actions/whenItem' ], function( WhenItem ) { + var view = Marionette.CollectionView.extend({ + childView: WhenItem, + + initialize: function( options ) { + + }, + + onShow: function() { + /* + * If we don't have any conditions, add an empty one as we render. + */ + if ( 0 == this.collection.length ) { + this.collection.add( {} ); + } + }, + + onBeforeDestroy: function() { + /* + * If we don't have any conditions or we have more than one, just return. + */ + if ( 0 == this.collection.length || 1 < this.collection.length ) return; + /* + * If we only have one condition, and we didn't change the "key" attribute, reset our collection. + * This empties it. + */ + if ( '' == this.collection.models[0].get( 'key' ) ) { + this.collection.reset(); + } + } + + } ); + + return view; +} ); +/** + * Layout view for our Action condition + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( 'views/actions/conditionLayout',[ 'views/actions/whenCollection' ], function( WhenCollection ) { + var view = Marionette.LayoutView.extend( { + template: '#tmpl-nf-cl-actions-condition-layout', + + regions: { + 'when': '.nf-when' + }, + + initialize: function( options ) { + this.model = options.dataModel.get( 'conditions' ); + if ( ! options.dataModel.get( 'conditions' ) ) return; + + this.collection = options.dataModel.get( 'conditions' ).get( 'when' ); + this.conditionModel = options.dataModel.get( 'conditions' ); + }, + + onRender: function() { + if ( ! this.collection ) return; + /* + * Show our "when" collection in the "when" area. + */ + this.when.show( new WhenCollection( { collection: this.collection } ) ); + }, + + events: { + 'change .condition-setting': 'changeSetting', + 'click .nf-add-when': 'clickAddWhen' + }, + + clickAddWhen: function( e ) { + nfRadio.channel( 'conditions' ).trigger( 'click:addWhen', e, this.model ); + }, + + changeSetting: function( e ) { + nfRadio.channel( 'conditions' ).trigger( 'change:setting', e, this.model ) + } + + }); + + return view; +} ); +/** + * Returns the childview we need to use for our conditional logic form settings. + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( 'controllers/returnChildView',[ 'views/advanced/conditionCollection', 'views/actions/conditionLayout' ], function( AdvancedView, ActionsView ) { + var controller = Marionette.Object.extend( { + initialize: function() { + nfRadio.channel( 'advanced_conditions' ).reply( 'get:settingChildView', this.getAdvancedChildView ); + nfRadio.channel( 'action_conditions' ).reply( 'get:settingChildView', this.getActionChildView ); + }, + + getAdvancedChildView: function( settingModel ) { + return AdvancedView; + }, + + getActionChildView: function( settingModel ) { + return ActionsView; + } + + }); + + return controller; +} ); + +/** + * When Model + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( 'models/whenModel',[], function() { + var model = Backbone.Model.extend( { + defaults: { + connector: 'AND', + key: '', + comparator: '', + value: '', + type: 'field', + modelType: 'when' + }, + + initialize: function() { + nfRadio.channel( 'conditions' ).trigger( 'init:whenModel', this ); + } + } ); + + return model; +} ); +/** + * When Collection + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( 'models/whenCollection',['models/whenModel'], function( WhenModel ) { + var collection = Backbone.Collection.extend( { + model: WhenModel, + + initialize: function( models, options ) { + this.options = options; + } + } ); + return collection; +} ); +/** + * Then Model + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( 'models/thenModel',[], function() { + var model = Backbone.Model.extend( { + defaults: { + key: '', + trigger: '', + value: '', + type: 'field', + modelType: 'then' + }, + + initialize: function() { + nfRadio.channel( 'conditions' ).trigger( 'init:thenModel', this ); + } + } ); + + return model; +} ); +/** + * Then Collection + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( 'models/thenCollection',['models/thenModel'], function( ThenModel ) { + var collection = Backbone.Collection.extend( { + model: ThenModel, + + initialize: function( models, options ) { + this.options = options; + } + } ); + return collection; +} ); +/** + * Else Model + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( 'models/elseModel',[], function() { + var model = Backbone.Model.extend( { + defaults: { + key: '', + trigger: '', + value: '', + type: 'field', + modelType: 'else' + }, + + initialize: function() { + nfRadio.channel( 'conditions' ).trigger( 'init:elseModel', this ); + } + } ); + + return model; +} ); +/** + * Else Collection + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( 'models/elseCollection',['models/elseModel'], function( ElseModel ) { + var collection = Backbone.Collection.extend( { + model: ElseModel, + + initialize: function( models, options ) { + this.options = options; + } + } ); + return collection; +} ); +/** + * Conditon Model + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( 'models/conditionModel',[ 'models/whenCollection', 'models/thenCollection', 'models/elseCollection' ], function( WhenCollection, ThenCollection, ElseCollection ) { + var model = Backbone.Model.extend( { + defaults: { + collapsed: false, + process: 1, + connector: 'all', + when: [ {} ], + then: [ {} ], + else: [] + }, + + initialize: function() { + this.set( 'when', new WhenCollection( this.get( 'when' ), { conditionModel: this } ) ); + this.set( 'then', new ThenCollection( this.get( 'then' ), { conditionModel: this } ) ); + this.set( 'else', new ElseCollection( this.get( 'else' ), { conditionModel: this } ) ); + + nfRadio.channel( 'conditions' ).trigger( 'init:model', this ); + } + } ); + + return model; +} ); +/** + * Conditon Collection + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( 'models/conditionCollection',['models/conditionModel'], function( ConditionModel ) { + var collection = Backbone.Collection.extend( { + model: ConditionModel, + + initialize: function( models, options ) { + this.options = options; + } + } ); + return collection; +} ); +/** + * Item view for our drawer header + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( 'views/drawerHeader',[], function( ) { + var view = Marionette.ItemView.extend({ + template: "#tmpl-nf-cl-advanced-drawer-header", + + events: { + 'click .nf-add-new': 'clickAddNew' + }, + + clickAddNew: function( e ) { + nfRadio.channel( 'conditions' ).trigger( 'click:addNew', e ); + } + }); + + return view; +} ); +/** + * Adds a new condition when the add new button is clicked. + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( 'controllers/newCondition',[ 'models/whenCollection', 'models/whenModel' ], function( WhenCollection, WhenModel ) { + var controller = Marionette.Object.extend( { + initialize: function() { + this.listenTo( nfRadio.channel( 'conditions' ), 'click:addNew', this.addNew ); + }, + + addNew: function( e ) { + var conditionCollection = nfRadio.channel( 'settings' ).request( 'get:setting', 'conditions' ); + var conditionModel = conditionCollection.add( {} ); + + // Add our condition addition to our change log. + var label = { + object: 'Condition', + label: nfcli18n.newConditionCondition, + change: 'Added', + dashicon: 'plus-alt' + }; + + var data = { + collection: conditionCollection + } + + nfRadio.channel( 'changes' ).request( 'register:change', 'addCondition', conditionModel, null, label, data ); + + // Set our 'clean' status to false so that we get a notice to publish changes + nfRadio.channel( 'app' ).request( 'update:setting', 'clean', false ); + nfRadio.channel( 'app' ).request( 'update:db' ); + } + + }); + + return controller; +} ); + +/** + * Updates condition settings on field change or drawer close + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( 'controllers/updateSettings',[], function() { + var controller = Marionette.Object.extend( { + initialize: function() { + this.listenTo( nfRadio.channel( 'conditions' ), 'change:setting', this.updateSetting ); + }, + + updateSetting: function( e, dataModel ) { + var value = jQuery( e.target ).val(); + var id = jQuery( e.target ).data( 'id' ); + var before = dataModel.get( id ); + + if ( jQuery( e.target ).find( ':selected' ).data( 'type' ) ) { + dataModel.set( 'type', jQuery( e.target ).find( ':selected' ).data( 'type' ) ); + } + + dataModel.set( id, value ); + + var after = value; + + var changes = { + attr: id, + before: before, + after: after + }; + + /* + * The "Advanced" domain uses a collection of conditions, while the "Actions" domain uses a single collection. + * Here, if we don't have a collection property, then dataModel must be our conditionModel. + */ + var conditionModel = ( 'undefined' == typeof dataModel.collection ) ? dataModel : dataModel.collection.options.conditionModel; + + var data = { + conditionModel: conditionModel + } + + var label = { + object: 'Condition', + label: 'Condition', + change: 'Changed ' + id + ' from ' + before + ' to ' + after + }; + + nfRadio.channel( 'changes' ).request( 'register:change', 'changeSetting', dataModel, changes, label, data ); + + // Set our 'clean' status to false so that we get a notice to publish changes + nfRadio.channel( 'app' ).request( 'update:setting', 'clean', false ); + nfRadio.channel( 'app' ).request( 'update:db' ); + } + + }); + + return controller; +} ); +/** + * Listens for clicks on our different condition controls + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( 'controllers/clickControls',[], function() { + var controller = Marionette.Object.extend( { + initialize: function() { + this.listenTo( nfRadio.channel( 'conditions' ), 'click:removeCondition', this.removeCondition ); + this.listenTo( nfRadio.channel( 'conditions' ), 'click:collapseCondition', this.collapseCondition ); + this.listenTo( nfRadio.channel( 'conditions' ), 'click:removeWhen', this.removeWhen ); + this.listenTo( nfRadio.channel( 'conditions' ), 'click:removeThen', this.removeThen ); + this.listenTo( nfRadio.channel( 'conditions' ), 'click:removeElse', this.removeElse ); + this.listenTo( nfRadio.channel( 'conditions' ), 'click:addWhen', this.addWhen ); + this.listenTo( nfRadio.channel( 'conditions' ), 'click:addThen', this.addThen ); + this.listenTo( nfRadio.channel( 'conditions' ), 'click:addElse', this.addElse ); + }, + + removeCondition: function( e, conditionModel ) { + var conditionCollection = conditionModel.collection; + conditionModel.collection.remove( conditionModel ); + + /* + * Register our remove condition event with our changes manager + */ + + var label = { + object: 'Condition', + label: nfcli18n.clickControlsConditionlabel, + change: 'Removed', + dashicon: 'dismiss' + }; + + var data = { + collection: conditionCollection + } + + nfRadio.channel( 'changes' ).request( 'register:change', 'removeCondition', conditionModel, null, label, data ); + + // Set our 'clean' status to false so that we get a notice to publish changes + nfRadio.channel( 'app' ).request( 'update:setting', 'clean', false ); + nfRadio.channel( 'app' ).request( 'update:db' ); + }, + + collapseCondition: function( e, conditionModel ) { + conditionModel.set( 'collapsed', ! conditionModel.get( 'collapsed' ) ); + }, + + removeWhen: function( e, whenModel ) { + var collection = whenModel.collection; + this.removeItem( whenModel ); + /* + * Register our remove when change. + */ + + var label = { + object: 'Condition - When', + label: nfcli18n.clickControlsConditionWhen, + change: 'Removed', + dashicon: 'dismiss' + }; + + var data = { + collection: collection + } + + nfRadio.channel( 'changes' ).request( 'register:change', 'removeWhen', whenModel, null, label, data ); + }, + + removeThen: function( e, thenModel ) { + var collection = thenModel.collection; + this.removeItem( thenModel ); + /* + * Register our remove then change. + */ + + var label = { + object: 'Condition - Then', + label: nfcli18n.clickControlsConditionThen, + change: 'Removed', + dashicon: 'dismiss' + }; + + var data = { + collection: collection + } + + nfRadio.channel( 'changes' ).request( 'register:change', 'removeThen', thenModel, null, label, data ); + }, + + removeElse: function( e, elseModel ) { + var collection = elseModel.collection; + this.removeItem( elseModel ); + /* + * Register our remove else change. + */ + + var label = { + object: 'Condition - Else', + label: nfcli18n.clickControlsConditionElse, + change: 'Removed', + dashicon: 'dismiss' + }; + + var data = { + collection: collection + } + + nfRadio.channel( 'changes' ).request( 'register:change', 'removeElse', elseModel, null, label, data ); + + }, + + removeItem: function( itemModel ) { + itemModel.collection.remove( itemModel ); + + // Set our 'clean' status to false so that we get a notice to publish changes + nfRadio.channel( 'app' ).request( 'update:setting', 'clean', false ); + nfRadio.channel( 'app' ).request( 'update:db' ); + }, + + addWhen: function( e, conditionModel ) { + var whenModel = conditionModel.get( 'when' ).add( {} ); + + /* + * Register our add when as a change. + */ + + var label = { + object: 'Condition - When Criteron', + label: nfcli18n.clickControlsConditionWhenCriteron, + change: 'Added', + dashicon: 'plus-alt' + }; + + var data = { + conditionModel: conditionModel + } + + nfRadio.channel( 'changes' ).request( 'register:change', 'addWhen', whenModel, null, label, data ); + + // Set our 'clean' status to false so that we get a notice to publish changes + nfRadio.channel( 'app' ).request( 'update:setting', 'clean', false ); + nfRadio.channel( 'app' ).request( 'update:db' ); + }, + + addThen: function( e, conditionModel ) { + var thenModel = conditionModel.get( 'then' ).add( {} ); + + /* + * Register our add then as a change. + */ + + var label = { + object: 'Condition - Do Item', + label: nfcli18n.clickControlsConditionDoItem, + change: 'Added', + dashicon: 'plus-alt' + }; + + var data = { + conditionModel: conditionModel + } + + nfRadio.channel( 'changes' ).request( 'register:change', 'addThen', thenModel, null, label, data ); + + // Set our 'clean' status to false so that we get a notice to publish changes + nfRadio.channel( 'app' ).request( 'update:setting', 'clean', false ); + nfRadio.channel( 'app' ).request( 'update:db' ); + }, + + addElse: function( e, conditionModel ) { + var elseModel = conditionModel.get( 'else' ).add( {} ); + + /* + * Register our add when as a change. + */ + + var label = { + object: 'Condition - Else Item', + label: nfcli18n.clickControlsConditionElseItem, + change: 'Added', + dashicon: 'plus-alt' + }; + + var data = { + conditionModel: conditionModel + } + + nfRadio.channel( 'changes' ).request( 'register:change', 'addElse', elseModel, null, label, data ); + + // Set our 'clean' status to false so that we get a notice to publish changes + nfRadio.channel( 'app' ).request( 'update:setting', 'clean', false ); + nfRadio.channel( 'app' ).request( 'update:db' ); + } + + }); + + return controller; +} ); + +/** + * Handles undoing everything for conditions. + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( 'controllers/undo',[], function() { + var controller = Marionette.Object.extend( { + initialize: function() { + nfRadio.channel( 'changes' ).reply( 'undo:addCondition', this.undoAddCondition, this ); + nfRadio.channel( 'changes' ).reply( 'undo:removeCondition', this.undoRemoveCondition, this ); + nfRadio.channel( 'changes' ).reply( 'undo:addWhen', this.undoAddWhen, this ); + nfRadio.channel( 'changes' ).reply( 'undo:addThen', this.undoAddThen, this ); + nfRadio.channel( 'changes' ).reply( 'undo:addElse', this.undoAddElse, this ); + nfRadio.channel( 'changes' ).reply( 'undo:removeWhen', this.undoRemoveWhen, this ); + nfRadio.channel( 'changes' ).reply( 'undo:removeThen', this.undoRemoveThen, this ); + nfRadio.channel( 'changes' ).reply( 'undo:removeElse', this.undoRemoveElse, this ); + }, + + undoAddCondition: function( change, undoAll ) { + var dataModel = change.get( 'model' ); + var data = change.get( 'data' ); + + data.collection.remove( dataModel ); + + /* + * Loop through our change collection and remove any setting changes that belong to the condition we've added. + */ + var changeCollection = nfRadio.channel( 'changes' ).request( 'get:collection' ); + var results = changeCollection.where( function( changeModel ) { + if ( ( changeModel = dataModel ) || 'undefined' != typeof changeModel.get( 'data' ).conditionModel && changeModel.get( 'data' ).conditionModel == dataModel ) { + return true; + } else { + return false; + } + } ); + + _.each( results, function( model ) { + changeCollection.remove( model ); + } ); + + this.maybeRemoveChange( change, undoAll ); + }, + + undoRemoveCondition: function( change, undoAll ) { + var dataModel = change.get( 'model' ); + var data = change.get( 'data' ); + + data.collection.add( dataModel ); + + this.maybeRemoveChange( change, undoAll ); + }, + + undoAddWhen: function( change, undoAll ) { + var whenModel = change.get( 'model' ); + var data = change.get( 'data' ); + + data.conditionModel.get( 'when' ).remove( whenModel ); + + this.maybeRemoveChange( change, undoAll ); + }, + + undoAddThen: function( change, undoAll ) { + var thenModel = change.get( 'model' ); + var data = change.get( 'data' ); + + data.conditionModel.get( 'then' ).remove( thenModel ); + + this.maybeRemoveChange( change, undoAll ); + }, + + undoAddElse: function( change, undoAll ) { + var elseModel = change.get( 'model' ); + var data = change.get( 'data' ); + + data.conditionModel.get( 'else' ).remove( elseModel ); + + this.maybeRemoveChange( change, undoAll ); + }, + + undoRemoveWhen: function( change, undoAll ) { + var whenModel = change.get( 'model' ); + var data = change.get( 'data' ); + + data.collection.add( whenModel ); + + this.maybeRemoveChange( change, undoAll ); + }, + + undoRemoveThen: function( change, undoAll ) { + var thenModel = change.get( 'model' ); + var data = change.get( 'data' ); + + data.collection.add( thenModel ); + + this.maybeRemoveChange( change, undoAll ); + }, + + undoRemoveElse: function( change, undoAll ) { + var elseModel = change.get( 'model' ); + var data = change.get( 'data' ); + + data.collection.add( elseModel ); + + this.maybeRemoveChange( change, undoAll ); + }, + + /** + * If our undo action was requested to 'remove' the change from the collection, remove it. + * + * @since 3.0 + * @param backbone.model change model of our change + * @param boolean remove should we remove this item from our change collection + * @return void + */ + maybeRemoveChange: function( change, undoAll ) { + var undoAll = typeof undoAll !== 'undefined' ? undoAll : false; + if ( ! undoAll ) { + // Update preview. + nfRadio.channel( 'app' ).request( 'update:db' ); + var changeCollection = nfRadio.channel( 'changes' ).request( 'get:collection' ); + changeCollection.remove( change ); + if ( 0 == changeCollection.length ) { + nfRadio.channel( 'app' ).request( 'update:setting', 'clean', true ); + nfRadio.channel( 'app' ).request( 'close:drawer' ); + } + } + } + + }); + + return controller; +} ); + +/** + * Returns the type of input value we'd like to use. + * This covers all the core field types. + * + * Add-ons can copy this code structure in order to get custom "values" for conditions. + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( 'controllers/coreValues',[], function() { + var controller = Marionette.Object.extend( { + initialize: function() { + nfRadio.channel( 'conditions-checkbox' ).reply( 'get:valueInput', this.getCheckboxValue ); + nfRadio.channel( 'conditions-list' ).reply( 'get:valueInput', this.getListValue ); + nfRadio.channel( 'conditions-listcountry' ).reply( 'get:valueInput', this.getListCountryValue ); + nfRadio.channel( 'conditions-date' ).reply( 'get:valueInput', this.getDateValue ); + }, + + getCheckboxValue: function( key, trigger, value ) { + /* + * Checks our values ensures they've been converted to strings and + * sets the value. + */ + if( 1 == value && value.length > 1 ) { + value = 'checked'; + } else if( 0 == value && value.length > 1 ) { + value = 'unchecked'; + } else if( 0 == value.length ){ + value = ''; + } + + var template = Backbone.Radio.channel( 'app' ).request( 'get:template', '#tmpl-nf-cl-value-checkbox' ); + return template( { value: value } ); + }, + + getListValue: function( key, trigger, value ) { + var fieldModel = nfRadio.channel( 'fields' ).request( 'get:field', key ); + var options = fieldModel.get( 'options' ); + var template = Backbone.Radio.channel( 'app' ).request( 'get:template', '#tmpl-nf-cl-value-list' ); + return template( { options: options, value: value } ); + }, + + getListCountryValue: function( key, trigger, value ) { + var fieldModel = nfRadio.channel( 'fields' ).request( 'get:field', key ); + var options = fieldModel.get( 'options' ); + var template = Backbone.Radio.channel( 'app' ).request( 'get:template', '#tmpl-nf-cl-value-list' ); + + options.reset(); + _.each( nfListCountries, function( value, label ) { + options.add( { label: label, value: value } ); + }); + + return template( { options: options, value: value } ); + }, + + getDateValue: function( key, trigger, value ) { + let fieldModel = nfRadio.channel( 'fields' ).request( 'get:field', key ); + let dateMode = fieldModel.get( 'date_mode' ); + + if ( 'undefined' == typeof dateMode ) { + dateMode = 'date_only'; + } + + let timestamp = value * 1000; + let dateObject = new Date( timestamp ); + dateObject = new Date( dateObject.getUTCFullYear(), dateObject.getUTCMonth(), dateObject.getUTCDate(), dateObject.getUTCHours(), dateObject.getUTCMinutes() ); + + let selectedHour = dateObject.getHours(); + let selectedMinute = dateObject.getMinutes(); + + let hourSelect = ''; + + let minuteSelect = ''; + + let date = moment( dateObject.toUTCString() ).format( 'YYYY-MM-DD' ); + if ( '1970-01-01' == date ) { + date = ''; + } + + let template = Backbone.Radio.channel( 'app' ).request( 'get:template', '#tmpl-nf-cl-value-date-' + dateMode ); + return template( { value: value, date: date, hourSelect: hourSelect, minuteSelect: minuteSelect } ); + }, + + + }); + + return controller; +} ); + +/** + * Returns an object with each comparator we'd like to use. + * This covers all the core field types. + * + * Add-ons can copy this code structure in order to get custom "comparators" for conditions. + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( 'controllers/coreComparators',[], function() { + var controller = Marionette.Object.extend( { + initialize: function() { + nfRadio.channel( 'conditions-checkbox' ).reply( 'get:comparators', this.getCheckboxComparators ); + nfRadio.channel( 'conditions-listradio' ).reply( 'get:comparators', this.getListSingleComparators ); + nfRadio.channel( 'conditions-listselect' ).reply( 'get:comparators', this.getListSingleComparators ); + nfRadio.channel( 'conditions-list' ).reply( 'get:comparators', this.getListComparators ); + nfRadio.channel( 'conditions-date' ).reply( 'get:comparators', this.getDateComparators ); + }, + + getCheckboxComparators: function( defaultComparators ) { + return { + is: { + label: nfcli18n.coreComparatorsIs, + value: 'equal' + }, + + isnot: { + label: nfcli18n.coreComparatorsIsNot, + value: 'notequal' + } + } + }, + + getListComparators: function( defaultComparators ) { + return { + has: { + label: nfcli18n.coreComparatorsHasSelected, + value: 'contains' + }, + + hasnot: { + label: nfcli18n.coreComparatorsDoesNotHaveSelected, + value: 'notcontains' + } + } + }, + + getListSingleComparators: function( defaultComparators, currentComparator ) { + /* + * Radio and Select lists need to use equal and notequal. + * In previous versions, however, they used contains and notcontains. + * In order to keep forms working that were made in those previous versions, + * we check to see if the currentComparator is contains or notcontains. + * If it is, we return those values; else we return equal or not equal. + */ + if ( 'contains' == currentComparator || 'notcontains' == currentComparator ) { + return { + has: { + label: nfcli18n.coreComparatorsHasSelected, + value: 'contains' + }, + + hasnot: { + label: nfcli18n.coreComparatorsDoesNotHaveSelected, + value: 'notcontains' + } + } + } + + return { + has: { + label: nfcli18n.coreComparatorsHasSelected, + value: 'equal' + }, + + hasnot: { + label: nfcli18n.coreComparatorsDoesNotHaveSelected, + value: 'notequal' + } + } + }, + + getDateComparators: function( defaultComparators ) { + return { + before: { + label: nfcli18n.coreComparatorsBefore, + value: 'less' + }, + + onorbefore: { + label: nfcli18n.coreComparatorsOnOrBefore, + value: 'lessequal' + }, + + equal: { + label: nfcli18n.coreComparatorsIs, + value: 'equal' + }, + + onorafter: { + label: nfcli18n.coreComparatorsOnOrAfter, + value: 'greaterequal' + }, + + after: { + label: nfcli18n.coreComparatorsAfter, + value: 'greater' + } + } + }, + + }); + + return controller; +} ); + +/** + * Returns an object with each trigger we'd like to use. + * This covers all the core field types. + * + * Add-ons can copy this code structure in order to get custom "triggers" for conditions. + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( 'controllers/coreTriggers',[], function() { + var controller = Marionette.Object.extend( { + initialize: function() { + nfRadio.channel( 'conditions-list' ).reply( 'get:triggers', this.getListTriggers ); + nfRadio.channel( 'conditions-submit' ).reply( 'get:triggers', this.getSubmitTriggers ); + nfRadio.channel( 'conditions-html' ).reply( 'get:triggers', this.getHTMLTriggers ); + nfRadio.channel( 'conditions-hr' ).reply( 'get:triggers', this.getDividerTriggers ); + nfRadio.channel( 'conditions-hidden' ).reply( 'get:triggers', this.getHiddenTriggers ); + }, + + getListTriggers: function( defaultTriggers ) { + var triggers = _.extend( defaultTriggers, { + select_option: { + label: nfcli18n.coreTriggersSelectOption, + value: 'select_option' + }, + + deselect_option: { + label: nfcli18n.coreTriggersDeselectOption, + value: 'deselect_option' + }, + + show_option: { + label: nfcli18n.coreTriggersShowOption, + value: 'show_option' + }, + + hide_option: { + label: nfcli18n.coreTriggersHideOption, + value: 'hide_option' + } + } ); + + var triggers = _.omit( defaultTriggers, 'change_value' ); + + return triggers; + }, + + getSubmitTriggers: function( defaultTriggers ) { + return _.omit( defaultTriggers, ['change_value', 'set_required', 'unset_required'] ); + }, + + getHTMLTriggers: function( defaultTriggers ) { + return _.omit( defaultTriggers, ['set_required', 'unset_required'] ); + }, + + getDividerTriggers: function( defaultTriggers ) { + return _.omit( defaultTriggers, ['change_value', 'set_required', 'unset_required'] ); + }, + + getHiddenTriggers: function( defaultTriggers ) { + return _.omit( defaultTriggers, ['set_required', 'unset_required'] ); + } + + }); + + return controller; +} ); +/** + * Returns the view to use in the drawer header. + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( 'controllers/getDrawerHeader',[ 'views/drawerHeader' ], function( DrawerHeaderView ) { + var controller = Marionette.Object.extend( { + initialize: function() { + nfRadio.channel( 'conditional_logic' ).reply( 'get:drawerHeaderView', this.getDrawerHeaderView, this ); + }, + + getDrawerHeaderView: function() { + return DrawerHeaderView; + } + }); + + return controller; +} ); + +/** + * Tracks key changes and updates when/then/else models + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( 'controllers/trackKeyChanges',[], function() { + var controller = Marionette.Object.extend( { + initialize: function() { + this.listenTo( nfRadio.channel( 'conditions' ), 'init:whenModel', this.registerKeyChangeTracker ); + this.listenTo( nfRadio.channel( 'conditions' ), 'init:thenModel', this.registerKeyChangeTracker ); + this.listenTo( nfRadio.channel( 'conditions' ), 'init:elseModel', this.registerKeyChangeTracker ); + }, + + registerKeyChangeTracker: function( itemModel ) { + // Update selected field if the selected field's key changes. + itemModel.listenTo( nfRadio.channel( 'app' ), 'replace:fieldKey', this.updateKey, itemModel ); + }, + + updateKey: function( fieldModel, keyModel, settingModel ) { + var oldKey = keyModel._previousAttributes[ 'key' ]; + var newKey = keyModel.get( 'key' ); + + if( this.get( 'key' ) == oldKey ) { + this.set( 'key', newKey ); + } + } + }); + + return controller; +} ); +/** + * When we init our action model, check to see if we have a 'conditions' setting that needs to be converted into a collection. + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( 'controllers/maybeConvertConditions',[ 'models/conditionModel' ], function( ConditionModel ) { + var controller = Marionette.Object.extend( { + initialize: function() { + this.listenTo( nfRadio.channel( 'actions' ), 'init:actionModel', this.maybeConvertConditions ); + }, + + maybeConvertConditions: function( actionModel ) { + var conditions = actionModel.get( 'conditions' ); + if ( ! conditions ) { + actionModel.set( 'conditions', new ConditionModel() ); + } else if ( false === conditions instanceof Backbone.Model ) { + actionModel.set( 'conditions', new ConditionModel( conditions ) ); + } + } + + }); + + return controller; +} ); + +/** + * Register filters for our when/then key groups/settings. + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( 'controllers/filters',[], function() { + var controller = Marionette.Object.extend( { + filters: [], + + initialize: function() { + nfRadio.channel( 'conditions' ).reply( 'add:groupFilter', this.addFilter, this ); + nfRadio.channel( 'conditions' ).reply( 'get:groupFilters', this.getFilters, this ); + }, + + addFilter: function( callback ) { + this.filters.push( callback ); + }, + + getFilters: function() { + return this.filters; + } + + }); + + return controller; +} ); + +/** + * Listens for changes in the "extra" settings in "when" settings. + * We use this for the date field to update the "value" to a timestamp when we change a date value setting. + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( 'controllers/fieldDate',[], function() { + var controller = Marionette.Object.extend( { + initialize: function() { + this.listenTo( nfRadio.channel( 'conditions' ), 'change:extra', this.maybeUpdateSetting ); + }, + + maybeUpdateSetting: function( e, dataModel ) { + let dateString = ''; + // Get our date + let date = jQuery( e.target ).parent().parent().find( "[data-type='date']" ).val(); + if ( 'undefined' == typeof date ) { + date = '1970-01-02'; + } + dateString += date + 'T'; + + // Get our hour + let hour = jQuery( e.target ).parent().parent().find( "[data-type='hour']" ).val(); + if ( 'undefined' == typeof hour ) { + hour = '00'; + } + dateString += hour + ':'; + + // Get our minute + let minute = jQuery( e.target ).parent().parent().find( "[data-type='minute']" ).val(); + if ( 'undefined' == typeof minute ) { + minute = '00'; + } + dateString += minute + 'Z'; + + // Build a timestamp + let dateObject = new Date( dateString ); + let timestamp = Math.floor( dateObject.getTime() / 1000 ); + + // Update our value with the timestamp + dataModel.set( 'value', timestamp ); + nfRadio.channel( 'app' ).request( 'update:setting', 'clean', false ); + }, + + }); + + return controller; +} ); +/** + * Loads all of our custom controllers. + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( 'controllers/loadControllers',[ + 'controllers/templateHelpers', + 'controllers/returnChildView', + 'models/conditionCollection', + 'views/drawerHeader', + 'controllers/newCondition', + 'controllers/updateSettings', + 'controllers/clickControls', + 'controllers/undo', + // 'controllers/maybeModifyElse', + 'controllers/coreValues', + 'controllers/coreComparators', + 'controllers/coreTriggers', + 'controllers/getDrawerHeader', + 'controllers/trackKeyChanges', + 'controllers/maybeConvertConditions', + 'controllers/filters', + 'controllers/fieldDate' + + ], function( + + TemplateHelpers, + ReturnChildView, + ConditionCollection, + DrawerHeaderView, + NewCondition, + UpdateSettings, + ClickControls, + Undo, + // MaybeModifyElse, + CoreValues, + CoreComparators, + CoreTriggers, + GetDrawerHeader, + TrackKeyChanges, + MaybeConvertConditions, + Filters, + FieldDate + ) { + var controller = Marionette.Object.extend( { + initialize: function() { + new TemplateHelpers(); + new ReturnChildView(); + new NewCondition(); + new UpdateSettings(); + new ClickControls(); + new Undo(); + // new MaybeModifyElse(); + new CoreValues(); + new CoreComparators(); + new CoreTriggers(); + new GetDrawerHeader(); + new TrackKeyChanges(); + new MaybeConvertConditions(); + new Filters(); + new FieldDate(); + } + }); + + return controller; +} ); + +var nfRadio = Backbone.Radio; + +require( [ 'controllers/loadControllers', 'models/conditionCollection' ], function( LoadControllers, ConditionCollection ) { + + var NFConditionalLogic = Marionette.Application.extend( { + + initialize: function( options ) { + this.listenTo( nfRadio.channel( 'app' ), 'after:appStart', this.afterNFLoad ); + }, + + onStart: function() { + new LoadControllers(); + }, + + afterNFLoad: function( app ) { + /* + * Convert our form's "condition" setting into a collection. + */ + var conditions = nfRadio.channel( 'settings' ).request( 'get:setting', 'conditions' ); + + if ( false === conditions instanceof Backbone.Collection ) { + conditions = new ConditionCollection( conditions ); + nfRadio.channel( 'settings' ).request( 'update:setting', 'conditions', conditions, true ); + } + } + } ); + + var nfConditionalLogic = new NFConditionalLogic(); + nfConditionalLogic.start(); +} ); +define("main", function(){}); + +}()); +//# sourceMappingURL=builder.js.map diff --git a/assets/js/min/builder.js.map b/assets/js/min/builder.js.map new file mode 100644 index 0000000..d8385b2 --- /dev/null +++ b/assets/js/min/builder.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["../../../config-wrap-start-default.js","../lib/almond.js","controllers/templateHelpers.js","views/advanced/whenItem.js","views/advanced/firstWhenItem.js","views/advanced/whenCollection.js","views/advanced/thenItem.js","views/advanced/thenCollection.js","views/advanced/elseItem.js","views/advanced/elseCollection.js","views/advanced/conditionItem.js","views/advanced/conditionCollection.js","views/actions/whenItem.js","views/actions/whenCollection.js","views/actions/conditionLayout.js","controllers/returnChildView.js","models/whenModel.js","models/whenCollection.js","models/thenModel.js","models/thenCollection.js","models/elseModel.js","models/elseCollection.js","models/conditionModel.js","models/conditionCollection.js","views/drawerHeader.js","controllers/newCondition.js","controllers/updateSettings.js","controllers/clickControls.js","controllers/undo.js","controllers/coreValues.js","controllers/coreComparators.js","controllers/coreTriggers.js","controllers/getDrawerHeader.js","controllers/trackKeyChanges.js","controllers/maybeConvertConditions.js","controllers/filters.js","controllers/fieldDate.js","controllers/loadControllers.js","main.js","../../../config-wrap-end-default.js"],"names":[],"mappings":"AAAA;AACA,ACDA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,ACjbA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,ACnUA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,ACpCA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,ACpCA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,AClDA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,AChCA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,AClBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,AC/BA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,AClBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,ACpEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,AC1CA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,AC/BA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,AC1CA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,AChDA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,AC3BA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,ACzBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,ACjBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,ACxBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,ACjBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,ACxBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,ACjBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,AC7BA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,ACjBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,ACtBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,ACzCA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,AC3DA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,AC1MA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,ACvIA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,ACtHA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,ACrHA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,ACpEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,ACrBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,AChCA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,AC3BA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,AC7BA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,AClDA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,ACrEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,AChCA","file":"builder.js","sourcesContent":["(function () {\n","/**\n * @license almond 0.3.1 Copyright (c) 2011-2014, The Dojo Foundation All Rights Reserved.\n * Available via the MIT or new BSD license.\n * see: http://github.com/jrburke/almond for details\n */\n//Going sloppy to avoid 'use strict' string cost, but strict practices should\n//be followed.\n/*jslint sloppy: true */\n/*global setTimeout: false */\n\nvar requirejs, require, define;\n(function (undef) {\n var main, req, makeMap, handlers,\n defined = {},\n waiting = {},\n config = {},\n defining = {},\n hasOwn = Object.prototype.hasOwnProperty,\n aps = [].slice,\n jsSuffixRegExp = /\\.js$/;\n\n function hasProp(obj, prop) {\n return hasOwn.call(obj, prop);\n }\n\n /**\n * Given a relative module name, like ./something, normalize it to\n * a real name that can be mapped to a path.\n * @param {String} name the relative name\n * @param {String} baseName a real name that the name arg is relative\n * to.\n * @returns {String} normalized name\n */\n function normalize(name, baseName) {\n var nameParts, nameSegment, mapValue, foundMap, lastIndex,\n foundI, foundStarMap, starI, i, j, part,\n baseParts = baseName && baseName.split(\"/\"),\n map = config.map,\n starMap = (map && map['*']) || {};\n\n //Adjust any relative paths.\n if (name && name.charAt(0) === \".\") {\n //If have a base name, try to normalize against it,\n //otherwise, assume it is a top-level require that will\n //be relative to baseUrl in the end.\n if (baseName) {\n name = name.split('/');\n lastIndex = name.length - 1;\n\n // Node .js allowance:\n if (config.nodeIdCompat && jsSuffixRegExp.test(name[lastIndex])) {\n name[lastIndex] = name[lastIndex].replace(jsSuffixRegExp, '');\n }\n\n //Lop off the last part of baseParts, so that . matches the\n //\"directory\" and not name of the baseName's module. For instance,\n //baseName of \"one/two/three\", maps to \"one/two/three.js\", but we\n //want the directory, \"one/two\" for this normalization.\n name = baseParts.slice(0, baseParts.length - 1).concat(name);\n\n //start trimDots\n for (i = 0; i < name.length; i += 1) {\n part = name[i];\n if (part === \".\") {\n name.splice(i, 1);\n i -= 1;\n } else if (part === \"..\") {\n if (i === 1 && (name[2] === '..' || name[0] === '..')) {\n //End of the line. Keep at least one non-dot\n //path segment at the front so it can be mapped\n //correctly to disk. Otherwise, there is likely\n //no path mapping for a path starting with '..'.\n //This can still fail, but catches the most reasonable\n //uses of ..\n break;\n } else if (i > 0) {\n name.splice(i - 1, 2);\n i -= 2;\n }\n }\n }\n //end trimDots\n\n name = name.join(\"/\");\n } else if (name.indexOf('./') === 0) {\n // No baseName, so this is ID is resolved relative\n // to baseUrl, pull off the leading dot.\n name = name.substring(2);\n }\n }\n\n //Apply map config if available.\n if ((baseParts || starMap) && map) {\n nameParts = name.split('/');\n\n for (i = nameParts.length; i > 0; i -= 1) {\n nameSegment = nameParts.slice(0, i).join(\"/\");\n\n if (baseParts) {\n //Find the longest baseName segment match in the config.\n //So, do joins on the biggest to smallest lengths of baseParts.\n for (j = baseParts.length; j > 0; j -= 1) {\n mapValue = map[baseParts.slice(0, j).join('/')];\n\n //baseName segment has config, find if it has one for\n //this name.\n if (mapValue) {\n mapValue = mapValue[nameSegment];\n if (mapValue) {\n //Match, update name to the new value.\n foundMap = mapValue;\n foundI = i;\n break;\n }\n }\n }\n }\n\n if (foundMap) {\n break;\n }\n\n //Check for a star map match, but just hold on to it,\n //if there is a shorter segment match later in a matching\n //config, then favor over this star map.\n if (!foundStarMap && starMap && starMap[nameSegment]) {\n foundStarMap = starMap[nameSegment];\n starI = i;\n }\n }\n\n if (!foundMap && foundStarMap) {\n foundMap = foundStarMap;\n foundI = starI;\n }\n\n if (foundMap) {\n nameParts.splice(0, foundI, foundMap);\n name = nameParts.join('/');\n }\n }\n\n return name;\n }\n\n function makeRequire(relName, forceSync) {\n return function () {\n //A version of a require function that passes a moduleName\n //value for items that may need to\n //look up paths relative to the moduleName\n var args = aps.call(arguments, 0);\n\n //If first arg is not require('string'), and there is only\n //one arg, it is the array form without a callback. Insert\n //a null so that the following concat is correct.\n if (typeof args[0] !== 'string' && args.length === 1) {\n args.push(null);\n }\n return req.apply(undef, args.concat([relName, forceSync]));\n };\n }\n\n function makeNormalize(relName) {\n return function (name) {\n return normalize(name, relName);\n };\n }\n\n function makeLoad(depName) {\n return function (value) {\n defined[depName] = value;\n };\n }\n\n function callDep(name) {\n if (hasProp(waiting, name)) {\n var args = waiting[name];\n delete waiting[name];\n defining[name] = true;\n main.apply(undef, args);\n }\n\n if (!hasProp(defined, name) && !hasProp(defining, name)) {\n throw new Error('No ' + name);\n }\n return defined[name];\n }\n\n //Turns a plugin!resource to [plugin, resource]\n //with the plugin being undefined if the name\n //did not have a plugin prefix.\n function splitPrefix(name) {\n var prefix,\n index = name ? name.indexOf('!') : -1;\n if (index > -1) {\n prefix = name.substring(0, index);\n name = name.substring(index + 1, name.length);\n }\n return [prefix, name];\n }\n\n /**\n * Makes a name map, normalizing the name, and using a plugin\n * for normalization if necessary. Grabs a ref to plugin\n * too, as an optimization.\n */\n makeMap = function (name, relName) {\n var plugin,\n parts = splitPrefix(name),\n prefix = parts[0];\n\n name = parts[1];\n\n if (prefix) {\n prefix = normalize(prefix, relName);\n plugin = callDep(prefix);\n }\n\n //Normalize according\n if (prefix) {\n if (plugin && plugin.normalize) {\n name = plugin.normalize(name, makeNormalize(relName));\n } else {\n name = normalize(name, relName);\n }\n } else {\n name = normalize(name, relName);\n parts = splitPrefix(name);\n prefix = parts[0];\n name = parts[1];\n if (prefix) {\n plugin = callDep(prefix);\n }\n }\n\n //Using ridiculous property names for space reasons\n return {\n f: prefix ? prefix + '!' + name : name, //fullName\n n: name,\n pr: prefix,\n p: plugin\n };\n };\n\n function makeConfig(name) {\n return function () {\n return (config && config.config && config.config[name]) || {};\n };\n }\n\n handlers = {\n require: function (name) {\n return makeRequire(name);\n },\n exports: function (name) {\n var e = defined[name];\n if (typeof e !== 'undefined') {\n return e;\n } else {\n return (defined[name] = {});\n }\n },\n module: function (name) {\n return {\n id: name,\n uri: '',\n exports: defined[name],\n config: makeConfig(name)\n };\n }\n };\n\n main = function (name, deps, callback, relName) {\n var cjsModule, depName, ret, map, i,\n args = [],\n callbackType = typeof callback,\n usingExports;\n\n //Use name if no relName\n relName = relName || name;\n\n //Call the callback to define the module, if necessary.\n if (callbackType === 'undefined' || callbackType === 'function') {\n //Pull out the defined dependencies and pass the ordered\n //values to the callback.\n //Default to [require, exports, module] if no deps\n deps = !deps.length && callback.length ? ['require', 'exports', 'module'] : deps;\n for (i = 0; i < deps.length; i += 1) {\n map = makeMap(deps[i], relName);\n depName = map.f;\n\n //Fast path CommonJS standard dependencies.\n if (depName === \"require\") {\n args[i] = handlers.require(name);\n } else if (depName === \"exports\") {\n //CommonJS module spec 1.1\n args[i] = handlers.exports(name);\n usingExports = true;\n } else if (depName === \"module\") {\n //CommonJS module spec 1.1\n cjsModule = args[i] = handlers.module(name);\n } else if (hasProp(defined, depName) ||\n hasProp(waiting, depName) ||\n hasProp(defining, depName)) {\n args[i] = callDep(depName);\n } else if (map.p) {\n map.p.load(map.n, makeRequire(relName, true), makeLoad(depName), {});\n args[i] = defined[depName];\n } else {\n throw new Error(name + ' missing ' + depName);\n }\n }\n\n ret = callback ? callback.apply(defined[name], args) : undefined;\n\n if (name) {\n //If setting exports via \"module\" is in play,\n //favor that over return value and exports. After that,\n //favor a non-undefined return value over exports use.\n if (cjsModule && cjsModule.exports !== undef &&\n cjsModule.exports !== defined[name]) {\n defined[name] = cjsModule.exports;\n } else if (ret !== undef || !usingExports) {\n //Use the return value from the function.\n defined[name] = ret;\n }\n }\n } else if (name) {\n //May just be an object definition for the module. Only\n //worry about defining if have a module name.\n defined[name] = callback;\n }\n };\n\n requirejs = require = req = function (deps, callback, relName, forceSync, alt) {\n if (typeof deps === \"string\") {\n if (handlers[deps]) {\n //callback in this case is really relName\n return handlers[deps](callback);\n }\n //Just return the module wanted. In this scenario, the\n //deps arg is the module name, and second arg (if passed)\n //is just the relName.\n //Normalize module name, if it contains . or ..\n return callDep(makeMap(deps, callback).f);\n } else if (!deps.splice) {\n //deps is a config object, not an array.\n config = deps;\n if (config.deps) {\n req(config.deps, config.callback);\n }\n if (!callback) {\n return;\n }\n\n if (callback.splice) {\n //callback is an array, which means it is a dependency list.\n //Adjust args if there are dependencies\n deps = callback;\n callback = relName;\n relName = null;\n } else {\n deps = undef;\n }\n }\n\n //Support require(['a'])\n callback = callback || function () {};\n\n //If relName is a function, it is an errback handler,\n //so remove it.\n if (typeof relName === 'function') {\n relName = forceSync;\n forceSync = alt;\n }\n\n //Simulate async callback;\n if (forceSync) {\n main(undef, deps, callback, relName);\n } else {\n //Using a non-zero value because of concern for what old browsers\n //do, and latest browsers \"upgrade\" to 4 if lower value is used:\n //http://www.whatwg.org/specs/web-apps/current-work/multipage/timers.html#dom-windowtimers-settimeout:\n //If want a value immediately, use require('id') instead -- something\n //that works in almond on the global level, but not guaranteed and\n //unlikely to work in other AMD implementations.\n setTimeout(function () {\n main(undef, deps, callback, relName);\n }, 4);\n }\n\n return req;\n };\n\n /**\n * Just drops the config on the floor, but returns req in case\n * the config return value is used.\n */\n req.config = function (cfg) {\n return req(cfg);\n };\n\n /**\n * Expose module registry for debugging and tooling\n */\n requirejs._defined = defined;\n\n define = function (name, deps, callback) {\n if (typeof name !== 'string') {\n throw new Error('See almond README: incorrect module build, no module name');\n }\n\n //This module may not have dependencies\n if (!deps.splice) {\n //deps is not an array, so probably means\n //an object literal or factory function for\n //the value. Adjust args.\n callback = deps;\n deps = [];\n }\n\n if (!hasProp(defined, name) && !hasProp(waiting, name)) {\n waiting[name] = [name, deps, callback];\n }\n };\n\n define.amd = {\n jQuery: true\n };\n}());\n\ndefine(\"../lib/almond\", function(){});\n\n","/**\n * Adds template helpers for the fields conditional logic setting type\n *\n * @package Ninja Forms Conditional Logic\n * @copyright (c) 2016 WP Ninjas\n * @since 3.0\n */\ndefine( 'controllers/templateHelpers',[], function() {\n\tvar controller = Marionette.Object.extend( {\n\t\tinitialize: function() {\n\t\t\tthis.listenTo( nfRadio.channel( 'conditions' ), 'init:model', this.addTemplateHelpers );\n\t\t\tthis.listenTo( nfRadio.channel( 'conditions' ), 'init:thenModel', this.addTemplateHelpers );\n\t\t\tthis.listenTo( nfRadio.channel( 'conditions' ), 'init:whenModel', this.addTemplateHelpers );\n\t\t\tthis.listenTo( nfRadio.channel( 'conditions' ), 'init:elseModel', this.addTemplateHelpers );\n\t\t\t\n\t\t},\n\n\t\taddTemplateHelpers: function( model ) {\n\t\t\tmodel.set( 'renderKeySelect', this.renderKeySelect );\n\t\t\tmodel.set( 'renderComparators', this.renderComparators );\n\t\t\tmodel.set( 'renderTriggers', this.renderTriggers );\n\t\t\tmodel.set( 'renderWhenValue', this.renderWhenValue );\n\t\t\tmodel.set( 'renderItemValue', this.renderItemValue );\n\t\t},\n\n\t\trenderKeySelect: function( currentValue, modelType ) {\n\t\t\t\n\t\t\tvar groups = []\n\n\t\t\tvar fieldCollection = nfRadio.channel( 'fields' ).request( 'get:collection' );\n\t\t\tvar fieldOptions = _.chain( fieldCollection.models )\n\t\t\t\t.filter( function( field ) { return ! nfRadio.channel( 'conditions-key-select-field-' + field.get( 'type' ) ).request( 'hide', modelType ) || false; })\n\t\t\t\t.filter( function( field ) {\n\n\t\t\t\t\t// filter out these fields for the when condition\n\t\t\t\t\tvar notForWhen = [ 'submit', 'hr', 'html', 'save', 'file-upload', 'password', 'passwordconfirm', 'product' ];\n\t\t\t\t\t\n\t\t\t\t\tif( field.get( 'key' ) === currentValue ) {\n\t\t\t\t\t\tnotForWhen = notForWhen.splice( notForWhen.indexOf( field.get( 'type' ), 1) );\n\t\t\t\t\t}\n\n\t\t\t\t\tif( notForWhen.includes( field.get( 'type' ) ) && 'when' === modelType ) {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\n\t\t\t\t\treturn true;\n\t\t\t\t})\n\t\t\t\t.map( function( field ) {\n var label = field.get( 'label' )\n\t\t\t\t\tif( 'undefined' !== typeof field.get( 'admin_label' ) && 0 < field.get( 'admin_label' ).length ){\n \tlabel = field.get( 'admin_label' );\n\t\t\t\t\t}\n\t\t\t\t\treturn { key: field.get( 'key' ), label: label }; }\n\t\t\t\t)\n\t\t\t\t.sortBy( function( field ){\n\t\t\t\t\treturn field.label.toLowerCase();\n\t\t\t\t} )\n\t\t\t\t.value();\n\t\t\t\t\n\t\t\tgroups.push( { label: 'Fields', type: 'field', options: fieldOptions } );\n\t\t\t\n\t\t\tvar calcCollection = nfRadio.channel( 'settings' ).request( 'get:setting', 'calculations' );\n\n\t\t\t/*\n\t\t\t * If we are working on a 'when' model and we have calculations, add them to our select options.\n\t\t\t */\n\t\t\tif ( 'when' == modelType && 0 < calcCollection.length ) {\n\t\t\t\tvar calcOptions = calcCollection.map( function( calc ) {\n\t\t\t\t\treturn { key: calc.get( 'name' ), label: calc.get( 'name' ) };\n\t\t\t\t} );\n\n\t\t\t\tgroups.push( { label: 'Calculations', type: 'calc', options: calcOptions } );\n\t\t\t}\n\n\t\t\t/*\n\t\t\t * Pass our groups through any 'when/then' group filters we have.\n\t\t\t */\n\t\t\tvar filters = nfRadio.channel( 'conditions' ).request( 'get:groupFilters' );\n\t\t\t_.each( filters, function( filter ) {\n\t\t\t\tgroups = filter( groups, modelType );\n\t\t\t} );\n\n\t\t\t/*\n\t\t\t * Use a template to get our field select\n\t\t\t */\n\t\t\tvar template = Backbone.Radio.channel( 'app' ).request( 'get:template', '#tmpl-nf-cl-key-select' );\n\n\t\t\tvar tmp = template( { groups: groups, currentValue: currentValue } );\n\t\t\treturn tmp;\n\t\t},\n\n\t\trenderComparators: function( type, key, currentComparator ) {\n\t\t\tvar defaultComparators = {\n\t\t\t\tequal: {\n\t\t\t\t\tlabel: nfcli18n.templateHelperEquals,\n\t\t\t\t\tvalue: 'equal'\n\t\t\t\t},\n\n\t\t\t\tnotequal: {\n\t\t\t\t\tlabel: nfcli18n.templateHelperDoesNotEqual,\n\t\t\t\t\tvalue: 'notequal'\n\t\t\t\t},\n\n\t\t\t\tcontains: {\n\t\t\t\t\tlabel: nfcli18n.templateHelperContains,\n\t\t\t\t\tvalue: 'contains'\n\t\t\t\t},\n\n\t\t\t\tnotcontains: {\n\t\t\t\t\tlabel: nfcli18n.templateHelperDoesNotContain,\n\t\t\t\t\tvalue: 'notcontains'\n\t\t\t\t},\n\n\t\t\t\tgreater: {\n\t\t\t\t\tlabel: nfcli18n.templateHelperGreaterThan,\n\t\t\t\t\tvalue: 'greater'\n\t\t\t\t},\n\n\t\t\t\tless: {\n\t\t\t\t\tlabel: nfcli18n.templateHelperLessThan,\n\t\t\t\t\tvalue: 'less'\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tif ( key ) {\n\t\t\t\t/*\n\t\t\t\t * This could be a field or a calculation key. If it's a calc key, get the calc model.\n\t\t\t\t */\n\t\t\t\tif ( 'calc' == type ) {\n\t\t\t\t\tvar comparators = _.omit( defaultComparators, 'contains', 'notcontains' );\n\t\t\t\t\t_.extend( comparators, {\n\t\t\t\t\t\tlessequal: {\n\t\t\t\t\t\t\tlabel: nfcli18n.templateHelperLessThanOrEqual,\n\t\t\t\t\t\t\tvalue: 'lessequal'\n\t\t\t\t\t\t},\n\n\t\t\t\t\t\tgreaterequal: {\n\t\t\t\t\t\t\tlabel: nfcli18n.templateHelperGreaterThanOrEqual,\n\t\t\t\t\t\t\tvalue: 'greaterequal'\n\t\t\t\t\t\t}\n\t\t\t\t\t} );\n\t\t\t\t} else {\n\t\t\t\t\t/*\n\t\t\t\t\t * Send out a radio request for an html value on a channel based upon the field type.\n\t\t\t\t\t *\n\t\t\t\t\t * Get our field by key\n\t\t\t\t\t * Get our field type model\n\t\t\t\t\t *\n\t\t\t\t\t * Send out a message on the type channel\n\t\t\t\t\t * If we don't get a response, send a message out on the parent type channel\n\t\t\t\t\t */\n\t\t\t\t\tvar fieldModel = nfRadio.channel( 'fields' ).request( 'get:field', key );\n\n\t\t\t\t\tif( fieldModel ) {\n\t\t\t\t\t\tvar comparators = nfRadio.channel('conditions-' + fieldModel.get('type')).request('get:comparators', defaultComparators, currentComparator );\n\t\t\t\t\t\tif (!comparators) {\n\t\t\t\t\t\t\tvar typeModel = nfRadio.channel('fields').request('get:type', fieldModel.get('type'));\n\t\t\t\t\t\t\tcomparators = nfRadio.channel('conditions-' + typeModel.get('parentType')).request('get:comparators', defaultComparators, currentComparator ) || defaultComparators;\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tvar comparators = defaultComparators;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tvar comparators = defaultComparators;\n\t\t\t}\n\n\t\t\t/*\n\t\t\t * Use a template to get our comparator select\n\t\t\t */\n\t\t\tvar template = Backbone.Radio.channel( 'app' ).request( 'get:template', '#tmpl-nf-cl-comparators' );\n\t\t\treturn template( { comparators: comparators, currentComparator: currentComparator } );\n\t\t},\n\n\t\trenderTriggers: function( type, key, currentTrigger, value ) {\n\t\t\tvar defaultTriggers = {\n\t\t\t\tshow_field: {\n\t\t\t\t\tlabel: nfcli18n.templateHelperShowField,\n\t\t\t\t\tvalue: 'show_field'\n\t\t\t\t},\n\n\t\t\t\thide_field: {\n\t\t\t\t\tlabel: nfcli18n.templateHelperHideField,\n\t\t\t\t\tvalue: 'hide_field'\n\t\t\t\t},\n\n\t\t\t\tchange_value: {\n\t\t\t\t\tlabel: nfcli18n.templateHelperChangeValue,\n\t\t\t\t\tvalue: 'change_value'\n\t\t\t\t},\n\n\t\t\t\tset_required: {\n\t\t\t\t\tlabel: nfcli18n.templateHelperSetRequired,\n\t\t\t\t\tvalue: 'set_required'\n\t\t\t\t},\n\n\t\t\t\tunset_required: {\n\t\t\t\t\tlabel: nfcli18n.templateHelperUnsetRequired,\n\t\t\t\t\tvalue: 'unset_required'\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tif ( key && 'field' == type ) {\n\t\t\t\t/*\n\t\t\t\t * Send out a radio request for an html value on a channel based upon the field type.\n\t\t\t\t *\n\t\t\t\t * Get our field by key\n\t\t\t\t * Get our field type model\n\t\t\t\t *\n\t\t\t\t * Send out a message on the type channel\n\t\t\t\t * If we don't get a response, send a message out on the parent type channel\n\t\t\t\t */\n\t\t\t\tvar fieldModel = nfRadio.channel( 'fields' ).request( 'get:field', key );\n\n\t\t\t\tif( 'undefined' != typeof fieldModel ) {\n\t\t\t\t\tvar typeModel = nfRadio.channel('fields').request('get:type', fieldModel.get('type'));\n\n\t\t\t\t\tvar triggers = nfRadio.channel('conditions-' + fieldModel.get('type')).request('get:triggers', defaultTriggers);\n\t\t\t\t\tif (!triggers) {\n\t\t\t\t\t\ttriggers = nfRadio.channel('conditions-' + typeModel.get('parentType')).request('get:triggers', defaultTriggers) || defaultTriggers;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tvar triggers = nfRadio.channel( 'conditions-' + type ).request( 'get:triggers', defaultTriggers ) || defaultTriggers;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tvar triggers = nfRadio.channel( 'conditions-' + type ).request( 'get:triggers', defaultTriggers ) || defaultTriggers;\n\t\t\t}\n\n\n\t\t\t/*\n\t\t\t * Use a template to get our comparator select\n\t\t\t */\n\t\t\tvar template = Backbone.Radio.channel( 'app' ).request( 'get:template', '#tmpl-nf-cl-triggers' );\n\t\t\treturn template( { triggers: triggers, currentTrigger: currentTrigger } );\n\t\t},\n\n\t\trenderWhenValue: function( type, key, comparator, value ) {\n\t\t\t/*\n\t\t\t * Use a template to get our value\n\t\t\t */\n\t\t\tvar template = Backbone.Radio.channel( 'app' ).request( 'get:template', '#tmpl-nf-cl-value-default' );\n\t\t\tvar defaultHTML = template( { value: value } );\n\n\t\t\t/*\n\t\t\t * If we have a key and it's not a calc, get our field type based HTML.\n\t\t\t */\n\t\t\tif ( key && 'calc' != type ) {\n\t\t\t\t/*\n\t\t\t\t * Send out a radio request for an html value on a channel based upon the field type.\n\t\t\t\t *\n\t\t\t\t * Get our field by key\n\t\t\t\t * Get our field type model\n\t\t\t\t *\n\t\t\t\t * Send out a message on the type channel\n\t\t\t\t * If we don't get a response, send a message out on the parent type channel\n\t\t\t\t */\n\t\t\t\tvar fieldModel = nfRadio.channel( 'fields' ).request( 'get:field', key );\n\n\t\t\t\tif( fieldModel ) {\n\t\t\t\t\tvar html = nfRadio.channel('conditions-' + fieldModel.get('type')).request('get:valueInput', key, comparator, value);\n\t\t\t\t\tif (!html) {\n\t\t\t\t\t\tvar typeModel = nfRadio.channel('fields').request('get:type', fieldModel.get('type'));\n\t\t\t\t\t\thtml = nfRadio.channel('conditions-' + typeModel.get('parentType')).request('get:valueInput', key, comparator, value) || defaultHTML;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\thtml = defaultHTML;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tvar html = defaultHTML;\n\t\t\t}\n\t\t\t\n\t\t\treturn html;\n\t\t},\n\n\t\trenderItemValue: function( key, trigger, value ) {\n\t\t\t/*\n\t\t\t * Use a template to get our value\n\t\t\t *\n\t\t\t * TODO: This should be much more dynamic.\n\t\t\t * At the moment, we manually check to see if we are doing a \"change_value\" or similar trigger.\n\t\t\t */\n\t\t\tif ( trigger != 'change_value'\n\t\t\t\t&& trigger != 'select_option'\n\t\t\t\t&& trigger != 'deselect_option'\n\t\t\t\t&& trigger != 'show_option'\n\t\t\t\t&& trigger != 'hide_option' \n\t\t\t) {\n\t\t\t\treturn '';\n\t\t\t}\n\n\t\t\tvar template = Backbone.Radio.channel( 'app' ).request( 'get:template', '#tmpl-nf-cl-value-default' );\n\t\t\tvar defaultHTML = template( { value: value } );\n\n\t\t\tif ( key ) {\n\t\t\t\t/*\n\t\t\t\t * Send out a radio request for an html value on a channel based upon the field type.\n\t\t\t\t *\n\t\t\t\t * Get our field by key\n\t\t\t\t * Get our field type model\n\t\t\t\t *\n\t\t\t\t * Send out a message on the type channel\n\t\t\t\t * If we don't get a response, send a message out on the parent type channel\n\t\t\t\t */\n\t\t\t\tvar fieldModel = nfRadio.channel( 'fields' ).request( 'get:field', key );\n\n\t\t\t\tif( 'undefined' != typeof fieldModel ) {\n\t\t\t\t\tvar typeModel = nfRadio.channel('fields').request('get:type', fieldModel.get('type'));\n\t\t\t\t\tvar html = nfRadio.channel('conditions-' + fieldModel.get('type')).request('get:valueInput', key, trigger, value);\n\t\t\t\t\tif (!html) {\n\t\t\t\t\t\thtml = nfRadio.channel('conditions-' + typeModel.get('parentType')).request('get:valueInput', key, trigger, value) || defaultHTML;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tvar html = defaultHTML;\n\t\t\t}\n\n\t\t\treturn html;\n\t\t}\n\t});\n\n\treturn controller;\n} );\n\n","/**\n * Item view for our condition and\n *\n * @package Ninja Forms Conditional Logic\n * @copyright (c) 2016 WP Ninjas\n * @since 3.0\n */\ndefine( 'views/advanced/whenItem',[], function( ) {\n\tvar view = Marionette.ItemView.extend({\n\t\ttemplate: \"#tmpl-nf-cl-advanced-when-item\",\n\n\t\tinitialize: function() {\n\t\t\tthis.listenTo( this.model, 'change', this.render );\n\t\t},\n\n\t\tonRender: function() {\n\t\t\tlet el = jQuery( this.el ).find( '[data-type=\"date\"]' );\n\t\t\tjQuery( el ).mask( '9999-99-99' );\n\t\t},\n\t\t\n\t\tevents: {\n\t\t\t'change .setting': 'changeSetting',\n\t\t\t'click .nf-remove-when': 'clickRemove'\n\t\t},\n\n\t\tchangeSetting: function( e ) {\n\t\t\tnfRadio.channel( 'conditions' ).trigger( 'change:setting', e, this.model )\n\t\t},\n\n\t\tclickRemove: function( e ) {\n\t\t\tnfRadio.channel( 'conditions' ).trigger( 'click:removeWhen', e, this.model );\n\t\t}\n\t});\n\n\treturn view;\n} );\n","/**\n * Item view for our condition's first when\n *\n * @package Ninja Forms Conditional Logic\n * @copyright (c) 2016 WP Ninjas\n * @since 3.0\n */\ndefine( 'views/advanced/firstWhenItem',[], function( ) {\n\tvar view = Marionette.ItemView.extend({\n\t\ttemplate: \"#tmpl-nf-cl-advanced-first-when-item\",\n\t\t\n\t\tinitialize: function() {\n\t\t\tthis.listenTo( this.model, 'change', this.render );\n\t\t},\n\n\t\tonRender: function() {\n\t\t\tlet el = jQuery( this.el ).find( '[data-type=\"date\"]' );\n\t\t\tjQuery( el ).mask( '9999-99-99' );\n\t\t},\n\n\t\tevents: {\n\t\t\t'change .setting': 'changeSetting',\n\t\t\t'change .extra': 'changeExtra',\n\t\t},\n\n\t\tchangeSetting: function( e ) {\n\t\t\tnfRadio.channel( 'conditions' ).trigger( 'change:setting', e, this.model );\n\t\t},\n\n\t\tchangeExtra: function( e ) {\n\t\t\tnfRadio.channel( 'conditions' ).trigger( 'change:extra', e, this.model );\n\t\t}\n\t});\n\n\treturn view;\n} );\n","/**\n * Collection view for our when collection\n *\n * @package Ninja Forms Conditional Logic\n * @copyright (c) 2016 WP Ninjas\n * @since 3.0\n */\ndefine( 'views/advanced/whenCollection',[ 'views/advanced/whenItem', 'views/advanced/firstWhenItem' ], function( WhenItem, FirstWhenItem ) {\n\tvar view = Marionette.CollectionView.extend({\n\t\tgetChildView: function( item ) {\n\t\t\tif ( item.collection.first() == item ) {\n\t\t\t\treturn FirstWhenItem;\n\t\t\t} else {\n\t\t\t\treturn WhenItem;\n\t\t\t}\n\t\t\t\n\t\t},\n\n\t\tinitialize: function( options ) {\n\t\t\tthis.firstWhenDiv = options.firstWhenDiv;\n\t\t\tthis.conditionModel = options.conditionModel;\n\t\t},\n\n \t// The default implementation:\n\t \tattachHtml: function( collectionView, childView, index ) {\n\t\t \tif ( 0 == index ) {\n\t\t \t\tthis.firstWhenDiv.append( childView.el );\n\t\t \t} else {\n\t\t \t\tif ( ! this.conditionModel.get( 'collapsed' ) ) {\n\t\t\t\t if (collectionView.isBuffering) {\n\t\t\t\t \t// buffering happens on reset events and initial renders\n\t\t\t\t \t// in order to reduce the number of inserts into the\n\t\t\t\t \t// document, which are expensive.\n\t\t\t\t \tcollectionView._bufferedChildren.splice(index, 0, childView);\n\t\t\t\t } else {\n\t\t\t\t\t\t// If we've already rendered the main collection, append\n\t\t\t\t\t\t// the new child into the correct order if we need to. Otherwise\n\t\t\t\t\t\t// append to the end.\n\t\t\t\t\t\tif (!collectionView._insertBefore(childView, index)){\n\t\t\t\t\t\t\tcollectionView._insertAfter(childView);\n\t\t\t\t\t\t}\n\t\t\t\t }\t\t\t \t\t\t\n\t\t \t\t}\n\t\t \t}\n\t \t},\n\n\t} );\n\n\treturn view;\n} );\n","/**\n * Item view for our condition then\n *\n * @package Ninja Forms Conditional Logic\n * @copyright (c) 2016 WP Ninjas\n * @since 3.0\n */\ndefine( 'views/advanced/thenItem',[], function( ) {\n\tvar view = Marionette.ItemView.extend({\n\t\ttemplate: \"#tmpl-nf-cl-trigger-item\",\n\n\t\tinitialize: function() {\n\t\t\tthis.listenTo( this.model, 'change', this.render );\n\t\t},\n\n\t\tevents: {\n\t\t\t'change .setting': 'changeSetting',\n\t\t\t'click .nf-remove-then': 'clickRemove'\n\t\t},\n\n\t\tchangeSetting: function( e ) {\n\t\t\tnfRadio.channel( 'conditions' ).trigger( 'change:setting', e, this.model );\n\t\t\tnfRadio.channel( 'conditions' ).trigger( 'change:then', e, this.model );\n\t\t},\n\n\t\tclickRemove: function( e ) {\n\t\t\tnfRadio.channel( 'conditions' ).trigger( 'click:removeThen', e, this.model );\n\t\t}\n\t});\n\n\treturn view;\n} );\n","/**\n * Collection view for our then statements\n *\n * @package Ninja Forms Conditional Logic\n * @copyright (c) 2016 WP Ninjas\n * @since 3.0\n */\ndefine( 'views/advanced/thenCollection',[ 'views/advanced/thenItem' ], function( ThenItem ) {\n\tvar view = Marionette.CollectionView.extend({\n\t\tchildView: ThenItem,\n\n\t\tinitialize: function( options ) {\n\n\t\t}\n\t});\n\n\treturn view;\n} );\n","/**\n * Item view for our condition then\n *\n * @package Ninja Forms Conditional Logic\n * @copyright (c) 2016 WP Ninjas\n * @since 3.0\n */\ndefine( 'views/advanced/elseItem',[], function( ) {\n\tvar view = Marionette.ItemView.extend({\n\t\ttemplate: \"#tmpl-nf-cl-trigger-item\",\n\n\t\tinitialize: function() {\n\t\t\tthis.listenTo( this.model, 'change', this.render );\n\t\t},\n\n\t\tevents: {\n\t\t\t'change .setting': 'changeSetting',\n\t\t\t'click .nf-remove-else': 'clickRemove'\n\t\t},\n\n\t\tchangeSetting: function( e ) {\n\t\t\tnfRadio.channel( 'conditions' ).trigger( 'change:setting', e, this.model )\n\t\t},\n\n\t\tclickRemove: function( e ) {\n\t\t\tnfRadio.channel( 'conditions' ).trigger( 'click:removeElse', e, this.model );\n\t\t}\n\t});\n\n\treturn view;\n} );\n","/**\n * Collection view for our else statements\n *\n * @package Ninja Forms Conditional Logic\n * @copyright (c) 2016 WP Ninjas\n * @since 3.0\n */\ndefine( 'views/advanced/elseCollection',[ 'views/advanced/elseItem' ], function( ElseItem ) {\n\tvar view = Marionette.CollectionView.extend({\n\t\tchildView: ElseItem,\n\n\t\tinitialize: function( options ) {\n\n\t\t}\n\t});\n\n\treturn view;\n} );\n","/**\n * Layout view for our conditions\n *\n * @package Ninja Forms Conditional Logic\n * @copyright (c) 2016 WP Ninjas\n * @since 3.0\n */\ndefine( 'views/advanced/conditionItem',[ 'views/advanced/whenCollection', 'views/advanced/thenCollection', 'views/advanced/elseCollection' ], function( WhenCollectionView, ThenCollectionView, ElseCollectionView ) {\n\tvar view = Marionette.LayoutView.extend({\n\t\ttemplate: \"#tmpl-nf-cl-advanced-condition\",\n\t\tregions: {\n\t\t\t'when': '.nf-when-region',\n\t\t\t'then': '.nf-then-region',\n\t\t\t'else': '.nf-else-region'\n\t\t},\n\n\t\tinitialize: function() {\n\t\t\t/*\n\t\t\t * When we change the \"collapsed\" attribute of our model, re-render.\n\t\t\t */\n\t\t\tthis.listenTo( this.model, 'change:collapsed', this.render );\n\n\t\t\t/*\n\t\t\t * When our drawer closes, send out a radio message on our setting type channel.\n\t\t\t */\n\t\t\tthis.listenTo( nfRadio.channel( 'drawer' ), 'closed', this.drawerClosed );\n\t\t},\n\n\t\tonRender: function() {\n\t\t\tvar firstWhenDiv = jQuery( this.el ).find( '.nf-first-when' );\n\t\t\tthis.when.show( new WhenCollectionView( { collection: this.model.get( 'when' ), firstWhenDiv: firstWhenDiv, conditionModel: this.model } ) );\n\t\t\tif ( ! this.model.get( 'collapsed' ) ) {\n\t\t\t\tthis.then.show( new ThenCollectionView( { collection: this.model.get( 'then' ) } ) );\n\t\t\t\tthis.else.show( new ElseCollectionView( { collection: this.model.get( 'else' ) } ) );\n\t\t\t}\n\t\t},\n\n\t\tevents: {\n\t\t\t'click .nf-remove-condition': 'clickRemove',\n\t\t\t'click .nf-collapse-condition': 'clickCollapse',\n\t\t\t'click .nf-add-when': 'clickAddWhen',\n\t\t\t'click .nf-add-then': 'clickAddThen',\n\t\t\t'click .nf-add-else': 'clickAddElse'\n\t\t},\n\n\t\tclickRemove: function( e ) {\n\t\t\tnfRadio.channel( 'conditions' ).trigger( 'click:removeCondition', e, this.model );\n\t\t},\n\n\t\tclickCollapse: function( e ) {\n\t\t\tnfRadio.channel( 'conditions' ).trigger( 'click:collapseCondition', e, this.model );\n\t\t},\n\n\t\tclickAddWhen: function( e ) {\n\t\t\tnfRadio.channel( 'conditions' ).trigger( 'click:addWhen', e, this.model );\n\t\t},\n\n\t\tclickAddThen: function( e ) {\n\t\t\tnfRadio.channel( 'conditions' ).trigger( 'click:addThen', e, this.model );\n\t\t},\n\n\t\tclickAddElse: function( e ) {\n\t\t\tnfRadio.channel( 'conditions' ).trigger( 'click:addElse', e, this.model );\n\t\t}\n\t});\n\n\treturn view;\n} );\n","/**\n * Collection view for our conditions\n *\n * @package Ninja Forms Conditional Logic\n * @copyright (c) 2016 WP Ninjas\n * @since 3.0\n */\ndefine( 'views/advanced/conditionCollection',[ 'views/advanced/conditionItem' ], function( conditionItem ) {\n\tvar view = Marionette.CollectionView.extend({\n\t\tchildView: conditionItem,\n\n\t\tinitialize: function( options ) {\n\t\t\tthis.collection = options.dataModel.get( 'conditions' );\n\t\t},\n\n onShow: function() {\n /*\n * If we don't have any conditions, add an empty one as we render.\n */\n if ( 0 == this.collection.length ) {\n this.collection.add( {} );\n }\n },\n\n onBeforeDestroy: function() {\n /*\n * If we don't have any conditions or we have more than one, just return.\n */\n if ( 0 == this.collection.length || 1 < this.collection.length ) return;\n /*\n * If we only have one condition, and we didn't change the \"key\" attribute, reset our collection.\n * This empties it.\n */\n if ( '' == this.collection.models[0].get( 'when' ).models[0].get( 'key' ) ) {\n this.collection.reset();\n }\n }\n\t});\n\n\treturn view;\n} );\n\n","/**\n * Item view for our condition and\n *\n * @package Ninja Forms Conditional Logic\n * @copyright (c) 2016 WP Ninjas\n * @since 3.0\n */\ndefine( 'views/actions/whenItem',[], function( ) {\n\tvar view = Marionette.ItemView.extend({\n\t\ttemplate: \"#tmpl-nf-cl-actions-condition-when\",\n\n\t\tinitialize: function() {\n\t\t\tthis.listenTo( this.model, 'change', this.render );\n\t\t},\n\t\t\n\t\tevents: {\n\t\t\t'change .setting': 'changeSetting',\n\t\t\t'click .nf-remove-when': 'clickRemove'\n\t\t},\n\n\t\tchangeSetting: function( e ) {\n\t\t\tnfRadio.channel( 'conditions' ).trigger( 'change:setting', e, this.model )\n\t\t},\n\n\t\tclickRemove: function( e ) {\n\t\t\tnfRadio.channel( 'conditions' ).trigger( 'click:removeWhen', e, this.model );\n\t\t}\n\t});\n\n\treturn view;\n} );\n","/**\n * Collection view for our when collection\n *\n * @package Ninja Forms Conditional Logic\n * @copyright (c) 2016 WP Ninjas\n * @since 3.0\n */\ndefine( 'views/actions/whenCollection',[ 'views/actions/whenItem' ], function( WhenItem ) {\n\tvar view = Marionette.CollectionView.extend({\n\t\tchildView: WhenItem,\n\n\t\tinitialize: function( options ) {\n\n\t\t},\n\n onShow: function() {\n /*\n * If we don't have any conditions, add an empty one as we render.\n */\n if ( 0 == this.collection.length ) {\n this.collection.add( {} );\n }\n },\n\n onBeforeDestroy: function() {\n /*\n * If we don't have any conditions or we have more than one, just return.\n */\n if ( 0 == this.collection.length || 1 < this.collection.length ) return;\n /*\n * If we only have one condition, and we didn't change the \"key\" attribute, reset our collection.\n * This empties it.\n */\n if ( '' == this.collection.models[0].get( 'key' ) ) {\n this.collection.reset();\n }\n }\n\n\t} );\n\n\treturn view;\n} );\n","/**\n * Layout view for our Action condition\n *\n * @package Ninja Forms Conditional Logic\n * @copyright (c) 2016 WP Ninjas\n * @since 3.0\n */\ndefine( 'views/actions/conditionLayout',[ 'views/actions/whenCollection' ], function( WhenCollection ) {\n\tvar view = Marionette.LayoutView.extend( {\n\t\ttemplate: '#tmpl-nf-cl-actions-condition-layout',\n\n\t\tregions: {\n\t\t\t'when': '.nf-when'\n\t\t},\n\n\t\tinitialize: function( options ) {\n\t\t\tthis.model = options.dataModel.get( 'conditions' );\n\t\t\tif ( ! options.dataModel.get( 'conditions' ) ) return;\n\n\t\t\tthis.collection = options.dataModel.get( 'conditions' ).get( 'when' );\n\t\t\tthis.conditionModel = options.dataModel.get( 'conditions' );\n\t\t},\n\n\t\tonRender: function() {\n\t\t\tif ( ! this.collection ) return;\n\t\t\t/*\n\t\t\t * Show our \"when\" collection in the \"when\" area.\n\t\t\t */\n\t\t\tthis.when.show( new WhenCollection( { collection: this.collection } ) );\n\t\t},\n\n\t\tevents: {\n\t\t\t'change .condition-setting': 'changeSetting',\n\t\t\t'click .nf-add-when': 'clickAddWhen'\n\t\t},\n\n\t\tclickAddWhen: function( e ) {\n\t\t\tnfRadio.channel( 'conditions' ).trigger( 'click:addWhen', e, this.model );\n\t\t},\n\n\t\tchangeSetting: function( e ) {\n\t\t\tnfRadio.channel( 'conditions' ).trigger( 'change:setting', e, this.model )\n\t\t}\n\n\t});\n\n\treturn view;\n} );\n","/**\n * Returns the childview we need to use for our conditional logic form settings.\n *\n * @package Ninja Forms Conditional Logic\n * @copyright (c) 2016 WP Ninjas\n * @since 3.0\n */\ndefine( 'controllers/returnChildView',[ 'views/advanced/conditionCollection', 'views/actions/conditionLayout' ], function( AdvancedView, ActionsView ) {\n\tvar controller = Marionette.Object.extend( {\n\t\tinitialize: function() {\n\t\t\tnfRadio.channel( 'advanced_conditions' ).reply( 'get:settingChildView', this.getAdvancedChildView );\n\t\t\tnfRadio.channel( 'action_conditions' ).reply( 'get:settingChildView', this.getActionChildView );\n\t\t},\n\n\t\tgetAdvancedChildView: function( settingModel ) {\n\t\t\treturn AdvancedView;\n\t\t},\n\n\t\tgetActionChildView: function( settingModel ) {\n\t\t\treturn ActionsView;\n\t\t}\n\n\t});\n\n\treturn controller;\n} );\n\n","/**\n * When Model\n *\n * @package Ninja Forms Conditional Logic\n * @copyright (c) 2016 WP Ninjas\n * @since 3.0\n */\ndefine( 'models/whenModel',[], function() {\n\tvar model = Backbone.Model.extend( {\n\t\tdefaults: {\n\t\t\tconnector: 'AND',\n\t\t\tkey: '',\n\t\t\tcomparator: '',\n\t\t\tvalue: '',\n\t\t\ttype: 'field',\n\t\t\tmodelType: 'when'\n\t\t},\n\n\t\tinitialize: function() {\n\t\t\tnfRadio.channel( 'conditions' ).trigger( 'init:whenModel', this );\n\t\t}\n\t} );\n\t\n\treturn model;\n} );\n","/**\n * When Collection\n *\n * @package Ninja Forms Conditional Logic\n * @copyright (c) 2016 WP Ninjas\n * @since 3.0\n */\ndefine( 'models/whenCollection',['models/whenModel'], function( WhenModel ) {\n\tvar collection = Backbone.Collection.extend( {\n\t\tmodel: WhenModel,\n\n\t\tinitialize: function( models, options ) {\n\t\t\tthis.options = options;\n\t\t}\n\t} );\n\treturn collection;\n} );\n","/**\n * Then Model\n *\n * @package Ninja Forms Conditional Logic\n * @copyright (c) 2016 WP Ninjas\n * @since 3.0\n */\ndefine( 'models/thenModel',[], function() {\n\tvar model = Backbone.Model.extend( {\n\t\tdefaults: {\n\t\t\tkey: '',\n\t\t\ttrigger: '',\n\t\t\tvalue: '',\n\t\t\ttype: 'field',\n\t\t\tmodelType: 'then'\n\t\t},\n\n\t\tinitialize: function() {\n\t\t\tnfRadio.channel( 'conditions' ).trigger( 'init:thenModel', this );\n\t\t}\n\t} );\n\t\n\treturn model;\n} );\n","/**\n * Then Collection\n *\n * @package Ninja Forms Conditional Logic\n * @copyright (c) 2016 WP Ninjas\n * @since 3.0\n */\ndefine( 'models/thenCollection',['models/thenModel'], function( ThenModel ) {\n\tvar collection = Backbone.Collection.extend( {\n\t\tmodel: ThenModel,\n\n\t\tinitialize: function( models, options ) {\n\t\t\tthis.options = options;\n\t\t}\n\t} );\n\treturn collection;\n} );\n","/**\n * Else Model\n *\n * @package Ninja Forms Conditional Logic\n * @copyright (c) 2016 WP Ninjas\n * @since 3.0\n */\ndefine( 'models/elseModel',[], function() {\n\tvar model = Backbone.Model.extend( {\n\t\tdefaults: {\n\t\t\tkey: '',\n\t\t\ttrigger: '',\n\t\t\tvalue: '',\n\t\t\ttype: 'field',\n\t\t\tmodelType: 'else'\n\t\t},\n\n\t\tinitialize: function() {\n\t\t\tnfRadio.channel( 'conditions' ).trigger( 'init:elseModel', this );\n\t\t}\n\t} );\n\t\n\treturn model;\n} );\n","/**\n * Else Collection\n *\n * @package Ninja Forms Conditional Logic\n * @copyright (c) 2016 WP Ninjas\n * @since 3.0\n */\ndefine( 'models/elseCollection',['models/elseModel'], function( ElseModel ) {\n\tvar collection = Backbone.Collection.extend( {\n\t\tmodel: ElseModel,\n\n\t\tinitialize: function( models, options ) {\n\t\t\tthis.options = options;\n\t\t}\n\t} );\n\treturn collection;\n} );\n","/**\n * Conditon Model\n *\n * @package Ninja Forms Conditional Logic\n * @copyright (c) 2016 WP Ninjas\n * @since 3.0\n */\ndefine( 'models/conditionModel',[ 'models/whenCollection', 'models/thenCollection', 'models/elseCollection' ], function( WhenCollection, ThenCollection, ElseCollection ) {\n\tvar model = Backbone.Model.extend( {\n\t\tdefaults: {\n\t\t\tcollapsed: false,\n\t\t\tprocess: 1,\n\t\t\tconnector: 'all',\n\t\t\twhen: [ {} ],\n\t\t\tthen: [ {} ],\n\t\t\telse: []\n\t\t},\n\n\t\tinitialize: function() {\n\t\t\tthis.set( 'when', new WhenCollection( this.get( 'when' ), { conditionModel: this } ) );\n\t\t\tthis.set( 'then', new ThenCollection( this.get( 'then' ), { conditionModel: this } ) );\n\t\t\tthis.set( 'else', new ElseCollection( this.get( 'else' ), { conditionModel: this } ) );\n\n\t\t\tnfRadio.channel( 'conditions' ).trigger( 'init:model', this );\n\t\t}\n\t} );\n\t\n\treturn model;\n} );\n","/**\n * Conditon Collection\n *\n * @package Ninja Forms Conditional Logic\n * @copyright (c) 2016 WP Ninjas\n * @since 3.0\n */\ndefine( 'models/conditionCollection',['models/conditionModel'], function( ConditionModel ) {\n\tvar collection = Backbone.Collection.extend( {\n\t\tmodel: ConditionModel,\n\n\t\tinitialize: function( models, options ) {\n\t\t\tthis.options = options;\n\t\t}\n\t} );\n\treturn collection;\n} );\n","/**\n * Item view for our drawer header\n *\n * @package Ninja Forms Conditional Logic\n * @copyright (c) 2016 WP Ninjas\n * @since 3.0\n */\ndefine( 'views/drawerHeader',[], function( ) {\n\tvar view = Marionette.ItemView.extend({\n\t\ttemplate: \"#tmpl-nf-cl-advanced-drawer-header\",\n\n\t\tevents: {\n\t\t\t'click .nf-add-new': 'clickAddNew'\n\t\t},\n\n\t\tclickAddNew: function( e ) {\n\t\t\tnfRadio.channel( 'conditions' ).trigger( 'click:addNew', e );\n\t\t}\n\t});\n\n\treturn view;\n} );\n","/**\n * Adds a new condition when the add new button is clicked.\n *\n * @package Ninja Forms Conditional Logic\n * @copyright (c) 2016 WP Ninjas\n * @since 3.0\n */\ndefine( 'controllers/newCondition',[ 'models/whenCollection', 'models/whenModel' ], function( WhenCollection, WhenModel ) {\n\tvar controller = Marionette.Object.extend( {\n\t\tinitialize: function() {\n\t\t\tthis.listenTo( nfRadio.channel( 'conditions' ), 'click:addNew', this.addNew );\n\t\t},\n\n\t\taddNew: function( e ) {\n\t\t\tvar conditionCollection = nfRadio.channel( 'settings' ).request( 'get:setting', 'conditions' );\n\t\t\tvar conditionModel = conditionCollection.add( {} );\n\n\t\t\t// Add our condition addition to our change log.\n\t\t\tvar label = {\n\t\t\t\tobject: 'Condition',\n\t\t\t\tlabel: nfcli18n.newConditionCondition,\n\t\t\t\tchange: 'Added',\n\t\t\t\tdashicon: 'plus-alt'\n\t\t\t};\n\n\t\t\tvar data = {\n\t\t\t\tcollection: conditionCollection\n\t\t\t}\n\n\t\t\tnfRadio.channel( 'changes' ).request( 'register:change', 'addCondition', conditionModel, null, label, data );\n\n\t\t\t// Set our 'clean' status to false so that we get a notice to publish changes\n\t\t\tnfRadio.channel( 'app' ).request( 'update:setting', 'clean', false );\n\t\t\tnfRadio.channel( 'app' ).request( 'update:db' );\n\t\t}\n\n\t});\n\n\treturn controller;\n} );\n\n","/**\n * Updates condition settings on field change or drawer close\n *\n * @package Ninja Forms Conditional Logic\n * @copyright (c) 2016 WP Ninjas\n * @since 3.0\n */\ndefine( 'controllers/updateSettings',[], function() {\n\tvar controller = Marionette.Object.extend( {\n\t\tinitialize: function() {\n\t\t\tthis.listenTo( nfRadio.channel( 'conditions' ), 'change:setting', this.updateSetting );\n\t\t},\n\n\t\tupdateSetting: function( e, dataModel ) {\n\t\t\tvar value = jQuery( e.target ).val();\n\t\t\tvar id = jQuery( e.target ).data( 'id' );\n\t\t\tvar before = dataModel.get( id );\n\n\t\t\tif ( jQuery( e.target ).find( ':selected' ).data( 'type' ) ) {\n\t\t\t\tdataModel.set( 'type', jQuery( e.target ).find( ':selected' ).data( 'type' ) );\n\t\t\t}\n\n\t\t\tdataModel.set( id, value );\n\n\t\t\tvar after = value;\n\n\t\t\tvar changes = {\n\t\t\t\tattr: id,\n\t\t\t\tbefore: before,\n\t\t\t\tafter: after\n\t\t\t};\n\n\t\t\t/*\n\t\t\t * The \"Advanced\" domain uses a collection of conditions, while the \"Actions\" domain uses a single collection.\n\t\t\t * Here, if we don't have a collection property, then dataModel must be our conditionModel.\n\t\t\t */\n\t\t\tvar conditionModel = ( 'undefined' == typeof dataModel.collection ) ? dataModel : dataModel.collection.options.conditionModel;\n\n\t\t\tvar data = {\n\t\t\t\tconditionModel: conditionModel\n\t\t\t}\n\n\t\t\tvar label = {\n\t\t\t\tobject: 'Condition',\n\t\t\t\tlabel: 'Condition',\n\t\t\t\tchange: 'Changed ' + id + ' from ' + before + ' to ' + after\n\t\t\t};\n\n\t\t\tnfRadio.channel( 'changes' ).request( 'register:change', 'changeSetting', dataModel, changes, label, data );\n\n\t\t\t// Set our 'clean' status to false so that we get a notice to publish changes\n\t\t\tnfRadio.channel( 'app' ).request( 'update:setting', 'clean', false );\n\t\t\tnfRadio.channel( 'app' ).request( 'update:db' );\n\t\t}\n\n\t});\n\n\treturn controller;\n} );\n","/**\n * Listens for clicks on our different condition controls\n *\n * @package Ninja Forms Conditional Logic\n * @copyright (c) 2016 WP Ninjas\n * @since 3.0\n */\ndefine( 'controllers/clickControls',[], function() {\n\tvar controller = Marionette.Object.extend( {\n\t\tinitialize: function() {\n\t\t\tthis.listenTo( nfRadio.channel( 'conditions' ), 'click:removeCondition', this.removeCondition );\n\t\t\tthis.listenTo( nfRadio.channel( 'conditions' ), 'click:collapseCondition', this.collapseCondition );\n\t\t\tthis.listenTo( nfRadio.channel( 'conditions' ), 'click:removeWhen', this.removeWhen );\n\t\t\tthis.listenTo( nfRadio.channel( 'conditions' ), 'click:removeThen', this.removeThen );\n\t\t\tthis.listenTo( nfRadio.channel( 'conditions' ), 'click:removeElse', this.removeElse );\n\t\t\tthis.listenTo( nfRadio.channel( 'conditions' ), 'click:addWhen', this.addWhen );\n\t\t\tthis.listenTo( nfRadio.channel( 'conditions' ), 'click:addThen', this.addThen );\n\t\t\tthis.listenTo( nfRadio.channel( 'conditions' ), 'click:addElse', this.addElse );\n\t\t},\n\n\t\tremoveCondition: function( e, conditionModel ) {\n\t\t\tvar conditionCollection = conditionModel.collection;\n\t\t\tconditionModel.collection.remove( conditionModel );\n\n\t\t\t/*\n\t\t\t * Register our remove condition event with our changes manager\n\t\t\t */\n\n\t\t\tvar label = {\n\t\t\t\tobject: 'Condition',\n\t\t\t\tlabel: nfcli18n.clickControlsConditionlabel,\n\t\t\t\tchange: 'Removed',\n\t\t\t\tdashicon: 'dismiss'\n\t\t\t};\n\n\t\t\tvar data = {\n\t\t\t\tcollection: conditionCollection\n\t\t\t}\n\n\t\t\tnfRadio.channel( 'changes' ).request( 'register:change', 'removeCondition', conditionModel, null, label, data );\n\n\t\t\t// Set our 'clean' status to false so that we get a notice to publish changes\n\t\t\tnfRadio.channel( 'app' ).request( 'update:setting', 'clean', false );\n\t\t\tnfRadio.channel( 'app' ).request( 'update:db' );\n\t\t},\n\n\t\tcollapseCondition: function( e, conditionModel ) {\n\t\t\tconditionModel.set( 'collapsed', ! conditionModel.get( 'collapsed' ) );\n\t\t},\n\n\t\tremoveWhen: function( e, whenModel ) {\n\t\t\tvar collection = whenModel.collection;\n\t\t\tthis.removeItem( whenModel );\n\t\t\t/*\n\t\t\t * Register our remove when change.\n\t\t\t */\n\t\t\t\n\t\t\tvar label = {\n\t\t\t\tobject: 'Condition - When',\n\t\t\t\tlabel: nfcli18n.clickControlsConditionWhen,\n\t\t\t\tchange: 'Removed',\n\t\t\t\tdashicon: 'dismiss'\n\t\t\t};\n\n\t\t\tvar data = {\n\t\t\t\tcollection: collection\n\t\t\t}\n\n\t\t\tnfRadio.channel( 'changes' ).request( 'register:change', 'removeWhen', whenModel, null, label, data );\n\t\t},\n\n\t\tremoveThen: function( e, thenModel ) {\n\t\t\tvar collection = thenModel.collection;\n\t\t\tthis.removeItem( thenModel );\n\t\t\t/*\n\t\t\t * Register our remove then change.\n\t\t\t */\n\t\t\t\n\t\t\tvar label = {\n\t\t\t\tobject: 'Condition - Then',\n\t\t\t\tlabel: nfcli18n.clickControlsConditionThen,\n\t\t\t\tchange: 'Removed',\n\t\t\t\tdashicon: 'dismiss'\n\t\t\t};\n\n\t\t\tvar data = {\n\t\t\t\tcollection: collection\n\t\t\t}\n\n\t\t\tnfRadio.channel( 'changes' ).request( 'register:change', 'removeThen', thenModel, null, label, data );\n\t\t},\n\n\t\tremoveElse: function( e, elseModel ) {\n\t\t\tvar collection = elseModel.collection;\n\t\t\tthis.removeItem( elseModel );\n\t\t\t/*\n\t\t\t * Register our remove else change.\n\t\t\t */\n\t\t\t\n\t\t\tvar label = {\n\t\t\t\tobject: 'Condition - Else',\n\t\t\t\tlabel: nfcli18n.clickControlsConditionElse,\n\t\t\t\tchange: 'Removed',\n\t\t\t\tdashicon: 'dismiss'\n\t\t\t};\n\n\t\t\tvar data = {\n\t\t\t\tcollection: collection\n\t\t\t}\n\n\t\t\tnfRadio.channel( 'changes' ).request( 'register:change', 'removeElse', elseModel, null, label, data );\n\t\t\t\n\t\t},\n\n\t\tremoveItem: function( itemModel ) {\n\t\t\titemModel.collection.remove( itemModel );\n\n\t\t\t// Set our 'clean' status to false so that we get a notice to publish changes\n\t\t\tnfRadio.channel( 'app' ).request( 'update:setting', 'clean', false );\n\t\t\tnfRadio.channel( 'app' ).request( 'update:db' );\n\t\t},\n\n\t\taddWhen: function( e, conditionModel ) {\n\t\t\tvar whenModel = conditionModel.get( 'when' ).add( {} );\n\n\t\t\t/*\n\t\t\t * Register our add when as a change.\n\t\t\t */\n\t\t\t\n\t\t\tvar label = {\n\t\t\t\tobject: 'Condition - When Criteron',\n\t\t\t\tlabel: nfcli18n.clickControlsConditionWhenCriteron,\n\t\t\t\tchange: 'Added',\n\t\t\t\tdashicon: 'plus-alt'\n\t\t\t};\n\n\t\t\tvar data = {\n\t\t\t\tconditionModel: conditionModel\n\t\t\t}\n\n\t\t\tnfRadio.channel( 'changes' ).request( 'register:change', 'addWhen', whenModel, null, label, data );\n\n\t\t\t// Set our 'clean' status to false so that we get a notice to publish changes\n\t\t\tnfRadio.channel( 'app' ).request( 'update:setting', 'clean', false );\n\t\t\tnfRadio.channel( 'app' ).request( 'update:db' );\n\t\t},\n\n\t\taddThen: function( e, conditionModel ) {\n\t\t\tvar thenModel = conditionModel.get( 'then' ).add( {} );\n\n\t\t\t/*\n\t\t\t * Register our add then as a change.\n\t\t\t */\n\t\t\t\n\t\t\tvar label = {\n\t\t\t\tobject: 'Condition - Do Item',\n\t\t\t\tlabel: nfcli18n.clickControlsConditionDoItem,\n\t\t\t\tchange: 'Added',\n\t\t\t\tdashicon: 'plus-alt'\n\t\t\t};\n\n\t\t\tvar data = {\n\t\t\t\tconditionModel: conditionModel\n\t\t\t}\n\n\t\t\tnfRadio.channel( 'changes' ).request( 'register:change', 'addThen', thenModel, null, label, data );\n\n\t\t\t// Set our 'clean' status to false so that we get a notice to publish changes\n\t\t\tnfRadio.channel( 'app' ).request( 'update:setting', 'clean', false );\n\t\t\tnfRadio.channel( 'app' ).request( 'update:db' );\n\t\t},\n\n\t\taddElse: function( e, conditionModel ) {\n\t\t\tvar elseModel = conditionModel.get( 'else' ).add( {} );\n\n\t\t\t/*\n\t\t\t * Register our add when as a change.\n\t\t\t */\n\t\t\t\n\t\t\tvar label = {\n\t\t\t\tobject: 'Condition - Else Item',\n\t\t\t\tlabel: nfcli18n.clickControlsConditionElseItem,\n\t\t\t\tchange: 'Added',\n\t\t\t\tdashicon: 'plus-alt'\n\t\t\t};\n\n\t\t\tvar data = {\n\t\t\t\tconditionModel: conditionModel\n\t\t\t}\n\n\t\t\tnfRadio.channel( 'changes' ).request( 'register:change', 'addElse', elseModel, null, label, data );\n\n\t\t\t// Set our 'clean' status to false so that we get a notice to publish changes\n\t\t\tnfRadio.channel( 'app' ).request( 'update:setting', 'clean', false );\n\t\t\tnfRadio.channel( 'app' ).request( 'update:db' );\n\t\t}\n\n\t});\n\n\treturn controller;\n} );\n\n","/**\n * Handles undoing everything for conditions.\n *\n * @package Ninja Forms Conditional Logic\n * @copyright (c) 2016 WP Ninjas\n * @since 3.0\n */\ndefine( 'controllers/undo',[], function() {\n\tvar controller = Marionette.Object.extend( {\n\t\tinitialize: function() {\n\t\t\tnfRadio.channel( 'changes' ).reply( 'undo:addCondition', this.undoAddCondition, this );\n\t\t\tnfRadio.channel( 'changes' ).reply( 'undo:removeCondition', this.undoRemoveCondition, this );\n\t\t\tnfRadio.channel( 'changes' ).reply( 'undo:addWhen', this.undoAddWhen, this );\n\t\t\tnfRadio.channel( 'changes' ).reply( 'undo:addThen', this.undoAddThen, this );\n\t\t\tnfRadio.channel( 'changes' ).reply( 'undo:addElse', this.undoAddElse, this );\n\t\t\tnfRadio.channel( 'changes' ).reply( 'undo:removeWhen', this.undoRemoveWhen, this );\n\t\t\tnfRadio.channel( 'changes' ).reply( 'undo:removeThen', this.undoRemoveThen, this );\n\t\t\tnfRadio.channel( 'changes' ).reply( 'undo:removeElse', this.undoRemoveElse, this );\n\t\t},\n\n\t\tundoAddCondition: function( change, undoAll ) {\n\t\t\tvar dataModel = change.get( 'model' );\n\t\t\tvar data = change.get( 'data' );\n\t\t\t\n\t\t\tdata.collection.remove( dataModel );\n\n\t\t\t/*\n\t\t\t * Loop through our change collection and remove any setting changes that belong to the condition we've added.\n\t\t\t */\n\t\t\tvar changeCollection = nfRadio.channel( 'changes' ).request( 'get:collection' );\n\t\t\tvar results = changeCollection.where( function( changeModel ) {\n\t\t\t\tif ( ( changeModel = dataModel ) || 'undefined' != typeof changeModel.get( 'data' ).conditionModel && changeModel.get( 'data' ).conditionModel == dataModel ) {\n\t\t\t\t\treturn true;\n\t\t\t\t} else {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t} );\n\n\t\t\t_.each( results, function( model ) {\n\t\t\t\tchangeCollection.remove( model );\n\t\t\t} );\n\n\t\t\tthis.maybeRemoveChange( change, undoAll );\n\t\t},\n\n\t\tundoRemoveCondition: function( change, undoAll ) {\n\t\t\tvar dataModel = change.get( 'model' );\n\t\t\tvar data = change.get( 'data' );\n\t\t\t\n\t\t\tdata.collection.add( dataModel );\n\n\t\t\tthis.maybeRemoveChange( change, undoAll );\n\t\t},\n\n\t\tundoAddWhen: function( change, undoAll ) {\n\t\t\tvar whenModel = change.get( 'model' );\n\t\t\tvar data = change.get( 'data' );\n\t\t\t\n\t\t\tdata.conditionModel.get( 'when' ).remove( whenModel );\n\n\t\t\tthis.maybeRemoveChange( change, undoAll );\n\t\t},\n\n\t\tundoAddThen: function( change, undoAll ) {\n\t\t\tvar thenModel = change.get( 'model' );\n\t\t\tvar data = change.get( 'data' );\n\t\t\t\n\t\t\tdata.conditionModel.get( 'then' ).remove( thenModel );\n\n\t\t\tthis.maybeRemoveChange( change, undoAll );\n\t\t},\n\n\t\tundoAddElse: function( change, undoAll ) {\n\t\t\tvar elseModel = change.get( 'model' );\n\t\t\tvar data = change.get( 'data' );\n\t\t\t\n\t\t\tdata.conditionModel.get( 'else' ).remove( elseModel );\n\n\t\t\tthis.maybeRemoveChange( change, undoAll );\n\t\t},\n\n\t\tundoRemoveWhen: function( change, undoAll ) {\n\t\t\tvar whenModel = change.get( 'model' );\n\t\t\tvar data = change.get( 'data' );\n\t\t\t\n\t\t\tdata.collection.add( whenModel );\n\n\t\t\tthis.maybeRemoveChange( change, undoAll );\n\t\t},\n\n\t\tundoRemoveThen: function( change, undoAll ) {\n\t\t\tvar thenModel = change.get( 'model' );\n\t\t\tvar data = change.get( 'data' );\n\t\t\t\n\t\t\tdata.collection.add( thenModel );\n\n\t\t\tthis.maybeRemoveChange( change, undoAll );\n\t\t},\n\n\t\tundoRemoveElse: function( change, undoAll ) {\n\t\t\tvar elseModel = change.get( 'model' );\n\t\t\tvar data = change.get( 'data' );\n\t\t\t\n\t\t\tdata.collection.add( elseModel );\n\n\t\t\tthis.maybeRemoveChange( change, undoAll );\n\t\t},\n\n\t\t/**\n\t\t * If our undo action was requested to 'remove' the change from the collection, remove it.\n\t\t * \n\t\t * @since 3.0\n\t\t * @param backbone.model \tchange \tmodel of our change\n\t\t * @param boolean \t\t\tremove \tshould we remove this item from our change collection\n\t\t * @return void\n\t\t */\n\t\tmaybeRemoveChange: function( change, undoAll ) {\t\t\t\n\t\t\tvar undoAll = typeof undoAll !== 'undefined' ? undoAll : false;\n\t\t\tif ( ! undoAll ) {\n\t\t\t\t// Update preview.\n\t\t\t\tnfRadio.channel( 'app' ).request( 'update:db' );\n\t\t\t\tvar changeCollection = nfRadio.channel( 'changes' ).request( 'get:collection' );\n\t\t\t\tchangeCollection.remove( change );\n\t\t\t\tif ( 0 == changeCollection.length ) {\n\t\t\t\t\tnfRadio.channel( 'app' ).request( 'update:setting', 'clean', true );\n\t\t\t\t\tnfRadio.channel( 'app' ).request( 'close:drawer' );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t});\n\n\treturn controller;\n} );\n\n","/**\n * Returns the type of input value we'd like to use.\n * This covers all the core field types.\n *\n * Add-ons can copy this code structure in order to get custom \"values\" for conditions.\n *\n * @package Ninja Forms Conditional Logic\n * @copyright (c) 2016 WP Ninjas\n * @since 3.0\n */\ndefine( 'controllers/coreValues',[], function() {\n\tvar controller = Marionette.Object.extend( {\n\t\tinitialize: function() {\n\t\t\tnfRadio.channel( 'conditions-checkbox' ).reply( 'get:valueInput', this.getCheckboxValue );\n\t\t\tnfRadio.channel( 'conditions-list' ).reply( 'get:valueInput', this.getListValue );\n\t\t\tnfRadio.channel( 'conditions-listcountry' ).reply( 'get:valueInput', this.getListCountryValue );\n\t\t\tnfRadio.channel( 'conditions-date' ).reply( 'get:valueInput', this.getDateValue );\n\t\t},\n\n\t\tgetCheckboxValue: function( key, trigger, value ) {\n\t\t\t/*\n\t\t\t * Checks our values ensures they've been converted to strings and\n\t\t\t * sets the value.\n\t\t\t */\n\t\t\tif( 1 == value && value.length > 1 ) {\n\t\t\t\tvalue = 'checked';\n\t\t\t} else if( 0 == value && value.length > 1 ) {\n value = 'unchecked';\n } else if( 0 == value.length ){\n\t\t\t\tvalue = '';\n\t\t\t}\n\n\t\t\tvar template = Backbone.Radio.channel( 'app' ).request( 'get:template', '#tmpl-nf-cl-value-checkbox' );\n\t\t\treturn template( { value: value } );\n\t\t},\n\n\t\tgetListValue: function( key, trigger, value ) {\n\t\t\tvar fieldModel = nfRadio.channel( 'fields' ).request( 'get:field', key );\n\t\t\tvar options = fieldModel.get( 'options' );\n\t\t\tvar template = Backbone.Radio.channel( 'app' ).request( 'get:template', '#tmpl-nf-cl-value-list' );\n\t\t\treturn template( { options: options, value: value } );\n\t\t},\n\n\t\tgetListCountryValue: function( key, trigger, value ) {\n\t\t\tvar fieldModel = nfRadio.channel( 'fields' ).request( 'get:field', key );\n\t\t\tvar options = fieldModel.get( 'options' );\n\t\t\tvar template = Backbone.Radio.channel( 'app' ).request( 'get:template', '#tmpl-nf-cl-value-list' );\n\n\t\t\toptions.reset();\n\t\t\t_.each( nfListCountries, function( value, label ) {\n\t\t\t\toptions.add( { label: label, value: value } );\n\t\t\t});\n\n\t\t\treturn template( { options: options, value: value } );\n\t\t},\n\n\t\tgetDateValue: function( key, trigger, value ) {\n\t\t\tlet fieldModel = nfRadio.channel( 'fields' ).request( 'get:field', key );\n\t\t\tlet dateMode = fieldModel.get( 'date_mode' );\n\n\t\t\tif ( 'undefined' == typeof dateMode ) {\n\t\t\t\tdateMode = 'date_only';\n\t\t\t}\n\n\t\t\tlet timestamp = value * 1000;\n\t\t\tlet dateObject = new Date( timestamp );\n\t\t\tdateObject = new Date( dateObject.getUTCFullYear(), dateObject.getUTCMonth(), dateObject.getUTCDate(), dateObject.getUTCHours(), dateObject.getUTCMinutes() );\n\n\t\t\tlet selectedHour = dateObject.getHours();\n\t\t\tlet selectedMinute = dateObject.getMinutes(); \n\n\t\t\tlet hourSelect = '';\n\n\t\t\tlet minuteSelect = '';\n\n\t\t\tlet date = moment( dateObject.toUTCString() ).format( 'YYYY-MM-DD' );\n\t\t\tif ( '1970-01-01' == date ) {\n\t\t\t\tdate = '';\n\t\t\t}\n\n\t\t\tlet template = Backbone.Radio.channel( 'app' ).request( 'get:template', '#tmpl-nf-cl-value-date-' + dateMode );\n\t\t\treturn template( { value: value, date: date, hourSelect: hourSelect, minuteSelect: minuteSelect } );\n\t\t},\n\n\n\t});\n\n\treturn controller;\n} );\n\n","/**\n * Returns an object with each comparator we'd like to use.\n * This covers all the core field types.\n *\n * Add-ons can copy this code structure in order to get custom \"comparators\" for conditions.\n *\n * @package Ninja Forms Conditional Logic\n * @copyright (c) 2016 WP Ninjas\n * @since 3.0\n */\ndefine( 'controllers/coreComparators',[], function() {\n\tvar controller = Marionette.Object.extend( {\n\t\tinitialize: function() {\n\t\t\tnfRadio.channel( 'conditions-checkbox' ).reply( 'get:comparators', this.getCheckboxComparators );\n\t\t\tnfRadio.channel( 'conditions-listradio' ).reply( 'get:comparators', this.getListSingleComparators );\n\t\t\tnfRadio.channel( 'conditions-listselect' ).reply( 'get:comparators', this.getListSingleComparators );\n\t\t\tnfRadio.channel( 'conditions-list' ).reply( 'get:comparators', this.getListComparators );\n\t\t\tnfRadio.channel( 'conditions-date' ).reply( 'get:comparators', this.getDateComparators );\n\t\t},\n\n\t\tgetCheckboxComparators: function( defaultComparators ) {\n\t\t\treturn {\n\t\t\t\tis: {\n\t\t\t\t\tlabel: nfcli18n.coreComparatorsIs,\n\t\t\t\t\tvalue: 'equal'\n\t\t\t\t},\n\n\t\t\t\tisnot: {\n\t\t\t\t\tlabel: nfcli18n.coreComparatorsIsNot,\n\t\t\t\t\tvalue: 'notequal'\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\tgetListComparators: function( defaultComparators ) {\n\t\t\treturn {\n\t\t\t\thas: {\n\t\t\t\t\tlabel: nfcli18n.coreComparatorsHasSelected,\n\t\t\t\t\tvalue: 'contains'\n\t\t\t\t},\n\n\t\t\t\thasnot: {\n\t\t\t\t\tlabel: nfcli18n.coreComparatorsDoesNotHaveSelected,\n\t\t\t\t\tvalue: 'notcontains'\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\tgetListSingleComparators: function( defaultComparators, currentComparator ) {\n\t\t\t/*\n\t\t\t * Radio and Select lists need to use equal and notequal.\n\t\t\t * In previous versions, however, they used contains and notcontains.\n\t\t\t * In order to keep forms working that were made in those previous versions,\n\t\t\t * we check to see if the currentComparator is contains or notcontains.\n\t\t\t * If it is, we return those values; else we return equal or not equal.\n\t\t\t */\n\t\t\tif ( 'contains' == currentComparator || 'notcontains' == currentComparator ) {\n\t\t\t\treturn {\n\t\t\t\t\thas: {\n\t\t\t\t\t\tlabel: nfcli18n.coreComparatorsHasSelected,\n\t\t\t\t\t\tvalue: 'contains'\n\t\t\t\t\t},\n\n\t\t\t\t\thasnot: {\n\t\t\t\t\t\tlabel: nfcli18n.coreComparatorsDoesNotHaveSelected,\n\t\t\t\t\t\tvalue: 'notcontains'\n\t\t\t\t\t}\n\t\t\t\t}\t\t\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\t\thas: {\n\t\t\t\t\t\tlabel: nfcli18n.coreComparatorsHasSelected,\n\t\t\t\t\t\tvalue: 'equal'\n\t\t\t\t\t},\n\n\t\t\t\t\thasnot: {\n\t\t\t\t\t\tlabel: nfcli18n.coreComparatorsDoesNotHaveSelected,\n\t\t\t\t\t\tvalue: 'notequal'\n\t\t\t\t\t}\n\t\t\t\t}\t\n\t\t},\n\n\t\tgetDateComparators: function( defaultComparators ) {\n\t\t\treturn {\n\t\t\t\tbefore: {\n\t\t\t\t\tlabel: nfcli18n.coreComparatorsBefore,\n\t\t\t\t\tvalue: 'less'\n\t\t\t\t},\n\n\t\t\t\tonorbefore: {\n\t\t\t\t\tlabel: nfcli18n.coreComparatorsOnOrBefore,\n\t\t\t\t\tvalue: 'lessequal'\n\t\t\t\t},\n\n\t\t\t\tequal: {\n\t\t\t\t\tlabel: nfcli18n.coreComparatorsIs,\n\t\t\t\t\tvalue: 'equal'\n\t\t\t\t},\n\n\t\t\t\tonorafter: {\n\t\t\t\t\tlabel: nfcli18n.coreComparatorsOnOrAfter,\n\t\t\t\t\tvalue: 'greaterequal'\n\t\t\t\t},\n\n\t\t\t\tafter: {\n\t\t\t\t\tlabel: nfcli18n.coreComparatorsAfter,\n\t\t\t\t\tvalue: 'greater'\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t});\n\n\treturn controller;\n} );\n\n","/**\n * Returns an object with each trigger we'd like to use.\n * This covers all the core field types.\n *\n * Add-ons can copy this code structure in order to get custom \"triggers\" for conditions.\n *\n * @package Ninja Forms Conditional Logic\n * @copyright (c) 2016 WP Ninjas\n * @since 3.0\n */\ndefine( 'controllers/coreTriggers',[], function() {\n\tvar controller = Marionette.Object.extend( {\n\t\tinitialize: function() {\n\t\t\tnfRadio.channel( 'conditions-list' ).reply( 'get:triggers', this.getListTriggers );\n\t\t\tnfRadio.channel( 'conditions-submit' ).reply( 'get:triggers', this.getSubmitTriggers );\n\t\t\tnfRadio.channel( 'conditions-html' ).reply( 'get:triggers', this.getHTMLTriggers );\n\t\t\tnfRadio.channel( 'conditions-hr' ).reply( 'get:triggers', this.getDividerTriggers );\n\t\t\tnfRadio.channel( 'conditions-hidden' ).reply( 'get:triggers', this.getHiddenTriggers );\n\t\t},\n\n\t\tgetListTriggers: function( defaultTriggers ) {\n\t\t\tvar triggers = _.extend( defaultTriggers, {\n\t\t\t\tselect_option: {\n\t\t\t\t\tlabel: nfcli18n.coreTriggersSelectOption,\n\t\t\t\t\tvalue: 'select_option'\n\t\t\t\t},\n\n\t\t\t\tdeselect_option: {\n\t\t\t\t\tlabel: nfcli18n.coreTriggersDeselectOption,\n\t\t\t\t\tvalue: 'deselect_option'\n\t\t\t\t},\n\n\t\t\t\tshow_option: {\n\t\t\t\t\tlabel: nfcli18n.coreTriggersShowOption,\n\t\t\t\t\tvalue: 'show_option'\n\t\t\t\t},\n\n\t\t\t\thide_option: {\n\t\t\t\t\tlabel: nfcli18n.coreTriggersHideOption,\n\t\t\t\t\tvalue: 'hide_option'\n\t\t\t\t}\n\t\t\t} );\n\n\t\t\tvar triggers = _.omit( defaultTriggers, 'change_value' );\n\n\t\t\treturn triggers;\n\t\t},\n\n\t\tgetSubmitTriggers: function( defaultTriggers ) {\n\t\t\treturn _.omit( defaultTriggers, ['change_value', 'set_required', 'unset_required'] );\n\t\t},\n\n\t\tgetHTMLTriggers: function( defaultTriggers ) {\n\t\t\treturn _.omit( defaultTriggers, ['set_required', 'unset_required'] );\n\t\t},\n\n\t\tgetDividerTriggers: function( defaultTriggers ) {\n\t\t\treturn _.omit( defaultTriggers, ['change_value', 'set_required', 'unset_required'] );\n\t\t},\n\n\t\tgetHiddenTriggers: function( defaultTriggers ) {\n\t\t\treturn _.omit( defaultTriggers, ['set_required', 'unset_required'] );\n\t\t}\n\n\t});\n\n\treturn controller;\n} );\n","/**\n * Returns the view to use in the drawer header.\n *\n * @package Ninja Forms Conditional Logic\n * @copyright (c) 2016 WP Ninjas\n * @since 3.0\n */\ndefine( 'controllers/getDrawerHeader',[ 'views/drawerHeader' ], function( DrawerHeaderView ) {\n\tvar controller = Marionette.Object.extend( {\n\t\tinitialize: function() {\n\t\t\tnfRadio.channel( 'conditional_logic' ).reply( 'get:drawerHeaderView', this.getDrawerHeaderView, this );\n\t\t},\n\n\t\tgetDrawerHeaderView: function() {\n\t\t\treturn DrawerHeaderView;\n\t\t}\n\t});\n\n\treturn controller;\n} );\n\n","/**\n * Tracks key changes and updates when/then/else models\n *\n * @package Ninja Forms Conditional Logic\n * @copyright (c) 2016 WP Ninjas\n * @since 3.0\n */\ndefine( 'controllers/trackKeyChanges',[], function() {\n\tvar controller = Marionette.Object.extend( {\n\t\tinitialize: function() {\n\t\t\tthis.listenTo( nfRadio.channel( 'conditions' ), 'init:whenModel', this.registerKeyChangeTracker );\n\t\t\tthis.listenTo( nfRadio.channel( 'conditions' ), 'init:thenModel', this.registerKeyChangeTracker );\n\t\t\tthis.listenTo( nfRadio.channel( 'conditions' ), 'init:elseModel', this.registerKeyChangeTracker );\n\t\t},\n\n\t\tregisterKeyChangeTracker: function( itemModel ) {\n // Update selected field if the selected field's key changes.\n itemModel.listenTo( nfRadio.channel( 'app' ), 'replace:fieldKey', this.updateKey, itemModel );\n },\n\n\t\tupdateKey: function( fieldModel, keyModel, settingModel ) {\n\t\t\tvar oldKey = keyModel._previousAttributes[ 'key' ];\n var newKey = keyModel.get( 'key' );\n \n if( this.get( 'key' ) == oldKey ) {\n this.set( 'key', newKey );\n }\n\t\t}\n\t});\n\n\treturn controller;\n} );\n","/**\n * When we init our action model, check to see if we have a 'conditions' setting that needs to be converted into a collection.\n *\n * @package Ninja Forms Conditional Logic\n * @copyright (c) 2016 WP Ninjas\n * @since 3.0\n */\ndefine( 'controllers/maybeConvertConditions',[ 'models/conditionModel' ], function( ConditionModel ) {\n\tvar controller = Marionette.Object.extend( {\n\t\tinitialize: function() {\n\t\t\tthis.listenTo( nfRadio.channel( 'actions' ), 'init:actionModel', this.maybeConvertConditions );\n\t\t},\n\n\t\tmaybeConvertConditions: function( actionModel ) {\n\t\t\tvar conditions = actionModel.get( 'conditions' );\n\t\t\tif ( ! conditions ) {\n\t\t\t\tactionModel.set( 'conditions', new ConditionModel() );\n\t\t\t} else if ( false === conditions instanceof Backbone.Model ) {\n\t\t\t\tactionModel.set( 'conditions', new ConditionModel( conditions ) );\n\t\t\t}\n\t\t}\n\n\t});\n\n\treturn controller;\n} );\n\n","/**\n * Register filters for our when/then key groups/settings.\n *\n * @package Ninja Forms Conditional Logic\n * @copyright (c) 2016 WP Ninjas\n * @since 3.0\n */\ndefine( 'controllers/filters',[], function() {\n\tvar controller = Marionette.Object.extend( {\n\t\tfilters: [],\n\n\t\tinitialize: function() {\n\t\t\tnfRadio.channel( 'conditions' ).reply( 'add:groupFilter', this.addFilter, this );\n\t\t\tnfRadio.channel( 'conditions' ).reply( 'get:groupFilters', this.getFilters, this );\n\t\t},\n\n\t\taddFilter: function( callback ) {\n\t\t\tthis.filters.push( callback );\n\t\t},\n\n\t\tgetFilters: function() {\n\t\t\treturn this.filters;\n\t\t}\n\n\t});\n\n\treturn controller;\n} );\n\n","/**\n * Listens for changes in the \"extra\" settings in \"when\" settings.\n * We use this for the date field to update the \"value\" to a timestamp when we change a date value setting.\n *\n * @package Ninja Forms Conditional Logic\n * @copyright (c) 2016 WP Ninjas\n * @since 3.0\n */\ndefine( 'controllers/fieldDate',[], function() {\n\tvar controller = Marionette.Object.extend( {\n\t\tinitialize: function() {\n\t\t\tthis.listenTo( nfRadio.channel( 'conditions' ), 'change:extra', this.maybeUpdateSetting );\n\t\t},\n\n\t\tmaybeUpdateSetting: function( e, dataModel ) {\n\t\t\tlet dateString = '';\n\t\t\t// Get our date\n\t\t\tlet date = jQuery( e.target ).parent().parent().find( \"[data-type='date']\" ).val();\n\t\t\tif ( 'undefined' == typeof date ) {\n\t\t\t\tdate = '1970-01-02';\n\t\t\t}\n\t\t\tdateString += date + 'T';\n\n\t\t\t// Get our hour\n\t\t\tlet hour = jQuery( e.target ).parent().parent().find( \"[data-type='hour']\" ).val();\n\t\t\tif ( 'undefined' == typeof hour ) {\n\t\t\t\thour = '00';\n\t\t\t}\n\t\t\tdateString += hour + ':';\n\n\t\t\t// Get our minute\n\t\t\tlet minute = jQuery( e.target ).parent().parent().find( \"[data-type='minute']\" ).val();\n\t\t\tif ( 'undefined' == typeof minute ) {\n\t\t\t\tminute = '00';\n\t\t\t}\n\t\t\tdateString += minute + 'Z';\n\n\t\t\t// Build a timestamp\n\t\t\tlet dateObject = new Date( dateString );\n\t\t\tlet timestamp = Math.floor( dateObject.getTime() / 1000 );\n\n\t\t\t// Update our value with the timestamp\n\t\t\tdataModel.set( 'value', timestamp );\n\t\t\tnfRadio.channel( 'app' ).request( 'update:setting', 'clean', false );\n\t\t},\n\n\t});\n\n\treturn controller;\n} );\n","/**\n * Loads all of our custom controllers.\n *\n * @package Ninja Forms Conditional Logic\n * @copyright (c) 2016 WP Ninjas\n * @since 3.0\n */\ndefine( 'controllers/loadControllers',[\n\t'controllers/templateHelpers',\n\t'controllers/returnChildView',\n\t'models/conditionCollection',\n\t'views/drawerHeader',\n\t'controllers/newCondition',\n\t'controllers/updateSettings',\n\t'controllers/clickControls',\n\t'controllers/undo',\n\t// 'controllers/maybeModifyElse',\n\t'controllers/coreValues',\n\t'controllers/coreComparators',\n\t'controllers/coreTriggers',\n\t'controllers/getDrawerHeader',\n\t'controllers/trackKeyChanges',\n\t'controllers/maybeConvertConditions',\n\t'controllers/filters',\n\t'controllers/fieldDate'\n\n\t], function(\n\n\tTemplateHelpers,\n\tReturnChildView,\n\tConditionCollection,\n\tDrawerHeaderView,\n\tNewCondition,\n\tUpdateSettings,\n\tClickControls,\n\tUndo,\n\t// MaybeModifyElse,\n\tCoreValues,\n\tCoreComparators,\n\tCoreTriggers,\n\tGetDrawerHeader,\n\tTrackKeyChanges,\n\tMaybeConvertConditions,\n\tFilters,\n\tFieldDate\n\t) {\n\tvar controller = Marionette.Object.extend( {\n\t\tinitialize: function() {\n\t\t\tnew TemplateHelpers();\n\t\t\tnew ReturnChildView();\n\t\t\tnew NewCondition();\n\t\t\tnew UpdateSettings();\n\t\t\tnew ClickControls();\n\t\t\tnew Undo();\n\t\t\t// new MaybeModifyElse();\n\t\t\tnew CoreValues();\n\t\t\tnew CoreComparators();\n\t\t\tnew CoreTriggers();\n\t\t\tnew GetDrawerHeader();\n\t\t\tnew TrackKeyChanges();\n\t\t\tnew MaybeConvertConditions();\n\t\t\tnew Filters();\n\t\t\tnew FieldDate();\n\t\t}\n\t});\n\n\treturn controller;\n} );\n\n","var nfRadio = Backbone.Radio;\n\nrequire( [ 'controllers/loadControllers', 'models/conditionCollection' ], function( LoadControllers, ConditionCollection ) {\n\n\tvar NFConditionalLogic = Marionette.Application.extend( {\n\n\t\tinitialize: function( options ) {\n\t\t\tthis.listenTo( nfRadio.channel( 'app' ), 'after:appStart', this.afterNFLoad );\n\t\t},\n\n\t\tonStart: function() {\n\t\t\tnew LoadControllers();\n\t\t},\n\n\t\tafterNFLoad: function( app ) {\n\t\t\t/*\n\t\t\t * Convert our form's \"condition\" setting into a collection.\n\t\t\t */\n\t\t\tvar conditions = nfRadio.channel( 'settings' ).request( 'get:setting', 'conditions' );\n\n\t\t\tif ( false === conditions instanceof Backbone.Collection ) {\n\t\t\t\tconditions = new ConditionCollection( conditions );\n\t\t\t\tnfRadio.channel( 'settings' ).request( 'update:setting', 'conditions', conditions, true );\n\t\t\t}\n\t\t}\n\t} );\n\n\tvar nfConditionalLogic = new NFConditionalLogic();\n\tnfConditionalLogic.start();\n} );\ndefine(\"main\", function(){});\n\n","}());"]} \ No newline at end of file diff --git a/assets/js/min/front-end.js b/assets/js/min/front-end.js new file mode 100644 index 0000000..ad7b118 --- /dev/null +++ b/assets/js/min/front-end.js @@ -0,0 +1,1131 @@ +(function () { +/** + * @license almond 0.3.1 Copyright (c) 2011-2014, The Dojo Foundation All Rights Reserved. + * Available via the MIT or new BSD license. + * see: http://github.com/jrburke/almond for details + */ +//Going sloppy to avoid 'use strict' string cost, but strict practices should +//be followed. +/*jslint sloppy: true */ +/*global setTimeout: false */ + +var requirejs, require, define; +(function (undef) { + var main, req, makeMap, handlers, + defined = {}, + waiting = {}, + config = {}, + defining = {}, + hasOwn = Object.prototype.hasOwnProperty, + aps = [].slice, + jsSuffixRegExp = /\.js$/; + + function hasProp(obj, prop) { + return hasOwn.call(obj, prop); + } + + /** + * Given a relative module name, like ./something, normalize it to + * a real name that can be mapped to a path. + * @param {String} name the relative name + * @param {String} baseName a real name that the name arg is relative + * to. + * @returns {String} normalized name + */ + function normalize(name, baseName) { + var nameParts, nameSegment, mapValue, foundMap, lastIndex, + foundI, foundStarMap, starI, i, j, part, + baseParts = baseName && baseName.split("/"), + map = config.map, + starMap = (map && map['*']) || {}; + + //Adjust any relative paths. + if (name && name.charAt(0) === ".") { + //If have a base name, try to normalize against it, + //otherwise, assume it is a top-level require that will + //be relative to baseUrl in the end. + if (baseName) { + name = name.split('/'); + lastIndex = name.length - 1; + + // Node .js allowance: + if (config.nodeIdCompat && jsSuffixRegExp.test(name[lastIndex])) { + name[lastIndex] = name[lastIndex].replace(jsSuffixRegExp, ''); + } + + //Lop off the last part of baseParts, so that . matches the + //"directory" and not name of the baseName's module. For instance, + //baseName of "one/two/three", maps to "one/two/three.js", but we + //want the directory, "one/two" for this normalization. + name = baseParts.slice(0, baseParts.length - 1).concat(name); + + //start trimDots + for (i = 0; i < name.length; i += 1) { + part = name[i]; + if (part === ".") { + name.splice(i, 1); + i -= 1; + } else if (part === "..") { + if (i === 1 && (name[2] === '..' || name[0] === '..')) { + //End of the line. Keep at least one non-dot + //path segment at the front so it can be mapped + //correctly to disk. Otherwise, there is likely + //no path mapping for a path starting with '..'. + //This can still fail, but catches the most reasonable + //uses of .. + break; + } else if (i > 0) { + name.splice(i - 1, 2); + i -= 2; + } + } + } + //end trimDots + + name = name.join("/"); + } else if (name.indexOf('./') === 0) { + // No baseName, so this is ID is resolved relative + // to baseUrl, pull off the leading dot. + name = name.substring(2); + } + } + + //Apply map config if available. + if ((baseParts || starMap) && map) { + nameParts = name.split('/'); + + for (i = nameParts.length; i > 0; i -= 1) { + nameSegment = nameParts.slice(0, i).join("/"); + + if (baseParts) { + //Find the longest baseName segment match in the config. + //So, do joins on the biggest to smallest lengths of baseParts. + for (j = baseParts.length; j > 0; j -= 1) { + mapValue = map[baseParts.slice(0, j).join('/')]; + + //baseName segment has config, find if it has one for + //this name. + if (mapValue) { + mapValue = mapValue[nameSegment]; + if (mapValue) { + //Match, update name to the new value. + foundMap = mapValue; + foundI = i; + break; + } + } + } + } + + if (foundMap) { + break; + } + + //Check for a star map match, but just hold on to it, + //if there is a shorter segment match later in a matching + //config, then favor over this star map. + if (!foundStarMap && starMap && starMap[nameSegment]) { + foundStarMap = starMap[nameSegment]; + starI = i; + } + } + + if (!foundMap && foundStarMap) { + foundMap = foundStarMap; + foundI = starI; + } + + if (foundMap) { + nameParts.splice(0, foundI, foundMap); + name = nameParts.join('/'); + } + } + + return name; + } + + function makeRequire(relName, forceSync) { + return function () { + //A version of a require function that passes a moduleName + //value for items that may need to + //look up paths relative to the moduleName + var args = aps.call(arguments, 0); + + //If first arg is not require('string'), and there is only + //one arg, it is the array form without a callback. Insert + //a null so that the following concat is correct. + if (typeof args[0] !== 'string' && args.length === 1) { + args.push(null); + } + return req.apply(undef, args.concat([relName, forceSync])); + }; + } + + function makeNormalize(relName) { + return function (name) { + return normalize(name, relName); + }; + } + + function makeLoad(depName) { + return function (value) { + defined[depName] = value; + }; + } + + function callDep(name) { + if (hasProp(waiting, name)) { + var args = waiting[name]; + delete waiting[name]; + defining[name] = true; + main.apply(undef, args); + } + + if (!hasProp(defined, name) && !hasProp(defining, name)) { + throw new Error('No ' + name); + } + return defined[name]; + } + + //Turns a plugin!resource to [plugin, resource] + //with the plugin being undefined if the name + //did not have a plugin prefix. + function splitPrefix(name) { + var prefix, + index = name ? name.indexOf('!') : -1; + if (index > -1) { + prefix = name.substring(0, index); + name = name.substring(index + 1, name.length); + } + return [prefix, name]; + } + + /** + * Makes a name map, normalizing the name, and using a plugin + * for normalization if necessary. Grabs a ref to plugin + * too, as an optimization. + */ + makeMap = function (name, relName) { + var plugin, + parts = splitPrefix(name), + prefix = parts[0]; + + name = parts[1]; + + if (prefix) { + prefix = normalize(prefix, relName); + plugin = callDep(prefix); + } + + //Normalize according + if (prefix) { + if (plugin && plugin.normalize) { + name = plugin.normalize(name, makeNormalize(relName)); + } else { + name = normalize(name, relName); + } + } else { + name = normalize(name, relName); + parts = splitPrefix(name); + prefix = parts[0]; + name = parts[1]; + if (prefix) { + plugin = callDep(prefix); + } + } + + //Using ridiculous property names for space reasons + return { + f: prefix ? prefix + '!' + name : name, //fullName + n: name, + pr: prefix, + p: plugin + }; + }; + + function makeConfig(name) { + return function () { + return (config && config.config && config.config[name]) || {}; + }; + } + + handlers = { + require: function (name) { + return makeRequire(name); + }, + exports: function (name) { + var e = defined[name]; + if (typeof e !== 'undefined') { + return e; + } else { + return (defined[name] = {}); + } + }, + module: function (name) { + return { + id: name, + uri: '', + exports: defined[name], + config: makeConfig(name) + }; + } + }; + + main = function (name, deps, callback, relName) { + var cjsModule, depName, ret, map, i, + args = [], + callbackType = typeof callback, + usingExports; + + //Use name if no relName + relName = relName || name; + + //Call the callback to define the module, if necessary. + if (callbackType === 'undefined' || callbackType === 'function') { + //Pull out the defined dependencies and pass the ordered + //values to the callback. + //Default to [require, exports, module] if no deps + deps = !deps.length && callback.length ? ['require', 'exports', 'module'] : deps; + for (i = 0; i < deps.length; i += 1) { + map = makeMap(deps[i], relName); + depName = map.f; + + //Fast path CommonJS standard dependencies. + if (depName === "require") { + args[i] = handlers.require(name); + } else if (depName === "exports") { + //CommonJS module spec 1.1 + args[i] = handlers.exports(name); + usingExports = true; + } else if (depName === "module") { + //CommonJS module spec 1.1 + cjsModule = args[i] = handlers.module(name); + } else if (hasProp(defined, depName) || + hasProp(waiting, depName) || + hasProp(defining, depName)) { + args[i] = callDep(depName); + } else if (map.p) { + map.p.load(map.n, makeRequire(relName, true), makeLoad(depName), {}); + args[i] = defined[depName]; + } else { + throw new Error(name + ' missing ' + depName); + } + } + + ret = callback ? callback.apply(defined[name], args) : undefined; + + if (name) { + //If setting exports via "module" is in play, + //favor that over return value and exports. After that, + //favor a non-undefined return value over exports use. + if (cjsModule && cjsModule.exports !== undef && + cjsModule.exports !== defined[name]) { + defined[name] = cjsModule.exports; + } else if (ret !== undef || !usingExports) { + //Use the return value from the function. + defined[name] = ret; + } + } + } else if (name) { + //May just be an object definition for the module. Only + //worry about defining if have a module name. + defined[name] = callback; + } + }; + + requirejs = require = req = function (deps, callback, relName, forceSync, alt) { + if (typeof deps === "string") { + if (handlers[deps]) { + //callback in this case is really relName + return handlers[deps](callback); + } + //Just return the module wanted. In this scenario, the + //deps arg is the module name, and second arg (if passed) + //is just the relName. + //Normalize module name, if it contains . or .. + return callDep(makeMap(deps, callback).f); + } else if (!deps.splice) { + //deps is a config object, not an array. + config = deps; + if (config.deps) { + req(config.deps, config.callback); + } + if (!callback) { + return; + } + + if (callback.splice) { + //callback is an array, which means it is a dependency list. + //Adjust args if there are dependencies + deps = callback; + callback = relName; + relName = null; + } else { + deps = undef; + } + } + + //Support require(['a']) + callback = callback || function () {}; + + //If relName is a function, it is an errback handler, + //so remove it. + if (typeof relName === 'function') { + relName = forceSync; + forceSync = alt; + } + + //Simulate async callback; + if (forceSync) { + main(undef, deps, callback, relName); + } else { + //Using a non-zero value because of concern for what old browsers + //do, and latest browsers "upgrade" to 4 if lower value is used: + //http://www.whatwg.org/specs/web-apps/current-work/multipage/timers.html#dom-windowtimers-settimeout: + //If want a value immediately, use require('id') instead -- something + //that works in almond on the global level, but not guaranteed and + //unlikely to work in other AMD implementations. + setTimeout(function () { + main(undef, deps, callback, relName); + }, 4); + } + + return req; + }; + + /** + * Just drops the config on the floor, but returns req in case + * the config return value is used. + */ + req.config = function (cfg) { + return req(cfg); + }; + + /** + * Expose module registry for debugging and tooling + */ + requirejs._defined = defined; + + define = function (name, deps, callback) { + if (typeof name !== 'string') { + throw new Error('See almond README: incorrect module build, no module name'); + } + + //This module may not have dependencies + if (!deps.splice) { + //deps is not an array, so probably means + //an object literal or factory function for + //the value. Adjust args. + callback = deps; + deps = []; + } + + if (!hasProp(defined, name) && !hasProp(waiting, name)) { + waiting[name] = [name, deps, callback]; + } + }; + + define.amd = { + jQuery: true + }; +}()); + +define("../lib/almond", function(){}); + +define( 'models/whenModel',[], function() { + var model = Backbone.Model.extend( { + initialize: function( models, options ) { + /* + * If our key or comparator is empty, don't do anything else. + */ + if ( ! this.get( 'key' ) || ! this.get( 'comparator' ) ) return; + + /* + * Our key could be a field or a calc. + * We need to setup a listener on either the field or calc model for changes. + */ + if ( 'calc' == this.get( 'type' ) ) { // We have a calculation key + /* + * Get our calc model + */ + var calcModel = nfRadio.channel( 'form-' + this.collection.options.condition.collection.formModel.get( 'id' ) ).request( 'get:calc', this.get( 'key' ) ); + /* + * When we update our calculation, update our compare + */ + this.listenTo( calcModel, 'change:value', this.updateCalcCompare ); + /* + * Update our compare status. + */ + this.updateCalcCompare( calcModel ); + } else { // We have a field key + // Get our field model + var fieldModel = nfRadio.channel( 'form-' + options.condition.collection.formModel.get( 'id' ) ).request( 'get:fieldByKey', this.get( 'key' ) ); + + if( 'undefined' == typeof fieldModel ) return; + + // When we change the value of our field, update our compare status. + fieldModel.on( 'change:value', this.updateFieldCompare, this ); + // When we keyup in our field, maybe update our compare status. + this.listenTo( nfRadio.channel( 'field-' + fieldModel.get( 'id' ) ), 'keyup:field', this.maybeupdateFieldCompare ); + // Update our compare status. + this.updateFieldCompare( fieldModel ); + + /* + * TODO: This should be moved to the show_field/hide_field file because it is specific to showing and hiding. + * Create a radio message here so that the specific JS file can hook into whenModel init. + */ + fieldModel.on( 'change:visible', this.updateFieldCompare, this ); + } + }, + + updateCalcCompare: function( calcModel ) { + this.updateCompare( calcModel.get( 'value' ) ); + }, + + maybeupdateFieldCompare: function( el, fieldModel, keyCode ) { + if( 'checkbox' == fieldModel.get( 'type' ) ){ + var fieldValue = ( 'checked' == jQuery( el ).attr( 'checked' ) ) ? 1 : 0; + } else if( 'listcheckbox' == fieldModel.get( 'type' ) ) { + // This field isn't a single element, so we need to reference the fieldModel, instead of the DOM. + var fieldValue = fieldModel.get( 'value' ).join(); + } else if ( 'date' == fieldModel.get ('type' ) ) { + var fieldValue = fieldModel.get( 'value' ); + + if ( _.isEmpty( fieldValue ) ) { + fieldValue = '1970/01/01'; + } + + let date_mode = fieldModel.get( 'date_mode' ); + if ( 'undefined' == typeof date_mode ) { // If 'date_mode' is undefined, then we assume it's date_only. + date_mode = 'date_only'; + } + let date = 0; + // If we're in time_only mode, then we need to use 1970-01-01 as our date. + if ( 'time_only' == fieldModel.get( 'date_mode' ) ) { + date = '1970/01/01'; + } else { + date = fieldValue; + } + + // Convert field value into a timestamp + let hour = fieldModel.get( 'selected_hour' ); + if ( 'undefined' == typeof hour ) { + hour = '00'; + } + + let minute = fieldModel.get( 'selected_minute' ); + if ( 'undefined' == typeof minute ) { + minute = '00'; + } + + // If we have a date_and_time field, but we haven't selected a date yet, we don't need to compare. + if ( 'date_and_time' == date_mode && '1970/01/01' == date ) { + fieldValue = false; + } else { + fieldValue = date + ' ' + hour + ':' + minute + ' UT'; + + let dateObject = new Date( fieldValue ); + fieldValue = Math.floor( dateObject.getTime() / 1000 ); + } + } else { + var fieldValue = jQuery( el ).val(); + } + + this.updateFieldCompare( fieldModel, null, fieldValue ); + }, + + updateCompare: function( value ) { + var this_val = this.get( 'value' ); + + // if this is a calcModel then let's convert to number for comparison + if ( 'calc' === this.get( 'type' ) ) { + this_val = Number( this_val ); + value = Number( value ); + } + // Check to see if the value of the field model value COMPARATOR the value of our when condition is true. + var status = this.compareValues[ this.get( 'comparator' ) ]( value, this_val ); + this.set( 'status', status ); + }, + + updateFieldCompare: function( fieldModel, val, fieldValue ) { + if ( _.isEmpty( fieldValue ) ) { + fieldValue = fieldModel.get( 'value' ); + } + + // Change the value of checkboxes to match the new convention. + if( 'checkbox' == fieldModel.get( 'type' ) ) { + if( 0 == fieldValue ) { + fieldValue = 'unchecked'; + } else { + fieldValue = 'checked'; + } + } else if ( 'date' == fieldModel.get( 'type' ) ) { + if ( _.isEmpty( fieldValue ) ) { + fieldValue = '1970/01/01'; + } + + let date_mode = fieldModel.get( 'date_mode' ); + if ( 'undefined' == typeof date_mode ) { // If 'date_mode' is undefined, then we assume it's date_only. + date_mode = 'date_only'; + } + let date = 0; + // If we're in time_only mode, then we need to use 1970-01-01 as our date. + if ( 'time_only' == fieldModel.get( 'date_mode' ) ) { + date = '1970/01/01'; + } else { + date = fieldValue; + } + + // Convert field value into a timestamp + let hour = fieldModel.get( 'selected_hour' ); + if ( 'undefined' == typeof hour ) { + hour = '00'; + } + + let ampm = fieldModel.get( 'selected_ampm' ); + if ( 'undefined' != typeof ampm ) { + // Convert our hour into 24 hr format. + if ( 'pm' == ampm && '12' != hour ) { + hour = parseInt( hour ) + 12; + } else if ( 'am' == ampm && '12' == hour ) { + hour = '00'; + } + } + + let minute = fieldModel.get( 'selected_minute' ); + if ( 'undefined' == typeof minute ) { + minute = '00'; + } + + // If we have a date_and_time field, but we haven't selected a date yet, we don't need to compare. + if ( 'date_and_time' == date_mode && '1970/01/01' == date ) { + fieldValue = false; + } else { + fieldValue = date + ' ' + hour + ':' + minute + ' UT'; + + let dateObject = new Date( fieldValue ); + fieldValue = Math.floor( dateObject.getTime() / 1000 ); + } + } + + this.updateCompare( fieldValue ); + + /* + * TODO: This should be moved to the show_field/hide_field file because it is specific to showing and hiding. + */ + if ( ! fieldModel.get( 'visible' ) ) { + this.set( 'status', false ); + } + }, + + compareValues: { + 'equal': function( a, b ) { + return a == b; + }, + 'notequal': function( a, b ) { + return a != b; + }, + 'contains': function( a, b ) { + if ( jQuery.isArray( a ) ) { + /* + * If a is an array, then we're searching for an index. + */ + return a.indexOf( b ) >= 0; + } else { + /* + * If a is a string, then we're searching for a string position. + * + * If our b value has quotes in it, we want to find that exact word or phrase. + */ + if ( b.indexOf( '"' ) >= 0 ) { + b = b.replace( /['"]+/g, '' ); + return new RegExp("\\b" + b + "\\b").test( a ); + } + return a.toLowerCase().indexOf( b.toLowerCase() ) >= 0; + } + }, + 'notcontains': function( a, b ) { + return ! this.contains( a, b ); + }, + 'greater': function( a, b ) { + /* + * In 2.9.x, you could use the greater and less like string count. + * i.e. if textbox > (empty string) do something. + * This recreates that ability. + */ + if ( jQuery.isNumeric( b ) ) { + return parseFloat( a ) > parseFloat( b ); + } else if ( 'string' == typeof a ) { + return 0 < a.length; + } + + }, + 'less': function( a, b ) { + /* + * In 2.9.x, you could use the greater and less like string count. + * i.e. if textbox > (empty string) do something. + * This recreates that ability. + */ + if ( jQuery.isNumeric( b ) ) { + return parseFloat( a ) < parseFloat( b ); + } else if ( 'string' == typeof a ) { + return 0 >= a.length; + } + + }, + 'greaterequal': function( a, b ) { + return parseFloat( a ) > parseFloat( b ) || parseFloat( a ) == parseFloat( b ); + }, + 'lessequal': function( a, b ) { + return parseFloat( a ) < parseFloat( b ) || parseFloat( a ) == parseFloat( b ); + } + } + } ); + + return model; +} ); +define( 'models/whenCollection',['models/whenModel'], function( WhenModel ) { + var collection = Backbone.Collection.extend( { + model: WhenModel, + + initialize: function( models, options ) { + this.options = options; + } + } ); + return collection; +} ); +define( 'models/conditionModel',[ 'models/whenCollection' ], function( WhenCollection ) { + var model = Backbone.Model.extend( { + initialize: function( options ) { + /* + * Our "when" statement will be like: + * When field1 == value + * AND field2 == value + * + * We need to create a collection out of this when statement, with each row as a model. + */ + this.set( 'when', new WhenCollection( this.get( 'when' ), { condition: this } ) ); + /* + * When we update any of our "when" models' status, check to see if we should send a message. + */ + this.get( 'when' ).on( 'change:status', this.checkWhen, this ); + /* + * Check our initial status; + */ + this.checkWhen(); + }, + + checkWhen: function() { + /* + * If we have any OR connectors, then any status being true should trigger pass. + * Otherwise, we need every status to be true. + */ + var statusResults = this.get( 'when' ).pluck( 'status' ); + + var connectors = this.get( 'when' ).pluck( 'connector' ); + var allAND = _.every( _.values( connectors ), function( v ) { return v == 'AND' }, this ); + if ( allAND ) { + var status = _.every( _.values( statusResults ), function(v) { return v; }, this ); + } else { + var status = _.some( _.values( statusResults ), function(v) { return v; }, this ); + } + + if ( status ) { + /* + * Send out a request for each of our "then" statements. + */ + _.each( this.get( 'then' ), function( then, index ) { + nfRadio.channel( 'condition:trigger' ).request( then.trigger, this, then ); + }, this ); + } else { + /* + * Send out a request for each of our "else" statements. + */ + _.each( this.get( 'else' ), function( elseData, index ) { + nfRadio.channel( 'condition:trigger' ).request( elseData.trigger, this, elseData ); + }, this ); + } + } + } ); + + return model; +} ); +define( 'models/conditionCollection',['models/conditionModel'], function( ConditionModel ) { + var collection = Backbone.Collection.extend( { + model: ConditionModel, + + initialize: function( models, options ) { + this.formModel = options.formModel; + } + } ); + return collection; +} ); +/** + * Initialise condition collection + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( 'controllers/initCollection',[ 'models/conditionCollection' ], function( ConditionCollection ) { + var controller = Marionette.Object.extend( { + initialize: function( formModel ) { + this.collection = new ConditionCollection( formModel.get( 'conditions' ), { formModel: formModel } ); + this.listenTo(nfRadio.channel('fields'), 'reset:collection', this.resetCollection); + }, + resetCollection: function( fieldsCollection ) { + var formModel = fieldsCollection.options.formModel; + this.collection = new ConditionCollection( formModel.get( 'conditions' ), { formModel: formModel } ); + } + }); + + return controller; +} ); +/** + * Handle showing/hiding fields + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( 'controllers/showHide',[], function() { + var controller = Marionette.Object.extend( { + initialize: function() { + nfRadio.channel( 'condition:trigger' ).reply( 'hide_field', this.hideField, this ); + nfRadio.channel( 'condition:trigger' ).reply( 'show_field', this.showField, this ); + }, + + hideField: function( conditionModel, then ) { + var targetFieldModel = nfRadio.channel( 'form-' + conditionModel.collection.formModel.get( 'id' ) ).request( 'get:fieldByKey', then.key ); + + if( 'undefined' == typeof targetFieldModel ) return; + targetFieldModel.set( 'visible', false ); + if ( ! targetFieldModel.get( 'clean' ) ) { + targetFieldModel.trigger( 'change:value', targetFieldModel ); + } + + nfRadio.channel( 'fields' ).request( 'remove:error', targetFieldModel.get( 'id' ), 'required-error' ); + }, + + showField: function( conditionModel, then ) { + var targetFieldModel = nfRadio.channel( 'form-' + conditionModel.collection.formModel.get( 'id' ) ).request( 'get:fieldByKey', then.key ); + //TODO: Add an error to let the user know the show/hide field is empty. + if( 'undefined' == typeof targetFieldModel ) return; + targetFieldModel.set( 'visible', true ); + if ( ! targetFieldModel.get( 'clean' ) ) { + targetFieldModel.trigger( 'change:value', targetFieldModel ); + } + if ( 'recaptcha' === targetFieldModel.get( 'type' ) ) { + this.renderRecaptcha(); + } + var viewEl = { el: nfRadio.channel( 'form-' + conditionModel.collection.formModel.get( 'id' ) ).request( 'get:el' ) }; + nfRadio.channel( 'form' ).request( 'init:help', viewEl ); + }, + + renderRecaptcha: function() { + jQuery( '.g-recaptcha' ).each( function() { + var callback = jQuery( this ).data( 'callback' ); + var fieldID = jQuery( this ).data( 'fieldid' ); + if ( typeof window[ callback ] !== 'function' ){ + window[ callback ] = function( response ) { + nfRadio.channel( 'recaptcha' ).request( 'update:response', response, fieldID ); + }; + } + var opts = { + theme: jQuery( this ).data( 'theme' ), + sitekey: jQuery( this ).data( 'sitekey' ), + callback: callback + }; + + grecaptcha.render( jQuery( this )[0], opts ); + } ); + } + }); + + return controller; +} ); +/** + * Setting/unsetting required. + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2019 WP Ninjas + * @since 3.0 + */ +define( 'controllers/changeRequired',[], function() { + var controller = Marionette.Object.extend( { + initialize: function() { + nfRadio.channel( 'condition:trigger' ).reply( 'set_required', this.setRequired, this ); + nfRadio.channel( 'condition:trigger' ).reply( 'unset_required', this.unsetRequired, this ); + }, + + setRequired: function( conditionModel, then ) { + var targetFieldModel = nfRadio.channel( 'form-' + conditionModel.collection.formModel.get( 'id' ) ).request( 'get:fieldByKey', then.key ); + + if( 'undefined' == typeof targetFieldModel ) return; + targetFieldModel.set( 'required', 1 ); + targetFieldModel.trigger( 'reRender', targetFieldModel ); + }, + + unsetRequired: function( conditionModel, then ) { + var targetFieldModel = nfRadio.channel( 'form-' + conditionModel.collection.formModel.get( 'id' ) ).request( 'get:fieldByKey', then.key ); + + if( 'undefined' == typeof targetFieldModel ) return; + targetFieldModel.set( 'required', 0 ); + targetFieldModel.trigger( 'reRender', targetFieldModel ); + // Ensure we resolve any errors when the field is no longer required. + nfRadio.channel( 'fields' ).request( 'remove:error', targetFieldModel.get( 'id' ), 'required-error' ); + } + + }); + + return controller; +} ); +/** + * Handle adding or removing an option from our list + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( 'controllers/showHideOption',[], function() { + var controller = Marionette.Object.extend( { + initialize: function() { + nfRadio.channel( 'condition:trigger' ).reply( 'show_option', this.showOption, this ); + + nfRadio.channel( 'condition:trigger' ).reply( 'hide_option', this.hideOption, this ); + }, + + showOption: function( conditionModel, then ) { + var option = this.getOption( conditionModel, then ); + option.visible = true; + this.updateFieldModel( conditionModel, then ); + }, + + hideOption: function( conditionModel, then ) { + var option = this.getOption( conditionModel, then ); + option.visible = false; + this.updateFieldModel( conditionModel, then ); + }, + + getFieldModel: function( conditionModel, then ) { + return nfRadio.channel( 'form-' + conditionModel.collection.formModel.get( 'id' ) ).request( 'get:fieldByKey', then.key ); + }, + + getOption: function( conditionModel, then ) { + var targetFieldModel = this.getFieldModel( conditionModel, then ); + var options = targetFieldModel.get( 'options' ); + return _.find( options, function( option ) { return option.value == then.value } ); + }, + + updateFieldModel: function( conditionModel, then ) { + var targetFieldModel = this.getFieldModel( conditionModel, then ); + var options = targetFieldModel.get( 'options' ); + targetFieldModel.set( 'options', options ); + targetFieldModel.trigger( 'reRender' ); + } + }); + + return controller; +} ); +/** + * Handle changing a field's value + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( 'controllers/changeValue',[], function() { + var controller = Marionette.Object.extend( { + initialize: function() { + nfRadio.channel( 'condition:trigger' ).reply( 'change_value', this.changeValue, this ); + }, + + changeValue: function( conditionModel, then ) { + var targetFieldModel = nfRadio.channel( 'form-' + conditionModel.collection.formModel.get( 'id' ) ).request( 'get:fieldByKey', then.key ); + /* + * If we have a checkbox then we need to change the value that is set + * of the then variable to a 1 or 0 to re-render on the front end when + * the condition is met. + */ + if( 'checkbox' == targetFieldModel.get( 'type' ) ) { + // We also need to do the opposite of the value that is in the changed model. + if( 'unchecked' == targetFieldModel.changed.value ) { + then.value = 1; + } else if( 'checked' == targetFieldModel ) { + then.value = 0; + } + } + /* + * Change the value of our field model, and then trigger a re-render of its view. + */ + targetFieldModel.set( 'value', then.value ); + targetFieldModel.trigger( 'reRender', targetFieldModel ); + }, + + }); + return controller; +} ); +/** + * Handle selecting/deselecting list options + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( 'controllers/selectDeselect',[], function() { + var controller = Marionette.Object.extend( { + initialize: function() { + nfRadio.channel( 'condition:trigger' ).reply( 'select_option', this.selectOption, this ); + + nfRadio.channel( 'condition:trigger' ).reply( 'deselect_option', this.deselectOption, this ); + }, + + selectOption: function( conditionModel, then ) { + /* + * Get our field model and set this option's "selected" property to 1 + */ + var targetFieldModel = nfRadio.channel( 'form-' + conditionModel.collection.formModel.get( 'id' ) ).request( 'get:fieldByKey', then.key ); + + + if( _.contains( [ 'listselect', 'listcountry', 'liststate' ], targetFieldModel.get( 'type' ) ) ) { // TODO: Make this more dynamic. + targetFieldModel.set('clean', false); // Allows for changes to default values. + } + + var options = targetFieldModel.get( 'options' ); + + var option = _.find( options, { value: then.value } ); + option.selected = 1; + + targetFieldModel.set( 'options', options ); + + if( _.contains( [ 'listselect', 'listcountry', 'liststate' ], targetFieldModel.get( 'type' ) ) ) { // TODO: Make this more dynamic. + targetFieldModel.set( 'value', option.value ); // Propagate the selected option to the model's value. + } else { + var value = targetFieldModel.get( 'value' ); + if ( _.isArray( value ) ) { + if ( 0 > value.indexOf( option.value ) ) { + value.push( option.value ); + // Set the value to nothing so it knows that something has changed. + targetFieldModel.set( 'value', '' ); + } + } else { + value = option.value; + } + + targetFieldModel.set( 'value', value ); // Propagate the selected option to the model's value. + } + + /* + * Re render our field + */ + targetFieldModel.trigger( 'reRender', targetFieldModel ); + }, + + deselectOption: function( conditionModel, then ) { + /* + * When we are trying to deselect our option, we need to change it's "selected" property to 0 AND change its value. + */ + var targetFieldModel = nfRadio.channel( 'form-' + conditionModel.collection.formModel.get( 'id' ) ).request( 'get:fieldByKey', then.key ); + + /* + * Set "selected" to 0. + */ + var options = targetFieldModel.get( 'options' ); + var option = _.find( options, { value: then.value } ); + option.selected = 0; + targetFieldModel.set( 'options', options ); + + /* + * Update our value + */ + var currentValue = targetFieldModel.get( 'value' ); + if ( _.isArray( currentValue ) ) { + currentValue = _.without( currentValue, then.value ); + } else { + currentValue = ''; + } + targetFieldModel.set( 'value', currentValue ); + + /* + * Re render our field + */ + targetFieldModel.trigger( 'reRender', targetFieldModel ); + }, + + }); + + return controller; +} ); +/** + * Keep an internal record for which actions are active and deactive. + * + * @package Ninja Forms Conditional Logic + * @copyright (c) 2016 WP Ninjas + * @since 3.0 + */ +define( 'controllers/actions',[], function() { + var controller = Marionette.Object.extend( { + actions: {}, + + initialize: function() { + /* + * Listen for activate/deactivate action messages. + */ + nfRadio.channel( 'condition:trigger' ).reply( 'activate_action', this.activateAction, this ); + nfRadio.channel( 'condition:trigger' ).reply( 'deactivate_action', this.deactivateAction, this ); + + /* + * Reply to requests for action status. + */ + nfRadio.channel( 'actions' ).reply( 'get:status', this.getStatus, this ); + }, + + activateAction: function( conditionModel, thenObject ) { + this.actions[ thenObject.key ] = true; + console.log( 'activate action' ); + }, + + deactivateAction: function( conditionModel, thenObject ) { + console.log( 'deactivate action' ); + this.actions[ thenObject.key ] = false; + }, + + getStatus: function( $id ) { + return this.actions[ $id ]; + } + }); + + return controller; +} ); +var nfRadio = Backbone.Radio; + +require( [ 'controllers/initCollection', 'controllers/showHide', 'controllers/changeRequired', 'controllers/showHideOption', 'controllers/changeValue', 'controllers/selectDeselect', 'controllers/actions' ], function( InitCollection, ShowHide, ChangeRequired, ShowHideOption, ChangeValue, SelectDeselect, Actions ) { + + var NFConditionalLogic = Marionette.Application.extend( { + + initialize: function( options ) { + this.listenTo( nfRadio.channel( 'form' ), 'after:loaded', this.loadControllers ); + }, + + loadControllers: function( formModel ) { + new ShowHide(); + new ChangeRequired(); + new ShowHideOption(); + new ChangeValue(); + new SelectDeselect(); + new Actions(); + new InitCollection( formModel ); + }, + + onStart: function() { + + } + } ); + + var nfConditionalLogic = new NFConditionalLogic(); + nfConditionalLogic.start(); +} ); +define("main", function(){}); + +}()); +//# sourceMappingURL=front-end.js.map diff --git a/assets/js/min/front-end.js.map b/assets/js/min/front-end.js.map new file mode 100644 index 0000000..b31013b --- /dev/null +++ b/assets/js/min/front-end.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["../../../config-wrap-start-default.js","../lib/almond.js","models/whenModel.js","models/whenCollection.js","models/conditionModel.js","models/conditionCollection.js","controllers/initCollection.js","controllers/showHide.js","controllers/changeRequired.js","controllers/showHideOption.js","controllers/changeValue.js","controllers/selectDeselect.js","controllers/actions.js","main.js","../../../config-wrap-end-default.js"],"names":[],"mappings":"AAAA;AACA,ACDA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,ACjbA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,AC5PA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,ACVA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,ACxDA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,ACVA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,ACrBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,AC/DA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,ACpCA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,AC/CA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,ACtCA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,AC3FA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,ACzCA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,AC9BA","file":"front-end.js","sourcesContent":["(function () {\n","/**\n * @license almond 0.3.1 Copyright (c) 2011-2014, The Dojo Foundation All Rights Reserved.\n * Available via the MIT or new BSD license.\n * see: http://github.com/jrburke/almond for details\n */\n//Going sloppy to avoid 'use strict' string cost, but strict practices should\n//be followed.\n/*jslint sloppy: true */\n/*global setTimeout: false */\n\nvar requirejs, require, define;\n(function (undef) {\n var main, req, makeMap, handlers,\n defined = {},\n waiting = {},\n config = {},\n defining = {},\n hasOwn = Object.prototype.hasOwnProperty,\n aps = [].slice,\n jsSuffixRegExp = /\\.js$/;\n\n function hasProp(obj, prop) {\n return hasOwn.call(obj, prop);\n }\n\n /**\n * Given a relative module name, like ./something, normalize it to\n * a real name that can be mapped to a path.\n * @param {String} name the relative name\n * @param {String} baseName a real name that the name arg is relative\n * to.\n * @returns {String} normalized name\n */\n function normalize(name, baseName) {\n var nameParts, nameSegment, mapValue, foundMap, lastIndex,\n foundI, foundStarMap, starI, i, j, part,\n baseParts = baseName && baseName.split(\"/\"),\n map = config.map,\n starMap = (map && map['*']) || {};\n\n //Adjust any relative paths.\n if (name && name.charAt(0) === \".\") {\n //If have a base name, try to normalize against it,\n //otherwise, assume it is a top-level require that will\n //be relative to baseUrl in the end.\n if (baseName) {\n name = name.split('/');\n lastIndex = name.length - 1;\n\n // Node .js allowance:\n if (config.nodeIdCompat && jsSuffixRegExp.test(name[lastIndex])) {\n name[lastIndex] = name[lastIndex].replace(jsSuffixRegExp, '');\n }\n\n //Lop off the last part of baseParts, so that . matches the\n //\"directory\" and not name of the baseName's module. For instance,\n //baseName of \"one/two/three\", maps to \"one/two/three.js\", but we\n //want the directory, \"one/two\" for this normalization.\n name = baseParts.slice(0, baseParts.length - 1).concat(name);\n\n //start trimDots\n for (i = 0; i < name.length; i += 1) {\n part = name[i];\n if (part === \".\") {\n name.splice(i, 1);\n i -= 1;\n } else if (part === \"..\") {\n if (i === 1 && (name[2] === '..' || name[0] === '..')) {\n //End of the line. Keep at least one non-dot\n //path segment at the front so it can be mapped\n //correctly to disk. Otherwise, there is likely\n //no path mapping for a path starting with '..'.\n //This can still fail, but catches the most reasonable\n //uses of ..\n break;\n } else if (i > 0) {\n name.splice(i - 1, 2);\n i -= 2;\n }\n }\n }\n //end trimDots\n\n name = name.join(\"/\");\n } else if (name.indexOf('./') === 0) {\n // No baseName, so this is ID is resolved relative\n // to baseUrl, pull off the leading dot.\n name = name.substring(2);\n }\n }\n\n //Apply map config if available.\n if ((baseParts || starMap) && map) {\n nameParts = name.split('/');\n\n for (i = nameParts.length; i > 0; i -= 1) {\n nameSegment = nameParts.slice(0, i).join(\"/\");\n\n if (baseParts) {\n //Find the longest baseName segment match in the config.\n //So, do joins on the biggest to smallest lengths of baseParts.\n for (j = baseParts.length; j > 0; j -= 1) {\n mapValue = map[baseParts.slice(0, j).join('/')];\n\n //baseName segment has config, find if it has one for\n //this name.\n if (mapValue) {\n mapValue = mapValue[nameSegment];\n if (mapValue) {\n //Match, update name to the new value.\n foundMap = mapValue;\n foundI = i;\n break;\n }\n }\n }\n }\n\n if (foundMap) {\n break;\n }\n\n //Check for a star map match, but just hold on to it,\n //if there is a shorter segment match later in a matching\n //config, then favor over this star map.\n if (!foundStarMap && starMap && starMap[nameSegment]) {\n foundStarMap = starMap[nameSegment];\n starI = i;\n }\n }\n\n if (!foundMap && foundStarMap) {\n foundMap = foundStarMap;\n foundI = starI;\n }\n\n if (foundMap) {\n nameParts.splice(0, foundI, foundMap);\n name = nameParts.join('/');\n }\n }\n\n return name;\n }\n\n function makeRequire(relName, forceSync) {\n return function () {\n //A version of a require function that passes a moduleName\n //value for items that may need to\n //look up paths relative to the moduleName\n var args = aps.call(arguments, 0);\n\n //If first arg is not require('string'), and there is only\n //one arg, it is the array form without a callback. Insert\n //a null so that the following concat is correct.\n if (typeof args[0] !== 'string' && args.length === 1) {\n args.push(null);\n }\n return req.apply(undef, args.concat([relName, forceSync]));\n };\n }\n\n function makeNormalize(relName) {\n return function (name) {\n return normalize(name, relName);\n };\n }\n\n function makeLoad(depName) {\n return function (value) {\n defined[depName] = value;\n };\n }\n\n function callDep(name) {\n if (hasProp(waiting, name)) {\n var args = waiting[name];\n delete waiting[name];\n defining[name] = true;\n main.apply(undef, args);\n }\n\n if (!hasProp(defined, name) && !hasProp(defining, name)) {\n throw new Error('No ' + name);\n }\n return defined[name];\n }\n\n //Turns a plugin!resource to [plugin, resource]\n //with the plugin being undefined if the name\n //did not have a plugin prefix.\n function splitPrefix(name) {\n var prefix,\n index = name ? name.indexOf('!') : -1;\n if (index > -1) {\n prefix = name.substring(0, index);\n name = name.substring(index + 1, name.length);\n }\n return [prefix, name];\n }\n\n /**\n * Makes a name map, normalizing the name, and using a plugin\n * for normalization if necessary. Grabs a ref to plugin\n * too, as an optimization.\n */\n makeMap = function (name, relName) {\n var plugin,\n parts = splitPrefix(name),\n prefix = parts[0];\n\n name = parts[1];\n\n if (prefix) {\n prefix = normalize(prefix, relName);\n plugin = callDep(prefix);\n }\n\n //Normalize according\n if (prefix) {\n if (plugin && plugin.normalize) {\n name = plugin.normalize(name, makeNormalize(relName));\n } else {\n name = normalize(name, relName);\n }\n } else {\n name = normalize(name, relName);\n parts = splitPrefix(name);\n prefix = parts[0];\n name = parts[1];\n if (prefix) {\n plugin = callDep(prefix);\n }\n }\n\n //Using ridiculous property names for space reasons\n return {\n f: prefix ? prefix + '!' + name : name, //fullName\n n: name,\n pr: prefix,\n p: plugin\n };\n };\n\n function makeConfig(name) {\n return function () {\n return (config && config.config && config.config[name]) || {};\n };\n }\n\n handlers = {\n require: function (name) {\n return makeRequire(name);\n },\n exports: function (name) {\n var e = defined[name];\n if (typeof e !== 'undefined') {\n return e;\n } else {\n return (defined[name] = {});\n }\n },\n module: function (name) {\n return {\n id: name,\n uri: '',\n exports: defined[name],\n config: makeConfig(name)\n };\n }\n };\n\n main = function (name, deps, callback, relName) {\n var cjsModule, depName, ret, map, i,\n args = [],\n callbackType = typeof callback,\n usingExports;\n\n //Use name if no relName\n relName = relName || name;\n\n //Call the callback to define the module, if necessary.\n if (callbackType === 'undefined' || callbackType === 'function') {\n //Pull out the defined dependencies and pass the ordered\n //values to the callback.\n //Default to [require, exports, module] if no deps\n deps = !deps.length && callback.length ? ['require', 'exports', 'module'] : deps;\n for (i = 0; i < deps.length; i += 1) {\n map = makeMap(deps[i], relName);\n depName = map.f;\n\n //Fast path CommonJS standard dependencies.\n if (depName === \"require\") {\n args[i] = handlers.require(name);\n } else if (depName === \"exports\") {\n //CommonJS module spec 1.1\n args[i] = handlers.exports(name);\n usingExports = true;\n } else if (depName === \"module\") {\n //CommonJS module spec 1.1\n cjsModule = args[i] = handlers.module(name);\n } else if (hasProp(defined, depName) ||\n hasProp(waiting, depName) ||\n hasProp(defining, depName)) {\n args[i] = callDep(depName);\n } else if (map.p) {\n map.p.load(map.n, makeRequire(relName, true), makeLoad(depName), {});\n args[i] = defined[depName];\n } else {\n throw new Error(name + ' missing ' + depName);\n }\n }\n\n ret = callback ? callback.apply(defined[name], args) : undefined;\n\n if (name) {\n //If setting exports via \"module\" is in play,\n //favor that over return value and exports. After that,\n //favor a non-undefined return value over exports use.\n if (cjsModule && cjsModule.exports !== undef &&\n cjsModule.exports !== defined[name]) {\n defined[name] = cjsModule.exports;\n } else if (ret !== undef || !usingExports) {\n //Use the return value from the function.\n defined[name] = ret;\n }\n }\n } else if (name) {\n //May just be an object definition for the module. Only\n //worry about defining if have a module name.\n defined[name] = callback;\n }\n };\n\n requirejs = require = req = function (deps, callback, relName, forceSync, alt) {\n if (typeof deps === \"string\") {\n if (handlers[deps]) {\n //callback in this case is really relName\n return handlers[deps](callback);\n }\n //Just return the module wanted. In this scenario, the\n //deps arg is the module name, and second arg (if passed)\n //is just the relName.\n //Normalize module name, if it contains . or ..\n return callDep(makeMap(deps, callback).f);\n } else if (!deps.splice) {\n //deps is a config object, not an array.\n config = deps;\n if (config.deps) {\n req(config.deps, config.callback);\n }\n if (!callback) {\n return;\n }\n\n if (callback.splice) {\n //callback is an array, which means it is a dependency list.\n //Adjust args if there are dependencies\n deps = callback;\n callback = relName;\n relName = null;\n } else {\n deps = undef;\n }\n }\n\n //Support require(['a'])\n callback = callback || function () {};\n\n //If relName is a function, it is an errback handler,\n //so remove it.\n if (typeof relName === 'function') {\n relName = forceSync;\n forceSync = alt;\n }\n\n //Simulate async callback;\n if (forceSync) {\n main(undef, deps, callback, relName);\n } else {\n //Using a non-zero value because of concern for what old browsers\n //do, and latest browsers \"upgrade\" to 4 if lower value is used:\n //http://www.whatwg.org/specs/web-apps/current-work/multipage/timers.html#dom-windowtimers-settimeout:\n //If want a value immediately, use require('id') instead -- something\n //that works in almond on the global level, but not guaranteed and\n //unlikely to work in other AMD implementations.\n setTimeout(function () {\n main(undef, deps, callback, relName);\n }, 4);\n }\n\n return req;\n };\n\n /**\n * Just drops the config on the floor, but returns req in case\n * the config return value is used.\n */\n req.config = function (cfg) {\n return req(cfg);\n };\n\n /**\n * Expose module registry for debugging and tooling\n */\n requirejs._defined = defined;\n\n define = function (name, deps, callback) {\n if (typeof name !== 'string') {\n throw new Error('See almond README: incorrect module build, no module name');\n }\n\n //This module may not have dependencies\n if (!deps.splice) {\n //deps is not an array, so probably means\n //an object literal or factory function for\n //the value. Adjust args.\n callback = deps;\n deps = [];\n }\n\n if (!hasProp(defined, name) && !hasProp(waiting, name)) {\n waiting[name] = [name, deps, callback];\n }\n };\n\n define.amd = {\n jQuery: true\n };\n}());\n\ndefine(\"../lib/almond\", function(){});\n\n","define( 'models/whenModel',[], function() {\n\tvar model = Backbone.Model.extend( {\n\t\tinitialize: function( models, options ) {\n\t\t\t/*\n\t\t\t * If our key or comparator is empty, don't do anything else.\n\t\t\t */\n\t\t\tif ( ! this.get( 'key' ) || ! this.get( 'comparator' ) ) return;\n\n\t\t\t/*\n\t\t\t * Our key could be a field or a calc.\n\t\t\t * We need to setup a listener on either the field or calc model for changes.\n\t\t\t */\n\t\t\tif ( 'calc' == this.get( 'type' ) ) { // We have a calculation key\n\t\t\t\t/*\n\t\t\t\t * Get our calc model\n\t\t\t\t */\n\t\t\t\tvar calcModel = nfRadio.channel( 'form-' + this.collection.options.condition.collection.formModel.get( 'id' ) ).request( 'get:calc', this.get( 'key' ) );\n\t\t\t\t/*\n\t\t\t\t * When we update our calculation, update our compare\n\t\t\t\t */\n\t\t\t\tthis.listenTo( calcModel, 'change:value', this.updateCalcCompare );\n\t\t\t\t/*\n\t\t\t\t * Update our compare status.\n\t\t\t\t */\n\t\t\t\tthis.updateCalcCompare( calcModel );\n\t\t\t} else { // We have a field key\n\t\t\t\t// Get our field model\n\t\t\t\tvar fieldModel = nfRadio.channel( 'form-' + options.condition.collection.formModel.get( 'id' ) ).request( 'get:fieldByKey', this.get( 'key' ) );\n\n\t\t\t\tif( 'undefined' == typeof fieldModel ) return;\n\n\t\t\t\t// When we change the value of our field, update our compare status.\n\t\t\t\tfieldModel.on( 'change:value', this.updateFieldCompare, this );\n\t\t\t\t// When we keyup in our field, maybe update our compare status.\n\t\t\t\tthis.listenTo( nfRadio.channel( 'field-' + fieldModel.get( 'id' ) ), 'keyup:field', this.maybeupdateFieldCompare );\n\t\t\t\t// Update our compare status.\n\t\t\t\tthis.updateFieldCompare( fieldModel );\n\n\t\t\t\t/*\n\t\t\t\t * TODO: This should be moved to the show_field/hide_field file because it is specific to showing and hiding.\n\t\t\t\t * Create a radio message here so that the specific JS file can hook into whenModel init.\n\t\t\t\t */\n\t\t\t\tfieldModel.on( 'change:visible', this.updateFieldCompare, this );\n\t\t\t}\n\t\t},\n\n\t\tupdateCalcCompare: function( calcModel ) {\n\t\t\tthis.updateCompare( calcModel.get( 'value' ) );\n\t\t},\n\n\t\tmaybeupdateFieldCompare: function( el, fieldModel, keyCode ) {\n\t\t\tif( 'checkbox' == fieldModel.get( 'type' ) ){\n var fieldValue = ( 'checked' == jQuery( el ).attr( 'checked' ) ) ? 1 : 0;\n } else if( 'listcheckbox' == fieldModel.get( 'type' ) ) {\n\t\t\t\t// This field isn't a single element, so we need to reference the fieldModel, instead of the DOM.\n var fieldValue = fieldModel.get( 'value' ).join();\n } else if ( 'date' == fieldModel.get ('type' ) ) {\n\t\t\t\tvar fieldValue = fieldModel.get( 'value' );\n\n\t\t\t\tif ( _.isEmpty( fieldValue ) ) {\n\t\t\t\t\tfieldValue = '1970/01/01';\n\t\t\t\t}\n\n\t\t\t\tlet date_mode = fieldModel.get( 'date_mode' );\n\t\t\t\tif ( 'undefined' == typeof date_mode ) { // If 'date_mode' is undefined, then we assume it's date_only.\n\t\t\t\t\tdate_mode = 'date_only';\n\t\t\t\t}\n\t\t\t\tlet date = 0;\n\t\t\t\t// If we're in time_only mode, then we need to use 1970-01-01 as our date.\n\t\t\t\tif ( 'time_only' == fieldModel.get( 'date_mode' ) ) {\n\t\t\t\t\tdate = '1970/01/01';\n\t\t\t\t} else {\n\t\t\t\t\tdate = fieldValue;\n\t\t\t\t}\n\n\t\t\t\t// Convert field value into a timestamp\n\t\t\t\tlet hour = fieldModel.get( 'selected_hour' );\n\t\t\t\tif ( 'undefined' == typeof hour ) {\n\t\t\t\t\thour = '00';\n\t\t\t\t}\n\n\t\t\t\tlet minute = fieldModel.get( 'selected_minute' );\n\t\t\t\tif ( 'undefined' == typeof minute ) {\n\t\t\t\t\tminute = '00';\n\t\t\t\t}\n\n\t\t\t\t// If we have a date_and_time field, but we haven't selected a date yet, we don't need to compare.\n\t\t\t\tif ( 'date_and_time' == date_mode && '1970/01/01' == date ) {\n\t\t\t\t\tfieldValue = false;\n\t\t\t\t} else {\n\t\t\t\t\tfieldValue = date + ' ' + hour + ':' + minute + ' UT';\n\n\t\t\t\t\tlet dateObject = new Date( fieldValue );\n\t\t\t\t\tfieldValue = Math.floor( dateObject.getTime() / 1000 );\t\t\t\t\t\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tvar fieldValue = jQuery( el ).val();\n\t\t\t}\n\n\t\t\tthis.updateFieldCompare( fieldModel, null, fieldValue );\n\t\t},\n\n\t\tupdateCompare: function( value ) {\n\t\t\tvar this_val = this.get( 'value' );\n\n\t\t\t// if this is a calcModel then let's convert to number for comparison\n\t\t\tif ( 'calc' === this.get( 'type' ) ) {\n\t\t\t\tthis_val = Number( this_val );\n\t\t\t\tvalue = Number( value );\n\t\t\t}\n\t\t\t// Check to see if the value of the field model value COMPARATOR the value of our when condition is true.\n\t\t\tvar status = this.compareValues[ this.get( 'comparator' ) ]( value, this_val );\n\t\t\tthis.set( 'status', status );\n\t\t},\n\n\t\tupdateFieldCompare: function( fieldModel, val, fieldValue ) {\n\t\t\tif ( _.isEmpty( fieldValue ) ) {\n\t\t\t\tfieldValue = fieldModel.get( 'value' );\n\t\t\t}\n\n\t\t\t// Change the value of checkboxes to match the new convention.\n\t\t\tif( 'checkbox' == fieldModel.get( 'type' ) ) {\n\t\t\t\tif( 0 == fieldValue ) {\n\t\t\t\t\tfieldValue = 'unchecked';\n\t\t\t\t} else {\n\t\t\t\t\tfieldValue = 'checked';\n\t\t\t\t}\n\t\t\t} else if ( 'date' == fieldModel.get( 'type' ) ) {\n\t\t\t\tif ( _.isEmpty( fieldValue ) ) {\n\t\t\t\t\tfieldValue = '1970/01/01';\n\t\t\t\t}\n\n\t\t\t\tlet date_mode = fieldModel.get( 'date_mode' );\n\t\t\t\tif ( 'undefined' == typeof date_mode ) { // If 'date_mode' is undefined, then we assume it's date_only.\n\t\t\t\t\tdate_mode = 'date_only';\n\t\t\t\t}\n\t\t\t\tlet date = 0;\n\t\t\t\t// If we're in time_only mode, then we need to use 1970-01-01 as our date.\n\t\t\t\tif ( 'time_only' == fieldModel.get( 'date_mode' ) ) {\n\t\t\t\t\tdate = '1970/01/01';\n\t\t\t\t} else {\n\t\t\t\t\tdate = fieldValue;\n\t\t\t\t}\n\n\t\t\t\t// Convert field value into a timestamp\n\t\t\t\tlet hour = fieldModel.get( 'selected_hour' );\n\t\t\t\tif ( 'undefined' == typeof hour ) {\n\t\t\t\t\thour = '00';\n\t\t\t\t}\n\n\t\t\t\tlet ampm = fieldModel.get( 'selected_ampm' );\n\t\t\t\tif ( 'undefined' != typeof ampm ) {\n\t\t\t\t\t// Convert our hour into 24 hr format.\n\t\t\t\t\tif ( 'pm' == ampm && '12' != hour ) {\n\t\t\t\t\t\thour = parseInt( hour ) + 12;\n\t\t\t\t\t} else if ( 'am' == ampm && '12' == hour ) {\n\t\t\t\t\t\thour = '00';\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tlet minute = fieldModel.get( 'selected_minute' );\n\t\t\t\tif ( 'undefined' == typeof minute ) {\n\t\t\t\t\tminute = '00';\n\t\t\t\t}\n\n\t\t\t\t// If we have a date_and_time field, but we haven't selected a date yet, we don't need to compare.\n\t\t\t\tif ( 'date_and_time' == date_mode && '1970/01/01' == date ) {\n\t\t\t\t\tfieldValue = false;\n\t\t\t\t} else {\n\t\t\t\t\tfieldValue = date + ' ' + hour + ':' + minute + ' UT';\n\n\t\t\t\t\tlet dateObject = new Date( fieldValue );\n\t\t\t\t\tfieldValue = Math.floor( dateObject.getTime() / 1000 );\t\t\t\t\t\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis.updateCompare( fieldValue );\n\n\t\t\t/*\n\t\t\t * TODO: This should be moved to the show_field/hide_field file because it is specific to showing and hiding.\n\t\t\t */\n\t\t\tif ( ! fieldModel.get( 'visible' ) ) {\n\t\t\t\tthis.set( 'status', false );\n\t\t\t}\t\t\t\n\t\t},\n\n\t\tcompareValues: {\n\t\t\t'equal': function( a, b ) {\n\t\t\t\treturn a == b;\n\t\t\t},\n\t\t\t'notequal': function( a, b ) {\n\t\t\t\treturn a != b;\n\t\t\t},\n\t\t\t'contains': function( a, b ) {\n\t\t\t\tif ( jQuery.isArray( a ) ) {\n\t\t\t\t\t/*\n\t\t\t\t\t * If a is an array, then we're searching for an index.\n\t\t\t\t\t */\n\t\t\t\t\treturn a.indexOf( b ) >= 0;\n\t\t\t\t} else {\n\t\t\t\t\t/*\n\t\t\t\t\t * If a is a string, then we're searching for a string position.\n\t\t\t\t\t *\n\t\t\t\t\t * If our b value has quotes in it, we want to find that exact word or phrase.\n\t\t\t\t\t */\n\t\t\t\t\tif ( b.indexOf( '\"' ) >= 0 ) {\n\t\t\t\t\t\tb = b.replace( /['\"]+/g, '' );\n\t\t\t\t\t\treturn new RegExp(\"\\\\b\" + b + \"\\\\b\").test( a );\n\t\t\t\t\t}\n\t\t\t\t\treturn a.toLowerCase().indexOf( b.toLowerCase() ) >= 0; \t\t\t\t\n\t\t\t\t}\n\t\t\t},\n\t\t\t'notcontains': function( a, b ) {\n\t\t\t\treturn ! this.contains( a, b );\n\t\t\t},\n\t\t\t'greater': function( a, b ) {\n\t\t\t\t/*\n\t\t\t\t * In 2.9.x, you could use the greater and less like string count.\n\t\t\t\t * i.e. if textbox > (empty string) do something.\n\t\t\t\t * This recreates that ability.\n\t\t\t\t */\n\t\t\t\tif ( jQuery.isNumeric( b ) ) {\n\t\t\t\t\treturn parseFloat( a ) > parseFloat( b );\n\t\t\t\t} else if ( 'string' == typeof a ) {\n\t\t\t\t\treturn 0 < a.length;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t},\n\t\t\t'less': function( a, b ) {\n\t\t\t\t/*\n\t\t\t\t * In 2.9.x, you could use the greater and less like string count.\n\t\t\t\t * i.e. if textbox > (empty string) do something.\n\t\t\t\t * This recreates that ability.\n\t\t\t\t */\n\t\t\t\tif ( jQuery.isNumeric( b ) ) {\n\t\t\t\t\treturn parseFloat( a ) < parseFloat( b );\n\t\t\t\t} else if ( 'string' == typeof a ) {\n\t\t\t\t\treturn 0 >= a.length;\n\t\t\t\t}\n\t\t\n\t\t\t},\n\t\t\t'greaterequal': function( a, b ) {\n\t\t\t\treturn parseFloat( a ) > parseFloat( b ) || parseFloat( a ) == parseFloat( b );\n\t\t\t},\n\t\t\t'lessequal': function( a, b ) {\n\t\t\t\treturn parseFloat( a ) < parseFloat( b ) || parseFloat( a ) == parseFloat( b );\n\t\t\t}\n\t\t} \n\t} );\n\t\n\treturn model;\n} );\n","define( 'models/whenCollection',['models/whenModel'], function( WhenModel ) {\n\tvar collection = Backbone.Collection.extend( {\n\t\tmodel: WhenModel,\n\n\t\tinitialize: function( models, options ) {\n\t\t\tthis.options = options;\n\t\t}\n\t} );\n\treturn collection;\n} );\n","define( 'models/conditionModel',[ 'models/whenCollection' ], function( WhenCollection ) {\n\tvar model = Backbone.Model.extend( {\n\t\tinitialize: function( options ) {\n\t\t\t/*\n\t\t\t * Our \"when\" statement will be like:\n\t\t\t * When field1 == value\n\t\t\t * AND field2 == value\n\t\t\t *\n\t\t\t * We need to create a collection out of this when statement, with each row as a model.\n\t\t\t */\n\t\t\tthis.set( 'when', new WhenCollection( this.get( 'when' ), { condition: this } ) );\n\t\t\t/*\n\t\t\t * When we update any of our \"when\" models' status, check to see if we should send a message.\n\t\t\t */\n\t\t\tthis.get( 'when' ).on( 'change:status', this.checkWhen, this );\n\t\t\t/*\n\t\t\t * Check our initial status;\n\t\t\t */\n\t\t\tthis.checkWhen();\n\t\t},\n\n\t\tcheckWhen: function() {\n\t\t\t/*\n\t\t\t * If we have any OR connectors, then any status being true should trigger pass.\n\t\t\t * Otherwise, we need every status to be true.\n\t\t\t */\n\t\t\tvar statusResults = this.get( 'when' ).pluck( 'status' );\n\n\t\t\tvar connectors = this.get( 'when' ).pluck( 'connector' );\n\t\t\tvar allAND = _.every( _.values( connectors ), function( v ) { return v == 'AND' }, this );\n\t\t\tif ( allAND ) {\n\t\t\t\tvar status = _.every( _.values( statusResults ), function(v) { return v; }, this );\n\t\t\t} else {\n\t\t\t\tvar status = _.some( _.values( statusResults ), function(v) { return v; }, this );\n\t\t\t}\n\n\t\t\tif ( status ) {\n\t\t\t \t/*\n\t\t\t \t * Send out a request for each of our \"then\" statements.\n\t\t\t \t */\n\t\t\t\t_.each( this.get( 'then' ), function( then, index ) {\n\t\t\t\t\tnfRadio.channel( 'condition:trigger' ).request( then.trigger, this, then );\n\t\t\t\t}, this );\t\t\t \n\t\t\t} else {\n\t\t\t\t/*\n\t\t\t\t * Send out a request for each of our \"else\" statements.\n\t\t\t\t */\n\t\t\t\t_.each( this.get( 'else' ), function( elseData, index ) {\n\t\t\t\t\tnfRadio.channel( 'condition:trigger' ).request( elseData.trigger, this, elseData );\n\t\t\t\t}, this );\n\t\t\t}\n\t\t}\n\t} );\n\t\n\treturn model;\n} );\n","define( 'models/conditionCollection',['models/conditionModel'], function( ConditionModel ) {\n\tvar collection = Backbone.Collection.extend( {\n\t\tmodel: ConditionModel,\n\n\t\tinitialize: function( models, options ) {\n\t\t\tthis.formModel = options.formModel;\n\t\t}\n\t} );\n\treturn collection;\n} );\n","/**\n * Initialise condition collection\n * \n * @package Ninja Forms Conditional Logic\n * @copyright (c) 2016 WP Ninjas\n * @since 3.0\n */\ndefine( 'controllers/initCollection',[ 'models/conditionCollection' ], function( ConditionCollection ) {\n\tvar controller = Marionette.Object.extend( {\n\t\tinitialize: function( formModel ) {\n\t\t\tthis.collection = new ConditionCollection( formModel.get( 'conditions' ), { formModel: formModel } );\n this.listenTo(nfRadio.channel('fields'), 'reset:collection', this.resetCollection);\n\t\t},\n resetCollection: function( fieldsCollection ) {\n var formModel = fieldsCollection.options.formModel;\n this.collection = new ConditionCollection( formModel.get( 'conditions' ), { formModel: formModel } );\n }\n\t});\n\n\treturn controller;\n} );\n","/**\n * Handle showing/hiding fields\n * \n * @package Ninja Forms Conditional Logic\n * @copyright (c) 2016 WP Ninjas\n * @since 3.0\n */\ndefine( 'controllers/showHide',[], function() {\n\tvar controller = Marionette.Object.extend( {\n\t\tinitialize: function() {\n\t\t\tnfRadio.channel( 'condition:trigger' ).reply( 'hide_field', this.hideField, this );\n\t\t\tnfRadio.channel( 'condition:trigger' ).reply( 'show_field', this.showField, this );\n\t\t},\n\n\t\thideField: function( conditionModel, then ) {\n\t\t\tvar targetFieldModel = nfRadio.channel( 'form-' + conditionModel.collection.formModel.get( 'id' ) ).request( 'get:fieldByKey', then.key );\n\n\t\t\tif( 'undefined' == typeof targetFieldModel ) return;\n\t\t\ttargetFieldModel.set( 'visible', false );\n if ( ! targetFieldModel.get( 'clean' ) ) {\n\t\t\t\ttargetFieldModel.trigger( 'change:value', targetFieldModel );\n\t\t\t}\n\t\t\t\n\t\t\tnfRadio.channel( 'fields' ).request( 'remove:error', targetFieldModel.get( 'id' ), 'required-error' );\n\t\t},\n\n\t\tshowField: function( conditionModel, then ) {\n\t\t\tvar targetFieldModel = nfRadio.channel( 'form-' + conditionModel.collection.formModel.get( 'id' ) ).request( 'get:fieldByKey', then.key );\n\t\t\t//TODO: Add an error to let the user know the show/hide field is empty.\n\t\t\tif( 'undefined' == typeof targetFieldModel ) return;\n\t\t\ttargetFieldModel.set( 'visible', true );\n if ( ! targetFieldModel.get( 'clean' ) ) {\n targetFieldModel.trigger( 'change:value', targetFieldModel );\n\t\t\t}\n\t\t\tif ( 'recaptcha' === targetFieldModel.get( 'type' ) ) {\n\t\t\t\tthis.renderRecaptcha();\n\t\t\t}\n\t\t\tvar viewEl = { el: nfRadio.channel( 'form-' + conditionModel.collection.formModel.get( 'id' ) ).request( 'get:el' ) };\n nfRadio.channel( 'form' ).request( 'init:help', viewEl );\n\t\t},\n\n\t\trenderRecaptcha: function() {\n\t\t\tjQuery( '.g-recaptcha' ).each( function() {\n var callback = jQuery( this ).data( 'callback' );\n var fieldID = jQuery( this ).data( 'fieldid' );\n if ( typeof window[ callback ] !== 'function' ){\n window[ callback ] = function( response ) {\n nfRadio.channel( 'recaptcha' ).request( 'update:response', response, fieldID );\n };\n }\n\t\t\t\tvar opts = {\n\t\t\t\t\ttheme: jQuery( this ).data( 'theme' ),\n\t\t\t\t\tsitekey: jQuery( this ).data( 'sitekey' ),\n\t\t\t\t\tcallback: callback\n\t\t\t\t};\n\t\t\t\t\n\t\t\t\tgrecaptcha.render( jQuery( this )[0], opts );\n\t\t\t} );\n\t\t}\n\t});\n\n\treturn controller;\n} );\n","/**\n * Setting/unsetting required.\n * \n * @package Ninja Forms Conditional Logic\n * @copyright (c) 2019 WP Ninjas\n * @since 3.0\n */\ndefine( 'controllers/changeRequired',[], function() {\n\tvar controller = Marionette.Object.extend( {\n\t\tinitialize: function() {\n\t\t\tnfRadio.channel( 'condition:trigger' ).reply( 'set_required', this.setRequired, this );\n\t\t\tnfRadio.channel( 'condition:trigger' ).reply( 'unset_required', this.unsetRequired, this );\n\t\t},\n\n\t\tsetRequired: function( conditionModel, then ) {\n\t\t\tvar targetFieldModel = nfRadio.channel( 'form-' + conditionModel.collection.formModel.get( 'id' ) ).request( 'get:fieldByKey', then.key );\n\n\t\t\tif( 'undefined' == typeof targetFieldModel ) return;\n targetFieldModel.set( 'required', 1 );\n\t\t\ttargetFieldModel.trigger( 'reRender', targetFieldModel );\n\t\t},\n\n\t\tunsetRequired: function( conditionModel, then ) {\n\t\t\tvar targetFieldModel = nfRadio.channel( 'form-' + conditionModel.collection.formModel.get( 'id' ) ).request( 'get:fieldByKey', then.key );\n\n\t\t\tif( 'undefined' == typeof targetFieldModel ) return;\n targetFieldModel.set( 'required', 0 );\n targetFieldModel.trigger( 'reRender', targetFieldModel );\n // Ensure we resolve any errors when the field is no longer required.\n\t\t\tnfRadio.channel( 'fields' ).request( 'remove:error', targetFieldModel.get( 'id' ), 'required-error' );\n }\n \n\t});\n\n\treturn controller;\n} );\n","/**\n * Handle adding or removing an option from our list\n * \n * @package Ninja Forms Conditional Logic\n * @copyright (c) 2016 WP Ninjas\n * @since 3.0\n */\ndefine( 'controllers/showHideOption',[], function() {\n\tvar controller = Marionette.Object.extend( {\n\t\tinitialize: function() {\n\t\t\tnfRadio.channel( 'condition:trigger' ).reply( 'show_option', this.showOption, this );\n\n\t\t\tnfRadio.channel( 'condition:trigger' ).reply( 'hide_option', this.hideOption, this );\n\t\t},\n\n\t\tshowOption: function( conditionModel, then ) {\n\t\t\tvar option = this.getOption( conditionModel, then );\n\t\t\toption.visible = true;\n\t\t\tthis.updateFieldModel( conditionModel, then );\n\t\t},\n\n\t\thideOption: function( conditionModel, then ) {\n\t\t\tvar option = this.getOption( conditionModel, then );\n\t\t\toption.visible = false;\n\t\t\tthis.updateFieldModel( conditionModel, then );\n\t\t},\n\n\t\tgetFieldModel: function( conditionModel, then ) {\n\t\t\treturn nfRadio.channel( 'form-' + conditionModel.collection.formModel.get( 'id' ) ).request( 'get:fieldByKey', then.key );\n\t\t},\n\n\t\tgetOption: function( conditionModel, then ) {\n\t\t\tvar targetFieldModel = this.getFieldModel( conditionModel, then );\n\t\t\tvar options = targetFieldModel.get( 'options' );\n\t\t\treturn _.find( options, function( option ) { return option.value == then.value } );\n\t\t},\n\n\t\tupdateFieldModel: function( conditionModel, then ) {\n\t\t\tvar targetFieldModel = this.getFieldModel( conditionModel, then );\n\t\t\tvar options = targetFieldModel.get( 'options' );\n\t\t\ttargetFieldModel.set( 'options', options );\n\t\t\ttargetFieldModel.trigger( 'reRender' );\n\t\t}\n\t});\n\n\treturn controller;\n} );\n","/**\n * Handle changing a field's value\n * \n * @package Ninja Forms Conditional Logic\n * @copyright (c) 2016 WP Ninjas\n * @since 3.0\n */\ndefine( 'controllers/changeValue',[], function() {\n\tvar controller = Marionette.Object.extend( {\n\t\tinitialize: function() {\n\t\t\tnfRadio.channel( 'condition:trigger' ).reply( 'change_value', this.changeValue, this );\n\t\t},\n\n\t\tchangeValue: function( conditionModel, then ) {\n\t\t\tvar targetFieldModel = nfRadio.channel( 'form-' + conditionModel.collection.formModel.get( 'id' ) ).request( 'get:fieldByKey', then.key );\n\t\t\t/*\n\t\t\t * If we have a checkbox then we need to change the value that is set\n\t\t\t * of the then variable to a 1 or 0 to re-render on the front end when\n\t\t\t * the condition is met.\n\t\t\t */\n\t\t\tif( 'checkbox' == targetFieldModel.get( 'type' ) ) {\n\t\t\t\t// We also need to do the opposite of the value that is in the changed model.\n\t\t\t\tif( 'unchecked' == targetFieldModel.changed.value ) {\n\t\t\t\t\tthen.value = 1;\n } else if( 'checked' == targetFieldModel ) {\n\t\t\t\t\tthen.value = 0;\n\t\t\t\t}\n\t\t\t}\n /*\n * Change the value of our field model, and then trigger a re-render of its view.\n */\n\t\t\ttargetFieldModel.set( 'value', then.value );\n\t\t\ttargetFieldModel.trigger( 'reRender', targetFieldModel );\n\t\t},\n\n\t});\n\treturn controller;\n} );\n","/**\n * Handle selecting/deselecting list options\n * \n * @package Ninja Forms Conditional Logic\n * @copyright (c) 2016 WP Ninjas\n * @since 3.0\n */\ndefine( 'controllers/selectDeselect',[], function() {\n\tvar controller = Marionette.Object.extend( {\n\t\tinitialize: function() {\n\t\t\tnfRadio.channel( 'condition:trigger' ).reply( 'select_option', this.selectOption, this );\n\n\t\t\tnfRadio.channel( 'condition:trigger' ).reply( 'deselect_option', this.deselectOption, this );\n\t\t},\n\n\t\tselectOption: function( conditionModel, then ) {\n\t\t\t/*\n\t\t\t * Get our field model and set this option's \"selected\" property to 1\n\t\t\t */\n\t\t\tvar targetFieldModel = nfRadio.channel( 'form-' + conditionModel.collection.formModel.get( 'id' ) ).request( 'get:fieldByKey', then.key );\n\n\n\t\t\tif( _.contains( [ 'listselect', 'listcountry', 'liststate' ], targetFieldModel.get( 'type' ) ) ) { // TODO: Make this more dynamic.\n\t\t\t\ttargetFieldModel.set('clean', false); // Allows for changes to default values.\n\t\t\t}\n\n\t\t\tvar options = targetFieldModel.get( 'options' );\n\n\t\t\tvar option = _.find( options, { value: then.value } );\n\t\t\toption.selected = 1;\n\n\t\t\ttargetFieldModel.set( 'options', options );\n\n\t\t\tif( _.contains( [ 'listselect', 'listcountry', 'liststate' ], targetFieldModel.get( 'type' ) ) ) { // TODO: Make this more dynamic.\n\t\t\t\ttargetFieldModel.set( 'value', option.value ); // Propagate the selected option to the model's value.\n\t\t\t} else {\n\t\t\t\tvar value = targetFieldModel.get( 'value' );\n\t\t\t\tif ( _.isArray( value ) ) {\n if ( 0 > value.indexOf( option.value ) ) {\n value.push( option.value );\n // Set the value to nothing so it knows that something has changed.\n targetFieldModel.set( 'value', '' );\n }\n\t\t\t\t} else {\n\t\t\t\t\tvalue = option.value;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\ttargetFieldModel.set( 'value', value ); // Propagate the selected option to the model's value.\n\t\t\t}\n\n\t\t\t/*\n\t\t\t * Re render our field\n\t\t\t */\n\t\t\ttargetFieldModel.trigger( 'reRender', targetFieldModel );\n\t\t},\n\n\t\tdeselectOption: function( conditionModel, then ) {\n\t\t\t/*\n\t\t\t * When we are trying to deselect our option, we need to change it's \"selected\" property to 0 AND change its value.\n\t\t\t */\n\t\t\tvar targetFieldModel = nfRadio.channel( 'form-' + conditionModel.collection.formModel.get( 'id' ) ).request( 'get:fieldByKey', then.key );\n\n\t\t\t/*\n\t\t\t * Set \"selected\" to 0.\n\t\t\t */\n\t\t\tvar options = targetFieldModel.get( 'options' );\n\t\t\tvar option = _.find( options, { value: then.value } );\n\t\t\toption.selected = 0;\n\t\t\ttargetFieldModel.set( 'options', options );\n\n\t\t\t/*\n\t\t\t * Update our value\n\t\t\t */\n\t\t\tvar currentValue = targetFieldModel.get( 'value' );\n\t\t\tif ( _.isArray( currentValue ) ) {\n\t\t\t\tcurrentValue = _.without( currentValue, then.value );\n\t\t\t} else {\n\t\t\t\tcurrentValue = '';\n\t\t\t}\n\t\t\ttargetFieldModel.set( 'value', currentValue );\n\n\t\t\t/*\n\t\t\t * Re render our field\n\t\t\t */\n\t\t\ttargetFieldModel.trigger( 'reRender', targetFieldModel );\n\t\t},\n\n\t});\n\n\treturn controller;\n} );\n","/**\n * Keep an internal record for which actions are active and deactive.\n * \n * @package Ninja Forms Conditional Logic\n * @copyright (c) 2016 WP Ninjas\n * @since 3.0\n */\ndefine( 'controllers/actions',[], function() {\n\tvar controller = Marionette.Object.extend( {\n\t\tactions: {},\n\t\t\n\t\tinitialize: function() {\n\t\t\t/*\n\t\t\t * Listen for activate/deactivate action messages.\n\t\t\t */\n\t\t\tnfRadio.channel( 'condition:trigger' ).reply( 'activate_action', this.activateAction, this );\n\t\t\tnfRadio.channel( 'condition:trigger' ).reply( 'deactivate_action', this.deactivateAction, this );\n\t\t\n\t\t\t/*\n\t\t\t * Reply to requests for action status.\n\t\t\t */\n\t\t\tnfRadio.channel( 'actions' ).reply( 'get:status', this.getStatus, this );\n\t\t},\n\n\t\tactivateAction: function( conditionModel, thenObject ) {\n\t\t\tthis.actions[ thenObject.key ] = true;\n\t\t\tconsole.log( 'activate action' );\n\t\t},\n\n\t\tdeactivateAction: function( conditionModel, thenObject ) {\n\t\t\tconsole.log( 'deactivate action' );\n\t\t\tthis.actions[ thenObject.key ] = false;\n\t\t},\n\n\t\tgetStatus: function( $id ) {\n\t\t\treturn this.actions[ $id ];\n\t\t}\n\t});\n\n\treturn controller;\n} );\n","var nfRadio = Backbone.Radio;\n\nrequire( [ 'controllers/initCollection', 'controllers/showHide', 'controllers/changeRequired', 'controllers/showHideOption', 'controllers/changeValue', 'controllers/selectDeselect', 'controllers/actions' ], function( InitCollection, ShowHide, ChangeRequired, ShowHideOption, ChangeValue, SelectDeselect, Actions ) {\n\n\tvar NFConditionalLogic = Marionette.Application.extend( {\n\n\t\tinitialize: function( options ) {\n\t\t\tthis.listenTo( nfRadio.channel( 'form' ), 'after:loaded', this.loadControllers );\n\t\t},\n\n\t\tloadControllers: function( formModel ) {\n\t\t\tnew ShowHide();\n\t\t\tnew ChangeRequired();\n\t\t\tnew ShowHideOption();\n\t\t\tnew ChangeValue();\n\t\t\tnew SelectDeselect();\n\t\t\tnew Actions();\n\t\t\tnew InitCollection( formModel );\n\t\t},\n\n\t\tonStart: function() {\n\t\t\t\n\t\t}\n\t} );\n\n\tvar nfConditionalLogic = new NFConditionalLogic();\n\tnfConditionalLogic.start();\n} );\ndefine(\"main\", function(){});\n\n","}());"]} \ No newline at end of file diff --git a/assets/scss/admin/builder.scss b/assets/scss/admin/builder.scss new file mode 100644 index 0000000..d9d1fd8 --- /dev/null +++ b/assets/scss/admin/builder.scss @@ -0,0 +1,157 @@ +.nf-condition { + border: 1px solid #aaa; + margin-top: 20px; +} +.nf-condition-label { + margin-bottom: 10px; +} +.nf-first-when-container { + background: #D3D3D3; + padding: 10px; + &::after { + clear: both; + content: ""; + display: block; + } + .nf-select, + .nf-input { + input { + } + } +} +.nf-when-controls { + float: right; + .fa { + color: #2BAAE7; + cursor: pointer; + font-size: 22px; + &.nf-remove-condition { + color: red; + } + } +} +.nf-when-container, +.nf-then-container, +.nf-else-container { + &::after { + clear: both; + content: ""; + display: block; + } + .nf-one-fourth { + .fa { + font-size: 22px; + padding-top: 15px; + } + &:first-child { + width: 7.5%; + } + } +} +.nf-when-container { + background: #D3D3D3; + padding: 0 10px 10px; + .nf-one-fourth { + width: 23%; + } + .nf-when { + &::after { + clear: both; + content: ""; + display: block; + } + } +} +.nf-then-container { + background: #fefefe; + padding: 10px; + .nf-one-fourth { + width: 30.5%; + &:first-child { + padding-left: 12px; + } + } + .nf-then { + &::after { + clear: both; + content: ""; + display: block; + } + } +} +.nf-else-container { + background: #ddd; + padding: 10px; + .nf-one-fourth { + width: 30.5%; + &:first-child { + padding-left: 12px; + } + } + .nf-else { + &::after { + clear: both; + content: ""; + display: block; + } + } +} +.nf-add-when, +.nf-add-then, +.nf-add-else { + color: #2BAAE7; + cursor: pointer; + font-size: 22px; + margin-bottom: 5px; + padding-left: 12px; +} +.nf-remove-then, +.nf-remove-else, +.nf-remove-when { + color: red; + cursor: pointer; +} + + +.nf-one-fourth { + float: left; + margin-bottom: 15px; + padding: 0 2%; + width: 25%; + &::after { + clear: both; + content: ""; + display: block; + } +} + +.nf-condition.actions { + .nf-when-container { + padding: 20px; + } + .nf-one-third { + width: 40%; + &:nth-child(2) { + width: 20%; + } + &.action-when-label { + padding-top: 16px; + text-align: center; + text-transform: uppercase; + font-weight: bold; + } + } + .nf-one-fourth { + width: 30.8%; + &:first-child { + width: 7.5%; + } + } + .nf-when { + &::after { + clear: both; + content: ""; + display: block; + } + } +} diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..bc97bb1 --- /dev/null +++ b/composer.json @@ -0,0 +1,3 @@ +{ + "name": "wpninjas/ninja-forms-conditional-logic" +} diff --git a/conditionals.php b/conditionals.php new file mode 100644 index 0000000..3bbd3c5 --- /dev/null +++ b/conditionals.php @@ -0,0 +1,360 @@ +integrations = array( + new NF_ConditionalLogic_Integrations_MultiPart() + ); + + self::$instance->triggers = NF_ConditionalLogic::config( 'Triggers' ); + self::$instance->comparators = NF_ConditionalLogic::config( 'Comparators' ); + } + + public function setup_admin() + { + if( ! is_admin() ) return; + + new NF_ConditionalLogic_Admin_Settings(); + } + + public function register_settings_groups( $settings_groups ) + { + return array_merge( $settings_groups, self::config( 'ActionSettingsGroups' ) ); + } + + public function register_action_settings( $action_settings ) + { + return array_merge( $action_settings, self::config( 'ActionSettings' ) ); + } + + public function action_settings_all( $settings_all ) + { + array_push( $settings_all, 'conditions' ); // TODO: Add registered settings names. + return $settings_all; + } + + public function builder_scripts() + { + $ver = self::VERSION; + wp_enqueue_style( 'nf-cl-builder', plugin_dir_url( __FILE__ ) . 'assets/css/builder.css', array(), $ver ); + wp_enqueue_script( 'nf-cl-builder', plugin_dir_url( __FILE__ ) . 'assets/js/min/builder.js', array(), $ver ); + wp_localize_script( 'nf-cl-builder', 'nfcli18n', self::config( 'i18nCLBuilder' ) ); + wp_localize_script( 'nf-cl-builder', 'nfListCountries', Ninja_Forms::config( 'CountryList' ) ); + } + + public function builder_templates() + { + NF_ConditionalLogic::template( 'builder-edit-settings.html.php' ); + } + + /* + |-------------------------------------------------------------------------- + | Internal API Methods + |-------------------------------------------------------------------------- + */ + + /** + * Comparator + * + * @since 3.0.0 + * @param $key + * @return NF_ConditionalLogic_Comparator + * @throws Exception + */ + public function comparator( $key ) + { + if( isset( $this->comparators[ $key ] ) ) return $this->comparators[ $key ][ 'instance' ]; + return new NF_ConditionalLogic_Comparators_NullComparator(); + } + + /** + * Trigger + * + * @since 3.0.0 + * @param $key + * @return NF_ConditionalLogic_Trigger + * @throws Exception + */ + public function trigger( $key ) + { + if( isset( $this->triggers[ $key ] ) ) return $this->triggers[ $key ][ 'instance' ]; + return false; + } + + /* + |-------------------------------------------------------------------------- + | Plugin Properties and Methods + |-------------------------------------------------------------------------- + */ + + /** + * @var NF_ConditionalLogic + * @since 3.0 + */ + private static $instance; + + /** + * Plugin Directory + * + * @since 3.0 + * @var string $dir + */ + public static $dir = ''; + + /** + * Plugin URL + * + * @since 3.0 + * @var string $url + */ + public static $url = ''; + + /** + * Main Plugin Instance + * + * Insures that only one instance of a plugin class exists in memory at any one + * time. Also prevents needing to define globals all over the place. + * + * @since 3.0 + * @static + * @static var array $instance + * @return NF_ConditionalLogic Highlander Instance + */ + public static function instance() + { + if (!isset(self::$instance) && !(self::$instance instanceof NF_ConditionalLogic)) { + self::$instance = new NF_ConditionalLogic(); + + self::$dir = plugin_dir_path(__FILE__); + + self::$url = plugin_dir_url(__FILE__); + + /* + * Register our autoloader + */ + spl_autoload_register(array(self::$instance, 'autoloader')); + } + + return self::$instance; + } + + /** + * Template + * + * @param string $file_name + * @param array $data + */ + public static function template( $file_name = '', array $data = array() ) + { + if( ! $file_name ) return; + + extract( $data ); + + include self::$dir . 'includes/Templates/' . $file_name; + } + + /** + * Config + * + * @param $file_name + * @return mixed + */ + public static function config( $file_name ) + { + return include self::$dir . 'includes/Config/' . $file_name . '.php'; + } + + /** + * Autoloader + * + * Loads files using the class name to mimic the folder structure. + * + * @param $class_name + */ + public function autoloader($class_name) + { + if (class_exists($class_name)) return; + + if ( false === strpos( $class_name, self::PREFIX ) ) return; + + $class_name = str_replace( self::PREFIX, '', $class_name ); + $classes_dir = realpath(plugin_dir_path(__FILE__)) . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR; + $class_file = str_replace('_', DIRECTORY_SEPARATOR, $class_name) . '.php'; + + if (file_exists($classes_dir . $class_file)) { + require_once $classes_dir . $class_file; + } + } + + /** + * Setup License + * + * Registers the plugin with the extension updater. + */ + public function setup_license() + { + if ( ! class_exists( 'NF_Extension_Updater' ) ) return; + + new NF_Extension_Updater( self::NAME, self::VERSION, self::AUTHOR, __FILE__, self::SLUG ); + } + + public function localize_action_conditions( $settings, $form_id ) { + /* + * Get any action conditions + */ + $action_conditions = array(); + + $form_actions = Ninja_Forms()->form( $form_id )->get_actions(); + foreach( $form_actions as $action ){ + $conditions = $action->get_setting( 'conditions' ); + if( ! isset ( $conditions[ 'when' ][0][ 'key' ] ) || empty( $conditions[ 'when' ][0][ 'key' ] ) ) continue; + + /* + * Make sure that we have a valid "then" setting. + */ + if ( ! isset ( $conditions[ 'then' ][0][ 'key' ] ) || empty( $conditions[ 'then' ][0][ 'key' ] ) ) { + $conditions[ 'then' ][0][ 'key' ] = $action->get_id(); + $conditions[ 'then' ][0][ 'trigger' ] = 'activate_action'; + $conditions[ 'then' ][0][ 'type' ] = 'action'; + } + + /* + * Make sure that we have a valid "else" setting. + */ + if ( ! isset ( $conditions[ 'else' ][0][ 'key' ] ) || empty( $conditions[ 'else' ][0][ 'key' ] ) ) { + $conditions[ 'else' ][0][ 'key' ] = $action->get_id(); + $conditions[ 'else' ][0][ 'trigger' ] = 'deactivate_action'; + $conditions[ 'else' ][0][ 'type' ] = 'action'; + $conditions[ 'else' ][0][ 'modelType' ] = 'else'; + } + + $settings[ 'conditions' ][] = $conditions; + + } + + return $settings; + } + } + + /** + * The main function responsible for returning The Highlander Plugin + * Instance to functions everywhere. + * + * Use this function like you would a global variable, except without needing + * to declare the global. + * + * @since 3.0 + * @return NF_ConditionalLogic Highlander Instance + */ + function NF_ConditionalLogic() + { + return NF_ConditionalLogic::instance(); + } + + NF_ConditionalLogic(); + + /* + * Localize our mock conditional logic data + */ + function nf_cl_display_mock_data( $form_id ) { + wp_enqueue_script( 'nf-cl-front-end', plugin_dir_url( __FILE__ ) . 'assets/js/min/front-end.js', array( 'nf-front-end' ) ); + } + + add_action( 'ninja_forms_enqueue_scripts', 'nf_cl_display_mock_data' ); + +} \ No newline at end of file diff --git a/deprecated/classes/trigger-base.php b/deprecated/classes/trigger-base.php new file mode 100644 index 0000000..33bfd84 --- /dev/null +++ b/deprecated/classes/trigger-base.php @@ -0,0 +1,68 @@ + 'text' ); + + /** + * @var type - What type of trigger? (Currently, only options are '' and 'date' ) + */ + var $type = ''; + + /** + * Get things rolling + */ + function __construct() { + $this->comparison_operators = array( + '==' => __( 'Equal To', 'ninja-forms-conditionals' ), + '!=' => __( 'Not Equal To', 'ninja-forms-conditionals' ), + '<' => __( 'Less Than', 'ninja-forms-conditionals' ), + '>' => __( 'Greater Than', 'ninja-forms-conditionals' ), + 'contains' => __( 'Contains', 'ninja-forms-conditionals' ), + 'notcontains' => __( 'Does Not Contain', 'ninja-forms-conditionals' ), + 'on' => __( 'On', 'ninja-forms-conditionals' ), + 'before' => __( 'Before', 'ninja-forms-conditionals' ), + 'after' => __( 'After', 'ninja-forms-conditionals' ), + ); + } + + /** + * Process our conditional trigger + * + * @since 1.2.8 + * @return bool + */ + function compare( $value, $compare ) { + // This space left intentionally blank. + } + + +} \ No newline at end of file diff --git a/deprecated/classes/trigger-date-submitted.php b/deprecated/classes/trigger-date-submitted.php new file mode 100644 index 0000000..62260e5 --- /dev/null +++ b/deprecated/classes/trigger-date-submitted.php @@ -0,0 +1,54 @@ +label = __( 'Date Submitted', 'ninja-forms-conditionals' ); + $this->slug = 'date_submitted'; + + // Set our type to date. This will control our "value" output if a user selects this trigger. + $this->type = 'date'; + + // Unset the comparison operators we don't want to use for this trigger type. + unset( $this->comparison_operators['=='] ); + unset( $this->comparison_operators['!='] ); + unset( $this->comparison_operators['<'] ); + unset( $this->comparison_operators['>'] ); + unset( $this->comparison_operators['contains'] ); + unset( $this->comparison_operators['notcontains'] ); + + $this->conditions = array( 'type' => 'text' ); + } + + /** + * Process our date submitted trigger. + * When this function is called, it will be passed the value of the parameter and will expect a bool return. + * + * @since 1.2.8 + * @return bool + */ + function compare( $value, $compare ) { + $plugin_settings = nf_get_settings(); + $date_format = $plugin_settings['date_format']; + + $now = date( $date_format, current_time( 'timestamp' ) ); + return ninja_forms_conditional_compare( $now, $value, $compare ); + } +} + +return new NF_CL_Date_Submitted_Trigger(); \ No newline at end of file diff --git a/deprecated/classes/trigger-sub-count.php b/deprecated/classes/trigger-sub-count.php new file mode 100644 index 0000000..0d8b6e5 --- /dev/null +++ b/deprecated/classes/trigger-sub-count.php @@ -0,0 +1,51 @@ +label = __( 'Number Of Submissions', 'ninja-forms-conditionals' ); + $this->slug = 'sub_count'; + + // Unset the comparison operators we don't want to use for this trigger type. + unset( $this->comparison_operators['before'] ); + unset( $this->comparison_operators['after'] ); + unset( $this->comparison_operators['contains'] ); + unset( $this->comparison_operators['notcontains'] ); + unset( $this->comparison_operators['on'] ); + + $this->conditions = array( 'type' => 'text' ); + } + + /** + * Process our date submitted trigger. + * When this function is called, it will be passed the value of the parameter and will expect a bool return. + * + * @since 1.2.8 + * @return bool + */ + function compare( $value, $compare ) { + global $ninja_forms_processing; + + $form_id = $ninja_forms_processing->get_form_ID(); + $sub_count = nf_get_sub_count( $form_id ); + + return ninja_forms_conditional_compare( $sub_count, $value, $compare ); + } +} + +return new NF_CL_Sub_Count_Trigger(); \ No newline at end of file diff --git a/deprecated/conditionals.php b/deprecated/conditionals.php new file mode 100644 index 0000000..e96da9e --- /dev/null +++ b/deprecated/conditionals.php @@ -0,0 +1,163 @@ +": + return $param1 > $param2; + case "contains": + if (stripos($param1, $param2) !== false) { + return true; + } else { + return false; + } + case "notcontains": + if (stripos($param1, $param2) === false) { + return true; + } else { + return false; + } + case "on": + $plugin_settings = nf_get_settings(); + if (strtolower(substr($plugin_settings['date_format'], 0, 1)) == 'd') { + $param1 = str_replace('/', '-', $param1); + $param2 = str_replace('/', '-', $param2); + } + + $date1 = new DateTime($param1); + $date2 = new DateTime($param2); + + return $date1 == $date2; + + case "before": + $plugin_settings = nf_get_settings(); + if (strtolower(substr($plugin_settings['date_format'], 0, 1)) == 'd') { + $param1 = str_replace('/', '-', $param1); + $param2 = str_replace('/', '-', $param2); + } + + $date1 = new DateTime($param1); + $date2 = new DateTime($param2); + + return $date1 < $date2; + case "after": + $plugin_settings = nf_get_settings(); + if (strtolower(substr($plugin_settings['date_format'], 0, 1)) == 'd') { + $param1 = str_replace('/', '-', $param1); + $param2 = str_replace('/', '-', $param2); + } + + $date1 = new DateTime($param1); + $date2 = new DateTime($param2); + + return $date1 > $date2; + } + } +} + +/** + * Hook into our nf_init action and register our trigger types. + * + * @since 1.2.8 + * @return void + */ +if( ! function_exists( 'nf_cl_init' ) ) { + function nf_cl_init($instance) + { + $instance->cl_triggers = array(); + $instance->cl_triggers['date_submitted'] = require_once(NINJA_FORMS_CON_DIR . '/classes/trigger-date-submitted.php'); + // $instance->cl_triggers['sub_count'] = require_once( NINJA_FORMS_CON_DIR . '/classes/trigger-sub-count.php' ); + + $instance->cl_triggers = apply_filters('nf_cl_criteria_triggers', $instance->cl_triggers); + } +} + +add_action( 'nf_init', 'nf_cl_init' ); \ No newline at end of file diff --git a/deprecated/css/ninja-forms-conditionals-admin.css b/deprecated/css/ninja-forms-conditionals-admin.css new file mode 100644 index 0000000..298a42f --- /dev/null +++ b/deprecated/css/ninja-forms-conditionals-admin.css @@ -0,0 +1,74 @@ +.description label, +#ninja-forms-conditionals .label { + border-radius: 4px; + padding: 5px; + overflow: hidden; +} + +#ninja-forms-conditionals { + /*border: 1px solid #ccc;*/ + border-radius: 5px; + clear: both; + float: none; + overflow: hidden; +} + +#ninja-forms-conditionals .label { +/* background: #ccc;*/ + display: block; +} + +.ninja-forms-condition { + border: 1px solid #ddd; + margin: 10px; +} + +.ninja-forms-condition-title { + background: #ddd; + padding: 10px; +} + +.ninja-forms-criteria { + background: #fff; + overflow: hidden; +} + +.single-criteria { + border-bottom: 1px solid #ddd; + padding: 5px 0 5px 20px; +} + +.nf-cl-criteria { + background: #fff; + overflow: hidden; +} + +.nf-cl-condition-title { + background: #ddd; + padding: 10px; +} + +.nf-cl-condition { + border: 1px solid #ddd; +} + +.nf-cl-add div { + margin-top: 3px; +} + +.nf-cl-delete { + text-decoration: none; + color: #333; +} + +.nf-cl-delete div { + margin-top: 5px; +} + +.nf-cl-delete:hover { + color: red; +} + +.delete-condition { + float: right; +} \ No newline at end of file diff --git a/deprecated/css/ninja-forms-conditionals-display.css b/deprecated/css/ninja-forms-conditionals-display.css new file mode 100644 index 0000000..e69de29 diff --git a/deprecated/includes/admin/after-import.php b/deprecated/includes/admin/after-import.php new file mode 100644 index 0000000..91accfb --- /dev/null +++ b/deprecated/includes/admin/after-import.php @@ -0,0 +1,41 @@ + array( + 'data' => $field_rows[$y]['data'], + ), + 'where' => array( + 'id' => $field_rows[$y]['id'], + ), + ); + ninja_forms_update_field($args); + } + } + } +} \ No newline at end of file diff --git a/deprecated/includes/admin/ajax.php b/deprecated/includes/admin/ajax.php new file mode 100644 index 0000000..59e2df9 --- /dev/null +++ b/deprecated/includes/admin/ajax.php @@ -0,0 +1,109 @@ + $new_html, 'field_id' => $field_id, 'x' => $x, 'y' => $y); + echo json_encode($array); + die(); +} + +add_action('wp_ajax_ninja_forms_change_action', 'ninja_forms_change_action'); +function ninja_forms_change_action(){ + global $wpdb, $ninja_forms_fields; + + $form_id = $_REQUEST['form_id']; + $action_slug = $_REQUEST['action_slug']; + $field_id = $_REQUEST['field_id']; + $x = $_REQUEST['x']; + $field_data = $_REQUEST['field_data']; + + $field_data = $field_data['ninja_forms_field_'.$field_id]; + + $field_row = ninja_forms_get_field_by_id($field_id); + $type = $field_row['type']; + $reg_field = $ninja_forms_fields[$type]; + if( isset( $reg_field['conditional']['action'][$action_slug] ) ){ + $conditional = $reg_field['conditional']['action'][$action_slug]; + }else if( $action_slug == 'change_value'){ + $conditional = array( 'output' => 'text' ); + }else{ + $conditional = ''; + } + + $conditional = apply_filters( 'nf_change_conditional_action_output', $conditional, $field_id, $action_slug ); + + header("Content-type: application/json"); + + if( isset( $conditional['output'] ) ){ + $new_type = $conditional['output']; + }else{ + $new_type = ''; + } + + $new_html = ninja_forms_return_echo( 'ninja_forms_field_conditional_action_output', $field_id, $x, $conditional, '', $field_data ); + $new_html = utf8_encode( $new_html ); + $array = array('new_html' => $new_html, 'new_type' => $new_type ); + echo json_encode($array); + + die(); + +} + +add_action('wp_ajax_ninja_forms_change_cr_field', 'ninja_forms_change_cr_field'); +function ninja_forms_change_cr_field(){ + global $wpdb, $ninja_forms_fields; + + $field_id = $_REQUEST['field_id']; + $field_value = $_REQUEST['field_value']; + $x = $_REQUEST['x']; + $y = $_REQUEST['y']; + + $field_row = ninja_forms_get_field_by_id($field_value); + $type = $field_row['type']; + $reg_field = $ninja_forms_fields[$type]; + $conditional = $reg_field['conditional']; + $cr = array( 'field' => $field_value ); + header("Content-type: application/json"); + + $new_html = ''; + + if(isset($conditional['value']) AND is_array($conditional['value'])){ + $new_html = ninja_forms_return_echo('ninja_forms_field_conditional_cr_value_output', $field_id, $x, $y, $conditional, $cr ); + $new_html = utf8_encode( $new_html ); + $array = array('new_html' => $new_html, 'new_type' => $conditional['value']['type'] ); + echo json_encode($array); + } + die(); +} + +function nf_cl_add_criteria() { + // Bail if we aren't in the admin. + if ( ! is_admin() ) + return false; + + // Bail if our nonce isn't correct. + check_ajax_referer( 'nf_ajax', 'nf_ajax_nonce' ); + + echo "HELLO WORLD"; + die(); +} + +add_action('wp_ajax_nf_cl_add_criteria', 'nf_cl_add_criteria'); \ No newline at end of file diff --git a/deprecated/includes/admin/mp-copy-page.php b/deprecated/includes/admin/mp-copy-page.php new file mode 100644 index 0000000..e982029 --- /dev/null +++ b/deprecated/includes/admin/mp-copy-page.php @@ -0,0 +1,42 @@ + $new_field ) { + $field = ninja_forms_get_field_by_id( $new_field ); + if ( isset ( $field['data']['conditional'] ) ) { + + for ($x=0; $x < count( $field['data']['conditional'] ); $x++) { + if ( isset ( $field['data']['conditional'][ $x ]['cr'] ) ) { + + for ($y=0; $y < count( $field['data']['conditional'][ $x ]['cr'] ); $y++) { + if ( isset ( $field['data']['conditional'][ $x ]['cr'][ $y ]['field'] ) ) { + $current_id = $field['data']['conditional'][ $x ]['cr'][ $y ]['field']; + + if ( isset ( $new_fields[ $current_id ] ) ) { + $field['data']['conditional'][ $x ]['cr'][ $y ]['field'] = $new_fields[ $current_id ]; + } + } + } + } + } + } + $field['data'] = serialize( $field['data'] ); + $args = array( + 'update_array' => array( + 'data' => $field['data'], + ), + 'where' => array( + 'id' => $new_field, + ), + ); + ninja_forms_update_field( $args ); + } +} + +add_action( 'nf_mp_copy_page', 'nf_cl_mp_copy_page' ); \ No newline at end of file diff --git a/deprecated/includes/admin/notifications.php b/deprecated/includes/admin/notifications.php new file mode 100644 index 0000000..cb84fbe --- /dev/null +++ b/deprecated/includes/admin/notifications.php @@ -0,0 +1,385 @@ + + + + + +
+
+
+ + + + + + + + + + + + + $d ) { // Loop through our conditions and save the data. + if ( 'new' == $cond_id ) { + // If we are creating a new condition, insert it and grab the id. + $cond_id = nf_cl_insert_condition( $n_id ); + } + + // Delete criteria that has been removed. + $criteria = nf_cl_get_criteria( $cond_id ); + foreach ( $criteria as $cr_id ) { + if ( ! isset ( $d['criteria'][ $cr_id ] ) ) { + nf_delete_object( $cr_id ); + } + } + + // Loop through any new criteria. + if ( isset ( $d['criteria']['new'] ) ) { + foreach ( $d['criteria']['new'] as $cr ) { + $cr_id = nf_cl_insert_criteria( $cond_id ); + foreach ( $cr as $key => $value ) { + // Insert our meta values + nf_update_object_meta( $cr_id, $key, $value ); + } + } + unset( $d['criteria']['new'] ); + } + + if ( isset ( $d['criteria'] ) ) { + foreach ( $d['criteria'] as $cr_id => $cr ) { + foreach ( $cr as $key => $value ) { + nf_update_object_meta( $cr_id, $key, $value ); + } + } + unset ( $d['criteria'] ); + } + + // Save our other condition values. + foreach ( $d as $key => $value ) { + nf_update_object_meta( $cond_id, $key, $value ); + } + } + +} + +add_action( 'nf_save_notification', 'nf_cl_save_notification', 10, 3 ); + +/** + * Hook into processing and modify our notifications + * + * @since 1.2.8 + * @return void + */ +function nf_cl_notification_process( $id ) { + global $ninja_forms_processing; + + // Check to see if this notification is active. If it isn't, we don't want to check anything else. + if ( ! Ninja_Forms()->notification( $id )->active ) + return false; + + // Check to see if we have any conditions on this notification + $conditions = nf_cl_get_conditions( $id ); + + if ( empty ( $conditions ) || ! is_array ( $conditions ) ) + return false; + + foreach ( $conditions as $cond_id ) { + // Grab our action + $action = nf_get_object_meta_value( $cond_id, 'action' ); + // Grab our connector + $connector = nf_get_object_meta_value( $cond_id, 'connector' ); + // Grab our criteria + $criteria = nf_cl_get_criteria( $cond_id ); + $pass_array = array(); + foreach ( $criteria as $cr_id ) { + $param = nf_get_object_meta_value( $cr_id, 'param' ); + $compare = nf_get_object_meta_value( $cr_id, 'compare' ); + $value = nf_get_object_meta_value( $cr_id, 'value' ); + + if ( isset ( Ninja_Forms()->cl_triggers[ $param ] ) ) { + $pass_array[] = Ninja_Forms()->cl_triggers[ $param ]->compare( $value, $compare ); + } else { + $user_value = $ninja_forms_processing->get_field_value( $param ); + $pass_array[] = ninja_forms_conditional_compare( $user_value, $value, $compare ); + } + } + + // Check our connector. If it is set to "all", then all our criteria have to match. + if ( 'and' == $connector ) { + $pass = true; + foreach ( $pass_array as $p ) { + if ( ! $p ) { + $pass = false; + break; + } + } + } else { // If our connector is set to "any", then only one criteria has to match. + $pass = false; + foreach ( $pass_array as $p ) { + if ( $p ) { + $pass = true; + break; + } + } + } + + if ( $pass ) { + if ( 'process' == $action ) { + Ninja_Forms()->notification( $id )->active = true; + } else if ( 'noprocess' == $action ) { + Ninja_Forms()->notification( $id )->active = false; + } + } else { + if ( 'process' == $action ) { + Ninja_Forms()->notification( $id )->active = false; + } else if ( 'noprocess' == $action ) { + Ninja_Forms()->notification( $id )->active = true; + } + } + } +} + +add_action( 'nf_notification_before_process', 'nf_cl_notification_process' ); + +/** + * Filter our form export. + */ +function nf_cl_form_export( $form_row ) { + // Make sure that this form has notifications on it. + if ( isset ( $form_row['notifications'] ) ) { + // Loop through our notifications and check conditions. + foreach ( $form_row['notifications'] as $id => $notification ) { + $conditions = nf_cl_get_conditions( $id ); + // Make sure that we actually notifications to connect. + if ( empty ( $conditions ) ) + continue; + + $c_array = array(); // Stores all of our conditions. + // Loop over each condition. + foreach ( $conditions as $c_id ) { + // Grab the criteria ids for this condition. + $criteria = nf_cl_get_criteria( $c_id ); + $cr_array = array(); // Stores all of our criteria. + // Loop through our criteria and populate our criteria array + foreach ( $criteria as $cr_id ) { + // Grab our three criteria settings. + $cr_array[] = array( + 'param' => nf_get_object_meta_value( $cr_id, 'param' ), + 'compare' => nf_get_object_meta_value( $cr_id, 'compare' ), + 'value' => nf_get_object_meta_value( $cr_id, 'value' ), + ); + } + // Add the criteria to the condition array. + $c_array[] = array( 'action' => nf_get_object_meta_value( $c_id, 'action' ), 'connector' => nf_get_object_meta_value( $c_id, 'connector' ), 'criteria' => $cr_array ); + } + $form_row['notifications'][ $id ]['conditions'] = $c_array; + } + } + + return $form_row; +} + +add_filter( 'nf_export_form_row', 'nf_cl_form_export' ); + +/** + * Change the field IDs in our notification conditions after we import a form. + */ +function nf_cl_form_import( $n, $n_id, $form ) { + + // Bail if we don't have any fields set. + if ( ! isset ( $form['field'] ) || empty ( $form['field'] ) ) + return $n; + + $fields = array(); + foreach ( $form['field'] as $field ) { + $old_id = $field['old_id']; + $fields[ $old_id ] = $field['id']; + } + + // Make sure we have some conditions. + if ( isset ( $n['conditions'] ) ) { + // Loop through our conditions, change the field ids, and insert them into the database. + foreach ( $n['conditions'] as $condition ) { + // Make sure that we have criteria set. + if ( ! isset ( $condition['criteria'] ) || empty( $condition['criteria'] ) ) + continue; // There isn't any criteria set. Skip to the next condition. + + // Insert our condition + $c_id = nf_cl_insert_condition( $n_id ); + + // Update our condition meta + nf_update_object_meta( $c_id, 'action', $condition['action'] ); + nf_update_object_meta( $c_id, 'connector', $condition['connector'] ); + + // Change our field ids. + // Loop through our criteria and search for field ids + + foreach ( $condition['criteria'] as $cr ) { + // First, we check the param to see if it is a field id. + if ( isset ( $fields[ $cr['param'] ] ) ) + $cr['param'] = $fields[ $cr['param'] ]; + + // Next, check to see if the value is a field id + if ( isset ( $fields[ $cr['value'] ] ) ) + $cr['value'] = $fields[ $cr['param'] ]; + + // Insert our criteria + $cr_id = nf_cl_insert_criteria( $c_id ); + + // Update our criteria object meta. + nf_update_object_meta( $cr_id, 'param', $cr['param'] ); + nf_update_object_meta( $cr_id, 'compare', $cr['compare'] ); + nf_update_object_meta( $cr_id, 'value', $cr['value'] ); + + } + + } + + unset( $n['conditions'] ); + } + + return $n; +} + +add_filter( 'nf_import_notification_meta', 'nf_cl_form_import', 10, 3 ); \ No newline at end of file diff --git a/deprecated/includes/admin/register-edit-field-section.php b/deprecated/includes/admin/register-edit-field-section.php new file mode 100644 index 0000000..d5e5135 --- /dev/null +++ b/deprecated/includes/admin/register-edit-field-section.php @@ -0,0 +1,366 @@ + + +
+ + + + +
+ + +
+
+ +
+
+ X + + + 'text' ); + }else{ + $conditional = ''; + } + ninja_forms_field_conditional_action_output( $field_id, $x, $conditional, $condition, $field_data ); + ?> + + + + : +
+
+ +
+
+ +
+    X → + + +
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + form( $form_id )->fields; + + /** + * We need to localize our script so that we have the appropriate JSON values to work with our backbone/underscore templates. + * First, we'll get a list of conditionals currently on this object. + * We need to check and see if we are on a notification page or editing a form. + */ + $conditions_json = array(); + if ( isset ( $_REQUEST['notification-action'] ) && 'edit' == $_REQUEST['notification-action'] ) { + $n_id = isset ( $_REQUEST['id'] ) ? $_REQUEST['id'] : ''; + if ( ! empty ( $n_id ) ) { + $conditionals = nf_cl_get_conditions( $n_id ); + foreach ( $conditionals as $cond_id ) { + $action = nf_get_object_meta_value( $cond_id, 'action' ); + $criteria = nf_cl_get_criteria( $cond_id ); + $criteria_json = array(); + foreach ( $criteria as $cr_id ) { + $selected_param = nf_get_object_meta_value( $cr_id, 'param' ); + $compare = nf_get_object_meta_value( $cr_id, 'compare' ); + $value = nf_get_object_meta_value( $cr_id, 'value' ); + $criteria_json[] = array( 'id' => $cr_id, 'param' => $selected_param, 'compare' => $compare, 'value' => $value ); + } + $connector = nf_get_object_meta_value( $cond_id, 'connector' ); + $conditions_json[ $cond_id ] = array( 'id' => $cond_id, 'action' => $action, 'connector' => $connector, 'criteria' => $criteria_json ); + } + } + } + + /** + * Now we get a list of all of our fields and their conditional values. + * $cl_fields will hold our fields and their labels. + * $field_conditions will hold our field type conditional settings. + */ + $cl_fields = array(); + $field_conditions = array(); + foreach ( $fields as $field ) { + $field_type = $field['type']; + $field_id = $field['id']; + if ( isset ( $ninja_forms_fields[ $field_type ]['process_field'] ) && $ninja_forms_fields[ $field_type ]['process_field'] ) { + $label = nf_get_field_admin_label( $field_id ); + $con_value = isset ( $ninja_forms_fields[ $field_type ]['conditional']['value'] ) ? $ninja_forms_fields[ $field_type ]['conditional']['value'] : array( 'type' => 'text' ); + $compare = array( + '==' => __( 'Equal To', 'ninja-forms-conditionals' ), + '!=' => __( 'Not Equal To', 'ninja-forms-conditionals' ), + '<' => __( 'Less Than', 'ninja-forms-conditionals' ), + '>' => __( 'Greater Than', 'ninja-forms-conditionals' ), + 'contains' => __( 'Contains', 'ninja-forms-conditionals' ), + 'notcontains' => __( 'Does Not Contain', 'ninja-forms-conditionals' ), + 'on' => __( 'On', 'ninja-forms-conditionals' ), + 'before' => __( 'Before', 'ninja-forms-conditionals' ), + 'after' => __( 'After', 'ninja-forms-conditionals' ), + ); + $type = $con_value['type']; + if ( 'list' == $type ) { + if ( isset ( $field['data']['list']['options'] ) && is_array ( $field['data']['list']['options'] ) ) { + $list_options = array(); + foreach ( $field['data']['list']['options'] as $opt ) { + $opt_label = $opt['label']; + $opt_value = $opt['value']; + if ( ! isset ( $field['data']['list_show_value'] ) || $field['data']['list_show_value'] != 1 ) { + $opt_value = $opt['label']; + } + $list_options[] = array( 'value' => $opt_value, 'label' => $opt_label ); + } + $con_value = array( 'type' => 'select', 'options' => $list_options ); + } + + unset( $compare['contains'] ); + unset( $compare['notcontains'] ); + unset( $compare['on'] ); + unset( $compare['before'] ); + unset( $compare['after'] ); + + } else if ( '_checkbox' == $field_type ) { + $options[] = array( 'value' => 'checked', 'label' => __( 'Checked', 'ninja-forms' ) ); + $options[] = array( 'value' => 'unchecked', 'label' => __( 'Unchecked', 'ninja-forms' ) ); + $con_value = array( 'type' => 'select', 'options' => $options ); + + unset( $compare['<'] ); + unset( $compare['>'] ); + unset( $compare['contains'] ); + unset( $compare['notcontains'] ); + unset( $compare['on'] ); + unset( $compare['before'] ); + unset( $compare['after'] ); + } else if ( '_text' == $field_type ) { + if ( isset ( $field['data']['datepicker'] ) && $field['data']['datepicker'] == 1 ) { + $field_type = 'date'; + unset( $compare['=='] ); + unset( $compare['!='] ); + unset( $compare['<'] ); + unset( $compare['>'] ); + unset( $compare['contains'] ); + unset( $compare['notcontains'] ); + } else { + unset( $compare['on'] ); + unset( $compare['before'] ); + unset( $compare['after'] ); + } + } + $compare = apply_filters( 'nf_cl_compare_array', $compare, $field_id ); + $cl_fields[] = array( 'id' => $field_id, 'label' => $label . ' ID - ' . $field_id, 'conditions' => $con_value, 'compare' => $compare, 'type' => $field_type ); + } + } + + $cl_fields = apply_filters( 'nf_cl_criteria_fields', $cl_fields ); + + usort( $cl_fields, 'nf_cl_sort_by_label' ); + + $triggers = array(); + + if ( isset ( Ninja_Forms()->cl_triggers ) ) { + foreach ( Ninja_Forms()->cl_triggers as $slug => $trigger ) { + $triggers[] = array( + 'id' => $slug, + 'label' => $trigger->label, + 'type' => $trigger->type, + 'compare' => $trigger->comparison_operators, + 'conditions' => $trigger->conditions, + ); + } + } + + $cr_param_groups = apply_filters( 'nf_cl_criteria_param_groups', array( + __( 'Triggers', 'ninja-forms-conditionals' ) => $triggers, + __( 'Fields', 'ninja-forms-conditionals' ) => $cl_fields, + ) ); + + wp_localize_script( 'nf-cl-admin', 'nf_cl', array( 'cr_param_groups' => $cr_param_groups, 'conditions' => $conditions_json ) ); + } +} + +add_action( 'admin_enqueue_scripts', 'ninja_forms_conditionals_admin_css' ); +function ninja_forms_conditionals_admin_css(){ + if( isset( $_REQUEST['page'] ) AND $_REQUEST['page'] == 'ninja-forms' ){ + wp_enqueue_style('ninja-forms-conditionals-admin', NINJA_FORMS_CON_URL .'/css/ninja-forms-conditionals-admin.css?nf_ver=' . NINJA_FORMS_CON_VERSION, array( 'ninja-forms-admin' ) ); + } +} \ No newline at end of file diff --git a/deprecated/includes/admin/upgrades/nf-update-notice.php b/deprecated/includes/admin/upgrades/nf-update-notice.php new file mode 100644 index 0000000..0048d0c --- /dev/null +++ b/deprecated/includes/admin/upgrades/nf-update-notice.php @@ -0,0 +1,24 @@ +' . __( 'This version of Conditional Logic requires at least version 2.8.6 of Ninja Forms. Please visit your plugins page to update.', 'ninja-forms-conditionals' ) . '', + admin_url( 'plugins.php' ) + ); + } +} + +add_action( 'admin_notices', 'nf_cl_show_upgrade_notices' ); diff --git a/deprecated/includes/admin/view-subs-header-filter.php b/deprecated/includes/admin/view-subs-header-filter.php new file mode 100644 index 0000000..b31a1b3 --- /dev/null +++ b/deprecated/includes/admin/view-subs-header-filter.php @@ -0,0 +1,10 @@ + + +
+ + - + + +
+ +
+
+ +
+
+ X + + + 'text' ); + }else{ + $conditional = ''; + } + ninja_forms_field_conditional_action_output( $field_id, $x, $conditional, $condition, $field_data ); + ?> + + + + : +
+
+ +
+
+ +
+    X → + + +
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + form( $form_id )->fields; + + /** + * We need to localize our script so that we have the appropriate JSON values to work with our backbone/underscore templates. + * First, we'll get a list of conditionals currently on this object. + * We need to check and see if we are on a notification page or editing a form. + */ + $conditions_json = array(); + if ( isset ( $_REQUEST['notification-action'] ) && 'edit' == $_REQUEST['notification-action'] ) { + $n_id = isset ( $_REQUEST['id'] ) ? $_REQUEST['id'] : ''; + if ( ! empty ( $n_id ) ) { + $conditionals = nf_cl_get_conditions( $n_id ); + foreach ( $conditionals as $cond_id ) { + $action = nf_get_object_meta_value( $cond_id, 'action' ); + $criteria = nf_cl_get_criteria( $cond_id ); + $criteria_json = array(); + foreach ( $criteria as $cr_id ) { + $selected_param = nf_get_object_meta_value( $cr_id, 'param' ); + $compare = nf_get_object_meta_value( $cr_id, 'compare' ); + $value = nf_get_object_meta_value( $cr_id, 'value' ); + $criteria_json[] = array( 'id' => $cr_id, 'param' => $selected_param, 'compare' => $compare, 'value' => $value ); + } + $connector = nf_get_object_meta_value( $cond_id, 'connector' ); + $conditions_json[ $cond_id ] = array( 'id' => $cond_id, 'action' => $action, 'connector' => $connector, 'criteria' => $criteria_json ); + } + } + } + + /** + * Now we get a list of all of our fields and their conditional values. + * $cl_fields will hold our fields and their labels. + * $field_conditions will hold our field type conditional settings. + */ + $cl_fields = array(); + $field_conditions = array(); + foreach ( $fields as $field ) { + $field_type = $field['type']; + $field_id = $field['id']; + if ( isset ( $ninja_forms_fields[ $field_type ]['process_field'] ) && $ninja_forms_fields[ $field_type ]['process_field'] ) { + $label = nf_get_field_admin_label( $field_id ); + $con_value = isset ( $ninja_forms_fields[ $field_type ]['conditional']['value'] ) ? $ninja_forms_fields[ $field_type ]['conditional']['value'] : array( 'type' => 'text' ); + $compare = array( + '==' => __( 'Equal To', 'ninja-forms-conditionals' ), + '!=' => __( 'Not Equal To', 'ninja-forms-conditionals' ), + '<' => __( 'Less Than', 'ninja-forms-conditionals' ), + '>' => __( 'Greater Than', 'ninja-forms-conditionals' ), + 'contains' => __( 'Contains', 'ninja-forms-conditionals' ), + 'notcontains' => __( 'Does Not Contain', 'ninja-forms-conditionals' ), + 'on' => __( 'On', 'ninja-forms-conditionals' ), + 'before' => __( 'Before', 'ninja-forms-conditionals' ), + 'after' => __( 'After', 'ninja-forms-conditionals' ), + ); + $type = $con_value['type']; + if ( 'list' == $type ) { + if ( isset ( $field['data']['list']['options'] ) && is_array ( $field['data']['list']['options'] ) ) { + $list_options = array(); + foreach ( $field['data']['list']['options'] as $opt ) { + $opt_label = $opt['label']; + $opt_value = $opt['value']; + if ( ! isset ( $field['data']['list_show_value'] ) || $field['data']['list_show_value'] != 1 ) { + $opt_value = $opt['label']; + } + $list_options[] = array( 'value' => $opt_value, 'label' => $opt_label ); + } + $con_value = array( 'type' => 'select', 'options' => $list_options ); + } + + unset( $compare['contains'] ); + unset( $compare['notcontains'] ); + unset( $compare['on'] ); + unset( $compare['before'] ); + unset( $compare['after'] ); + + } else if ( '_checkbox' == $field_type ) { + $options[] = array( 'value' => 'checked', 'label' => __( 'Checked', 'ninja-forms' ) ); + $options[] = array( 'value' => 'unchecked', 'label' => __( 'Unchecked', 'ninja-forms' ) ); + $con_value = array( 'type' => 'select', 'options' => $options ); + + unset( $compare['<'] ); + unset( $compare['>'] ); + unset( $compare['contains'] ); + unset( $compare['notcontains'] ); + unset( $compare['on'] ); + unset( $compare['before'] ); + unset( $compare['after'] ); + } else if ( '_text' == $field_type ) { + if ( isset ( $field['data']['datepicker'] ) && $field['data']['datepicker'] == 1 ) { + $field_type = 'date'; + unset( $compare['=='] ); + unset( $compare['!='] ); + unset( $compare['<'] ); + unset( $compare['>'] ); + unset( $compare['contains'] ); + unset( $compare['notcontains'] ); + } else { + unset( $compare['on'] ); + unset( $compare['before'] ); + unset( $compare['after'] ); + } + } + $compare = apply_filters( 'nf_cl_compare_array', $compare, $field_id ); + $cl_fields[] = array( 'id' => $field_id, 'label' => $label . ' ID - ' . $field_id, 'conditions' => $con_value, 'compare' => $compare, 'type' => $field_type ); + } + } + + $cl_fields = apply_filters( 'nf_cl_criteria_fields', $cl_fields ); + + usort( $cl_fields, 'nf_cl_sort_by_label' ); + + $triggers = array(); + + if ( isset ( Ninja_Forms()->cl_triggers ) ) { + foreach ( Ninja_Forms()->cl_triggers as $slug => $trigger ) { + $triggers[] = array( + 'id' => $slug, + 'label' => $trigger->label, + 'type' => $trigger->type, + 'compare' => $trigger->comparison_operators, + 'conditions' => $trigger->conditions, + ); + } + } + + $cr_param_groups = apply_filters( 'nf_cl_criteria_param_groups', array( + __( 'Triggers', 'ninja-forms-conditionals' ) => $triggers, + __( 'Fields', 'ninja-forms-conditionals' ) => $cl_fields, + ) ); + + wp_localize_script( 'nf-cl-admin', 'nf_cl', array( 'cr_param_groups' => $cr_param_groups, 'conditions' => $conditions_json ) ); + } +} + +add_action( 'admin_enqueue_scripts', 'ninja_forms_conditionals_admin_css' ); +function ninja_forms_conditionals_admin_css(){ + if( isset( $_REQUEST['page'] ) AND $_REQUEST['page'] == 'ninja-forms' ){ + wp_enqueue_style('ninja-forms-conditionals-admin', NINJA_FORMS_CON_URL .'/includes/deprecated/ninja-forms-conditionals-admin.css', array( 'ninja-forms-admin' ) ); + } +} \ No newline at end of file diff --git a/deprecated/includes/display/display-conditionals.php b/deprecated/includes/display/display-conditionals.php new file mode 100644 index 0000000..acdc474 --- /dev/null +++ b/deprecated/includes/display/display-conditionals.php @@ -0,0 +1,103 @@ + $user_value ) { + foreach( $field_results as $field ) { + + // if ( isset ( $ninja_forms_loading ) ) { + // $field = $ninja_forms_loading->get_field_settings( $field_id ); + // } else { + // $field = $ninja_forms_processing->get_field_settings( $field_id ); + // } + + if( isset( $ninja_forms_fields[$field['type']] ) ){ + $type = $ninja_forms_fields[$field['type']]; + $field_id = $field['id']; + $display_wrap = $type['display_wrap']; + $display_label = $type['display_label']; + $display_function = $type['display_function']; + $data = $field['data']; + + $x = 0; + if(isset($data['conditional']) AND is_array($data['conditional'])){ + if(!isset($local_vars)){ + $local_vars = array(); + } + foreach($data['conditional'] as $conditional){ + $local_vars['field_'.$field_id]['conditional'][$x]['action'] = $conditional['action']; + $local_vars['field_'.$field_id]['conditional'][$x]['connector'] = $conditional['connector']; + $local_vars['field_'.$field_id]['conditional'][$x]['value'] = $conditional['value']; + if(isset($conditional['cr']) AND is_array($conditional['cr'])){ + $y = 0; + foreach($conditional['cr'] as $cr){ + if( isset( $cr['field'] ) ){ + $field = $cr['field']; + }else{ + $field = ''; + } + + if( isset( $cr['operator'] ) ){ + $operator = $cr['operator']; + }else{ + $operator = ''; + } + + if( isset( $cr['value'] ) ){ + $value = $cr['value']; + }else{ + $value = ''; + } + $local_vars['field_'.$field_id]['conditional'][$x]['cr'][$y]['field'] = $field; + $local_vars['field_'.$field_id]['conditional'][$x]['cr'][$y]['operator'] = $operator; + $local_vars['field_'.$field_id]['conditional'][$x]['cr'][$y]['value'] = $value; + $y++; + } + } + $x++; + } + } + } + } + + if(isset($local_vars)){ + return $local_vars; + }else{ + return ''; + } + } +} diff --git a/deprecated/includes/display/field-class-filter.php b/deprecated/includes/display/field-class-filter.php new file mode 100644 index 0000000..f90808c --- /dev/null +++ b/deprecated/includes/display/field-class-filter.php @@ -0,0 +1,51 @@ +get_field_setting( $cr['field'], 'field_class' ); + } else { + $cr_field_class = $ninja_forms_processing->get_field_setting( $cr['field'], 'field_class' ); + } + + if ( strpos ( $cr_field_class, 'ninja-forms-field-conditional-listen' ) === false ) { + $cr_field_class .= ' ninja-forms-field-conditional-listen '; + } + + if ( isset ( $ninja_forms_loading ) ) { + $ninja_forms_loading->update_field_setting( $cr['field'], 'field_class', $cr_field_class ); + } else { + $ninja_forms_processing->update_field_setting( $cr['field'], 'field_class', $cr_field_class ); + } + + } + } + } + } + } +} + +add_action( 'ninja_forms_display_init', 'ninja_forms_conditionals_field_class_filter', 11, 2 ); \ No newline at end of file diff --git a/deprecated/includes/display/field-filter.php b/deprecated/includes/display/field-filter.php new file mode 100644 index 0000000..0280f98 --- /dev/null +++ b/deprecated/includes/display/field-filter.php @@ -0,0 +1,293 @@ +get_all_fields(); + } else { + $all_fields = $ninja_forms_processing->get_all_fields(); + } + + if ( ! is_array( $all_fields ) ) + return false; + + foreach ( $all_fields as $field_id => $user_value ) { + if ( isset ( $ninja_forms_loading ) ) { + $field = $ninja_forms_loading->get_field_settings( $field_id ); + } else { + $field = $ninja_forms_processing->get_field_settings( $field_id ); + } + + // Quick and dirty way of cleaning up the label for required elements with inside label positions + //$field['data']['req_added'] = 1; + + $data = apply_filters( 'ninja_forms_field', $field['data'], $field_id ); + + // We don't want to use the default value if we are on a calc field. + if ( $field['type'] == '_calc' ) { + $data['default_value'] = 0; + } + + $x = 0; + $display_style = ''; + + if( isset ( $data['conditional'] ) AND is_array ( $data['conditional'] ) AND !empty ( $data['conditional'] ) ){ + $action_pass = array(); + + foreach( $data['conditional'] as $conditional ){ + $action = $conditional['action']; + $con_value = $conditional['value']; + if ( is_array( $con_value ) ) { + if ( isset ( $con_value['value'] ) and isset ( $con_value['label'] ) ) { + if ( $con_value['value'] == '_ninja_forms_no_value' ) { + $con_value = $con_value['label']; + } else { + $con_value = $con_value['value']; + } + } + } + if(isset( $conditional['cr']) AND is_array($conditional['cr']) AND !empty($conditional['cr'])){ + $pass_array = array(); + $x = 0; + + foreach($conditional['cr'] as $cr){ + + $pass_array[$x] = false; + if( isset ( $ninja_forms_loading ) ) { + $user_value = $ninja_forms_loading->get_field_value( $cr['field'] ); + }else{ + $user_value = $ninja_forms_processing->get_field_value( $cr['field'] ); + } + + if( isset( $cr['value'] ) ){ + if( is_array( $user_value ) ){ + foreach( $user_value as $v ){ + if( !$pass_array[$x] ){ + $pass_array[$x] = ninja_forms_conditional_compare($v, $cr['value'], $cr['operator']); + }else{ + break; + } + } + }else{ + $pass_array[$x] = ninja_forms_conditional_compare($user_value, $cr['value'], $cr['operator']); + } + }else{ + $pass_array[$x] = true; + } + + $x++; + + } + + } + + if( isset ( $pass_array ) and is_array( $pass_array ) ){ + if( $conditional['connector'] == 'and' ){ + $pass = true; + }else if( $conditional['connector'] == 'or' ){ + $pass = false; + } + + foreach( $pass_array as $p ){ + if( $conditional['connector'] == 'and' ){ + if( $pass ){ + $pass = $p; + }else{ + break; + } + }else if( $conditional['connector'] == 'or' ){ + if( $pass ){ + break; + }else{ + $pass = $p; + } + } + } + } + + if ( isset ( $pass ) and ( !isset ( $action_pass[$action][$con_value] ) OR !$action_pass[$action][$con_value] ) ) { + $action_pass[$action][$con_value] = $pass; + } + } + + foreach( $data['conditional'] as $conditional ){ + $action = $conditional['action']; + $con_value = $conditional['value']; + if ( is_array( $con_value ) ) { + if ( isset ( $con_value['value'] ) and isset ( $con_value['label'] ) ) { + if ( $con_value['value'] == '_ninja_forms_no_value' ) { + $con_value = $con_value['label']; + } else { + $con_value = $con_value['value']; + } + } + } + $pass = $action_pass[$action][$con_value]; + + switch( $conditional['action'] ){ + case 'show': + if( !$pass ){ + $data['display_style'] = 'display:none;'; + $data['visible'] = false; + + if( isset( $data['class'] ) ) { + // Append to class list, with leading comma + $data['class'] .= ',ninja-forms-field-calc-no-new-op,ninja-forms-field-calc-no-old-op'; + } else { + // Create class list, without leading comma + $data['class'] = 'ninja-forms-field-calc-no-new-op,ninja-forms-field-calc-no-old-op'; + } + + // Set our $calc to 0 if we're dealing with a list field. + if ( $field['type'] == '_list' ) { + if ( isset ( $data['list']['options'] ) AND is_array ( $data['list']['options'] ) ) { + for ($x=0; $x < count( $data['list']['options'] ) ; $x++) { + //$data['list']['options'][$x]['calc'] = ''; + } + } + } + + if ( isset ( $ninja_forms_loading ) ) { + if( $field['type'] != '_spam' ){ + $ninja_forms_loading->update_field_value( $field_id, false ); + } + } else if ( isset ( $ninja_forms_processing ) ) { + if( $field['type'] != '_spam' ){ + $user_value = $ninja_forms_processing->get_field_value( $field_id ); + $ninja_forms_processing->update_field_value( $field_id, false ); + $ninja_forms_processing->update_extra_value( '_' . $field_id, $user_value); + } + } + + }else{ + $data['display_style'] = ''; + $data['visible'] = true; + + if ( isset ( $ninja_forms_processing ) ) { + if( $field['type'] != '_spam' ){ + $current_value = $ninja_forms_processing->get_field_value( $field_id ); + $user_value = $ninja_forms_processing->get_extra_value( '_' . $field_id ); + if ( ! $current_value && $user_value ) { + $ninja_forms_processing->update_field_value($field_id, $user_value); + } + } + } + + } + break; + case 'hide': + if( $pass ){ + $data['display_style'] = 'display:none;'; + $data['visible'] = false; + + if ( isset( $data['class'] ) ) { + // Append to class list, with leading comma + $data['class'] .= ',ninja-forms-field-calc-no-new-op,ninja-forms-field-calc-no-old-op'; + } else { + // Create class list, without leading comma + $data['class'] = 'ninja-forms-field-calc-no-new-op,ninja-forms-field-calc-no-old-op'; + } + + + // Set our $calc to 0 if we're dealing with a list field. + if ( $field['type'] == '_list' ) { + if ( isset ( $data['list']['options'] ) AND is_array ( $data['list']['options'] ) ) { + for ($x=0; $x < count( $data['list']['options'] ) ; $x++) { + //$data['list']['options'][$x]['calc'] = ''; + } + } + } + if ( isset ( $ninja_forms_processing ) ) { + if( $field['type'] != '_spam' ){ + $ninja_forms_processing->update_field_value( $field_id, false ); + } + } + } else { + $data['display_style'] = ''; + $data['visible']= true; + } + break; + case 'change_value': + if( $pass ){ + $data['default_value'] = $conditional['value']; + + if ( isset ( $ninja_forms_loading ) ) { + $ninja_forms_loading->update_field_value( $field_id, $conditional['value'] ); + } else if ( isset ( $ninja_forms_processing ) ) { + $ninja_forms_processing->update_field_value( $field_id, $conditional['value'] ); + } + } + break; + case 'add_value': + if( $pass ){ + if( !isset( $conditional['value']['value'] ) ){ + $value = $conditional['value']['label']; + }else{ + $value = $conditional['value']; + } + if( !isset( $data['list']['options'] ) OR !is_array( $data['list']['options'] ) ){ + $data['list']['options'] = array(); + } + $found = false; + for ($x=0; $x < count( $data['list']['options'] ) ; $x++) { + if( isset( $data['list_show_value'] ) AND $data['list_show_value'] == 1 ){ + if( $data['list']['options'][$x]['value'] == $con_value ){ + $found = true; + } + }else{ + if( $data['list']['options'][$x]['label'] == $con_value ){ + $found = true; + } + } + } + if ( !$found ) { + array_push( $data['list']['options'], $value ); + } + } + break; + case 'remove_value': + if( $pass ){ + if( isset( $data['list']['options'] ) AND is_array( $data['list']['options'] ) ){ + for ($x=0; $x < count( $data['list']['options'] ) ; $x++) { + if( isset( $data['list_show_value'] ) AND $data['list_show_value'] == 1 ){ + if( $data['list']['options'][$x]['value'] == $conditional['value'] ){ + $data['list']['options'][$x]['display_style'] = 'display:none;'; + $data['list']['options'][$x]['disabled'] = true; + } + }else{ + if( $data['list']['options'][$x]['label'] == $conditional['value'] ){ + $data['list']['options'][$x]['display_style'] = 'display:none;'; + $data['list']['options'][$x]['disabled'] = true; + } + } + } + $data['list']['options'] = array_values( $data['list']['options'] ); + } + } + break; + default: + $data['conditional_action'] = $conditional['action']; + $data['conditional_pass'] = $pass; + } + } + + $field['data'] = $data; + if ( isset ( $ninja_forms_loading ) ) { + $ninja_forms_loading->update_field_settings( $field_id, $field ); + } else { + if( 1 == $ninja_forms_processing->get_form_setting( 'processing_complete' ) ) { + $field['data']['default_value'] = ''; + } + $ninja_forms_processing->update_field_settings($field_id, $field); + } + } + } +} + +add_action( 'ninja_forms_display_pre_init', 'ninja_forms_conditionals_field_filter', 75 ); +add_action( 'ninja_forms_display_init', 'ninja_forms_conditionals_field_filter', 12 ); +add_action( 'ninja_forms_pre_process', 'ninja_forms_conditionals_field_filter', 1000 ); \ No newline at end of file diff --git a/deprecated/includes/display/scripts.php b/deprecated/includes/display/scripts.php new file mode 100644 index 0000000..e7b276d --- /dev/null +++ b/deprecated/includes/display/scripts.php @@ -0,0 +1,34 @@ + $conditionals ) ); + } +} + + +function ninja_forms_conditionals_display_css( $form_id ){ + $conditionals = ninja_forms_display_conditionals( $form_id ); + if( !empty( $conditionals ) ){ + wp_enqueue_style('ninja-forms-conditionals-display', NINJA_FORMS_CON_URL .'/css/ninja-forms-conditionals-display.css?nf_ver=' . NINJA_FORMS_CON_VERSION ); + } +} \ No newline at end of file diff --git a/deprecated/includes/functions.php b/deprecated/includes/functions.php new file mode 100644 index 0000000..c3eefed --- /dev/null +++ b/deprecated/includes/functions.php @@ -0,0 +1,81 @@ + tags because of PHP servers using ASP-like tags. +// Instead, we will use <# #> in our templates. +_.templateSettings = { + evaluate : /<#([\s\S]+?)#>/g, + interpolate : /<#=([\s\S]+?)#>/g +}; + +jQuery(document).ready(function($) { + console.log( "Loaded" ); + // Function that searches our criteria params object for an id. + nf_cl.getParam = function( param_id ) { + for ( var group in nf_cl.cr_param_groups ) { + for ( var id in nf_cl.cr_param_groups[ group ] ) { + if ( nf_cl.cr_param_groups[ group ][ id ]['id'] == param_id ) { + return nf_cl.cr_param_groups[ group ][ id ]; // Return as soon as the object is found + } + } + + } + return null; // The object was not found + } + + // Backbone View for all of our conditions. + var ConditionsView = Backbone.View.extend( { + el: $( '#nf_cl_conditions' ), + + // Watch for events + events: { + 'click .add-condition': 'addCondition', + 'click .delete-condition': 'deleteCondition', + 'click .add-cr': 'addCriteria', + 'click .delete-cr': 'deleteCriteria', + 'change .cr-param': 'changeParam' + }, + + // Get our view up and running. + initialize: function() { + _.bindAll(this, 'render'); // fixes loss of context for 'this' within methods + this.render(); + }, + + render: function() { + // Loop through our conditions and render a view for each. + if ( typeof nf_cl.conditions != 'undefined' ) { + for ( id in nf_cl.conditions ) { + conditionView.render( id ); + } + } + }, + + addCondition: function( e ) { // Add a new condition + e.preventDefault(); + // Hide the 'add' button + // First we have to see if we've clicked the div icon or the button. + if ( 'DIV' == e.target.tagName ) { + $( e.target ).parent().hide(); + } else { + $( e.target ).hide(); + } + conditionView.render( 'new' ); + }, + + deleteCondition: function( e ) { + e.preventDefault(); + $( e.target ).parent().parent().parent().remove(); + $( this.el ).find( '.nf-cl-add' ).show(); + }, + + addCriteria: function( e ) { + e.preventDefault(); + var cond_id = $( e.target ).data( 'cond-id' ); + var cr_id = 'new'; + var selected_param = ''; + var value = ''; + var compare = '=='; + criteriaView.conditionEl = $( '#nf_cl_condition_' + cond_id ); + criteriaView.renderCriteriaRow( cond_id, cr_id, nf_cl.cr_param_groups, selected_param, value, compare ); + }, + + deleteCriteria: function( e ) { + e.preventDefault(); + targetEl = $( e.target ).parent().parent(); + $( targetEl ).remove(); + }, + + changeParam: function( e ) { + e.preventDefault(); + var cr_id = $( e.target ).data( 'cr-id' ); + var cond_id = $( e.target ).data( 'cond-id' ); + var selected_param = $( e.target ).val(); + var value = ''; + var compare = ''; + if ( 'new' == cr_id ) { + var num = $( e.target ).data( 'num' ); + var cr_name = 'conditions[' + cond_id + '][criteria][new][' + num + ']'; + var div_id = 'nf_cr_new_' + num; + } else { + var num = ''; + var cr_name = 'conditions[' + cond_id + '][criteria][' + cr_id + ']'; + var div_id = 'nf_cr_' + cr_id; + } + criteriaView.renderCriteriaCompare( cr_id, cr_name, nf_cl.cr_param_groups, selected_param, compare, num, div_id ); + criteriaView.renderCriteriaValue( cr_id, cr_name, nf_cl.cr_param_groups, selected_param, value, num, div_id ); + + // Check to see if we should enable or disable the datepicker on our value. + var param = nf_cl.getParam( selected_param ); + if ( param.type == 'date' ) { + $( e.target ).next().next().find( 'input' ).datepicker({ + dateFormat: ninja_forms_settings.date_format + }); + } + } + } ); + + // Backbone View for our condition criteria + var ConditionView = Backbone.View.extend( { + el: $( '#nf_cl_conditions' ), // attaches `this.el` to an existing element. + + // Get our view up and running. + initialize: function() { + _.bindAll(this, 'render'); // fixes loss of context for 'this' within methods + }, + + render: function( cond_id ) { + if ( typeof nf_cl.conditions[ cond_id ] != 'undefined' ) { + var action = nf_cl.conditions[ cond_id ].action; + var connector = nf_cl.conditions[ cond_id ].connector; + } else { + var action = ''; + var connector = ''; + } + var tmp = _.template( $( '#tmpl-nf-cl-condition' ).html() )( { cond_id: cond_id, action: action, connector: connector } ); + $( this.el ).append( tmp ); + criteriaView.renderCriteriaRows( cond_id ); + } + + } ); + + // Backbone View for our condition criteria + var CriteriaView = Backbone.View.extend( { + conditionEl: '', // attaches `this.el` to an existing element. + + // Get our view up and running. + initialize: function() { + _.bindAll(this, 'render'); // fixes loss of context for 'this' within methods + }, + + renderCriteriaRows: function( cond_id ) { + // If we are working with a new condition, there won't be any criteria rows. + if ( 'new' == cond_id ) { + return false; + } + + this.conditionEl = $( '#nf_cl_condition_' + cond_id ); + var condition = nf_cl.conditions[ cond_id ]; + var criteria = condition.criteria; + var that = this; + _.each( criteria, function( cr ) { + var cr_id = cr.id; + var selected_param = cr.param; + var value = cr.value; + var compare = cr.compare; + that.renderCriteriaRow( cond_id, cr_id, nf_cl.cr_param_groups, selected_param, value, compare ); + } ); + }, + + renderCriteriaRow: function ( cond_id, cr_id, param_groups, selected_param, value, compare ) { + if ( 'new' == cr_id ) { + var num = $( this.conditionEl ).find( '.single-criteria' ).length; + var cr_name = 'conditions[' + cond_id + '][criteria][new][' + num + ']'; + var div_id = 'nf_cr_new_' + num; + var data_id = 'new-' + num; + } else { + var num = ''; + var cr_name = 'conditions[' + cond_id + '][criteria][' + cr_id + ']'; + var div_id = 'nf_cr_' + cr_id; + var data_id = cr_id; + } + var tmp = _.template( $( '#tmpl-nf-cl-criteria' ).html() )( { cr_id: cr_id, cr_name: cr_name, param_groups: param_groups, selected_param: selected_param, value: value, compare: compare, num: num, div_id: div_id, data_id: data_id, cond_id: cond_id } ); + $( this.conditionEl ).find( '.nf-cl-criteria' ).append( tmp ); + this.renderCriteriaCompare( cr_id, cr_name, param_groups, selected_param, compare, num, div_id ); + this.renderCriteriaValue( cr_id, cr_name, param_groups, selected_param, value, num, div_id ); + }, + + renderCriteriaCompare: function( cr_id, cr_name, param_groups, selected_param, compare, num, div_id ) { + var span = $( '#' + div_id ).find( '.cr-compare' ); + var tmp = _.template( $( '#tmpl-nf-cl-criteria-compare' ).html() )( { cr_id: cr_id, cr_name: cr_name, param_groups: param_groups, selected_param: selected_param, compare: compare, nf_cl: nf_cl } ); + $( span ).html( tmp ); + }, + + renderCriteriaValue: function( cr_id, cr_name, param_groups, selected_param, value, num, div_id ) { + var span = $( '#' + div_id ).find( '.cr-value' ); + var tmp = _.template( $( '#tmpl-nf-cl-criteria-value' ).html() )( { cr_id: cr_id, cr_name: cr_name, param_groups: param_groups, selected_param: selected_param, value: value, num: num, nf_cl: nf_cl } ); + $( span ).html( tmp ); + var param = nf_cl.getParam( selected_param ); + if ( param && param.type == 'date' ) { + $( span ).find( 'input' ).datepicker( { + dateFormat: ninja_forms_settings.date_format + } ); + } + } + + } ); + + // **CriteriaView instance**: Instantiate Criteria view. + var criteriaView = new CriteriaView(); + + // **ConditionView instance**: Instantiate Condition view. + var conditionView = new ConditionView(); + + // **ConditionsView instance**: Instantiate Conditions view. + var conditionsView = new ConditionsView(); + + //Listen to the "hidden list value" checkbox. + $(document).on( 'change', '.ninja-forms-field-list-show-value', function(){ + var field_id = this.id.replace("ninja_forms_field_", ""); + field_id = field_id.replace("_list_show_value", ""); + var new_values = new Object(); + if(this.checked){ + $(".ninja-forms-field-" + field_id + "-list-option-value").each(function(){ + var x = this.id.replace("ninja_forms_field_" + field_id + "_list_option_", ""); + x = x.replace("_value_span", ""); + new_values[x] = $(this).children(".ninja-forms-field-list-option-value").val(); + }); + $(".ninja-forms-field-" + field_id + "-list-option-value").show(); + $(".ninja-forms-field-conditional-cr-field").each(function(){ + if(this.value == field_id){ + $(this).nextElementInDom(".ninja-forms-field-conditional-cr-value-list:first").children('option').each(function(){ + this.value = new_values[this.title]; + }); + } + }); + $(".ninja-forms-field-" + field_id + "-conditional-value").each(function(){ + $(this).children('option').each(function(){ + this.value = new_values[this.title]; + }); + }); + }else{ + + $("#ninja_forms_field_" + field_id + "_list_options").children(".ninja-forms-field-" + field_id + "-list-option").find(".ninja-forms-field-list-option-label").each(function(){ + var parent_id = $(this).parent().parent().parent().parent().parent().prop("id"); + + var x = parent_id.replace("ninja_forms_field_" + field_id + "_list_option_", ""); + + new_values[x] = this.value; + }); + + $(".ninja-forms-field-conditional-cr-field").each(function(){ + if(this.value == field_id){ + $(this).nextElementInDom(".ninja-forms-field-conditional-cr-value-list:first").children('option').each(function(){ + this.value = new_values[this.title]; + }); + } + }); + + $(".ninja-forms-field-" + field_id + "-conditional-value").each(function(){ + $(this).children('option').each(function(){ + this.value = new_values[this.title]; + }); + }); + + $(".ninja-forms-field-" + field_id + "-list-option-value").hide(); + } + }); + + //Conditional Action Change + $(document).on( 'change', '.ninja-forms-field-conditional-action', function(){ + var value_id = this.id.replace('action', 'value'); + var label_id = this.id.replace('action', 'value_label'); + var form_id = $("#_form_id").val(); + var field_id = $(this).parent().parent().attr("name"); + var conditional_value_type = $("#ninja_forms_field_" + field_id + "_conditional_value_type").val(); + var list_show_value = $("#ninja_forms_field_" + field_id + "_list_show_value").prop("checked"); + var x = $(".ninja-forms-field-" + field_id + "-conditional").length; + x--; + var action_slug = this.value; + var field_data = ninja_forms_serialize_data( field_id ); + + $.post(ajaxurl, { form_id: form_id, field_id: field_id, x: x, action_slug: action_slug, field_data: field_data, action:"ninja_forms_change_action"}, function(response){ + + $("#ninja_forms_field_" + field_id + "_" + x + "_value_span").prop("innerHTML", response.new_html); + + if(response.new_type == 'list'){ + $("#" + value_id).children().remove().end(); + $(".ninja-forms-field-" + field_id + "-list-option").each(function(){ + + var label = $(this).find(".ninja-forms-field-list-option-label").val(); + + if(list_show_value){ + var value = $(this).find(".ninja-forms-field-list-option-value").val(); + }else{ + var value = label; + } + + var i = this.id.replace("ninja_forms_field_" + field_id + "_list_option_", ""); + $("#" + value_id).append(''); + }); + } + + }); + }); + + //Add New Conditional + $(document).on( 'click', '.ninja-forms-field-add-conditional', function(event){ + event.preventDefault(); + var field_id = this.id.replace("ninja_forms_field_", ""); + field_id = field_id.replace("_add_conditional", ""); + var form_id = $("#_form_id").val(); + var x = $(".ninja-forms-field-" + field_id + "-conditional").length; + $.post(ajaxurl, { form_id: form_id, field_id: field_id, x: x, action:"ninja_forms_add_conditional"}, function(response){ + $("#ninja_forms_field_" + field_id + "_conditionals").append(response); + }); + }); + + //Remove Conditional + $(document).on( 'click', '.ninja-forms-field-remove-conditional', function(event){ + event.preventDefault(); + var field_id = this.id.replace("ninja_forms_field_", ""); + field_id = field_id.replace("_remove_conditional", ""); + var x = this.name; + $("#ninja_forms_field_" + field_id + "_conditional_" + x).remove(); + }); + + //Add New Criterion + $(document).on( 'click', '.ninja-forms-field-add-cr', function(event){ + event.preventDefault(); + var field_id = this.id.replace("ninja_forms_field_", ""); + field_id = field_id.replace("_add_cr", ""); + var form_id = $("#_form_id").val(); + var x = this.name; + var y = $(".ninja-forms-field-" + field_id + "-conditional-" + x + "-cr").length; + $.post(ajaxurl, { form_id: form_id, field_id: field_id, x: x, y: y, action:"ninja_forms_add_cr"}, function(response){ + $("#ninja_forms_field_" + field_id + "_conditional_" + x + "_cr").append(response.new_html); + var title = ''; + var title_id = ''; + $(".ninja-forms-field-title").each(function(){ + title = this.innerHTML; + if( title.length > 30 ){ + title = title.substring(0,30) + "..."; + } + title_id = this.id.replace("ninja_forms_field_", ""); + title_id = title_id.replace("_title", ""); + $(".ninja-forms-field-conditional-cr-field > option").each(function(){ + if(this.value == title_id){ + this.text = "ID: " + title_id + " - " + title; + } + }); + }); + }); + }); + + //Remove Criterion + $(document).on( 'click', '.ninja-forms-field-remove-cr', function(event){ + event.preventDefault(); + var field_id = this.id.replace("ninja_forms_field_", ""); + field_id = field_id.replace("_remove_cr", ""); + var x = this.name; + var y = this.rel; + $("#ninja_forms_field_" + field_id + "_conditional_" + x + "_cr_" + y).remove(); + }); + + //Change Criterion Select List + $(document).on( 'change', '.ninja-forms-field-conditional-cr-field', function(){ + var field_id = this.id.replace("ninja_forms_field_", ""); + field_id = field_id.replace("_cr_field", ""); + var tmp = this.title.split("_"); + var x = tmp[0]; + var y = tmp[1]; + var field_value = this.value; + + if(this.value != ''){ + $.post(ajaxurl, { field_id: field_id, field_value: field_value, x: x, y: y, output_options: 1, action:"ninja_forms_change_cr_field"}, function(response){ + $("#ninja_forms_field_" + field_id + "_conditional_" + x + "_cr_" + y + "_value").prop("innerHTML", response.new_html); + if(response.new_type == 'list'){ + // Check our field model to see if we have any collapsed list options that may have changed. + var target_field = nfFields.get( field_value ); + + if ( 'undefined' !== typeof target_field.get( 'list' ) ) { + if ( 'undefined' !== target_field.get( 'list' ).options ) { + var i = 0; + $('select[name="ninja_forms_field_' + field_id + '\\[conditional\\]\\[' + x + '\\]\\[cr\\]\\[' + y + '\\]\\[value\\]"]').find( 'option' ).remove().end(); + _.each( target_field.get( 'list' ).options, function( option ) { + var label = option.label; + var value = option.value; + var calc = option.calc; + if ( 0 == target_field.get( 'list_show_value' ) ) { + value = label; + } + $('select[name="ninja_forms_field_' + field_id + '\\[conditional\\]\\[' + x + '\\]\\[cr\\]\\[' + y + '\\]\\[value\\]"]').append(''); + i++; + } ); + } + } + + if ( $(".ninja-forms-field-" + field_value + "-list-option").length > 0 ) { + $('select[name="ninja_forms_field_' + field_id + '\\[conditional\\]\\[' + x + '\\]\\[cr\\]\\[' + y + '\\]\\[value\\]"]').find( 'option' ).remove().end(); + $(".ninja-forms-field-" + field_value + "-list-option").each(function(){ + var label = $(this).find(".ninja-forms-field-list-option-label").val(); + if($("#ninja_forms_field_" + field_value + "_list_show_value").prop("checked") == true){ + var value = $(this).find(".ninja-forms-field-list-option-value").val(); + }else{ + var value = label; + } + var i = this.id.replace("ninja_forms_field_" + field_value + "_list_option_", ""); + $('select[name="ninja_forms_field_' + field_id + '\\[conditional\\]\\[' + x + '\\]\\[cr\\]\\[' + y + '\\]\\[value\\]"]').append(''); + }); + } + + + + } + }); + }else{ + $("#ninja_forms_field_" + field_id + "_conditional_" + x + "_cr_" + y + "_value").prop("innerHTML", ""); + } + }); + + //Change Criterion Select List + $(document).on( 'change', '.ninja-forms-notification-conditional-cr-field', function(){ + var field_id = this.id.replace("ninja_forms_field_", ""); + field_id = field_id.replace("_cr_field", ""); + var tmp = this.title.split("_"); + var x = tmp[0]; + var y = tmp[1]; + var field_value = this.value; + + if(this.value != ''){ + $.post(ajaxurl, { field_id: field_id, field_value: field_value, x: x, y: y, output_options: 1, action:"ninja_forms_change_cr_field"}, function(response){ + $("#ninja_forms_field_" + field_id + "_conditional_" + x + "_cr_" + y + "_value").prop("innerHTML", response.new_html); + if(response.new_type == 'list'){ + $(".ninja-forms-field-" + field_value + "-list-option").each(function(){ + var label = $(this).find(".ninja-forms-field-list-option-label").val(); + if($("#ninja_forms_field_" + field_value + "_list_show_value").prop("checked") == true){ + var value = $(this).find(".ninja-forms-field-list-option-value").val(); + }else{ + var value = label; + } + var i = this.id.replace("ninja_forms_field_" + field_value + "_list_option_", ""); + $('select[name="ninja_forms_field_' + field_id + '\\[conditional\\]\\[' + x + '\\]\\[cr\\]\\[' + y + '\\]\\[value\\]"]').append(''); + }); + } + }); + }else{ + $("#ninja_forms_field_" + field_id + "_conditional_" + x + "_cr_" + y + "_value").prop("innerHTML", ""); + } + }); + + function ninja_forms_serialize_data( field_id ){ + var data = $('input[name^=ninja_forms_field_' + field_id + ']'); + var field_data = jQuery(data).serializeFullArray(); + return field_data; + } + + // When we add a new field, add the new field to any conditional criterion we have. + $( document ).on( 'addField.clAdd', function( e, response ) { + $(".ninja-forms-field-conditional-cr-field").each(function(){ + $(this).append(''); + }); + } ); + + // When we remove a field, update our conditions + $( document ).on( 'removeField.clRemove', function( e, field_id ) { + $( '.ninja-forms-field-conditional-cr-field' ).each( function() { + $( this ).children( 'option' ).each( function() { + if( this.value == field_id ) { + $( this ).remove(); + } + }); + }); + } ); + +}); //Document.ready(); diff --git a/deprecated/js/dev/ninja-forms-conditionals-display.js b/deprecated/js/dev/ninja-forms-conditionals-display.js new file mode 100644 index 0000000..3397729 --- /dev/null +++ b/deprecated/js/dev/ninja-forms-conditionals-display.js @@ -0,0 +1,570 @@ +// Array Remove - By John Resig (MIT Licensed) +Array.prototype.remove = function(from, to) { + var rest = this.slice((to || from) + 1 || this.length); + this.length = from < 0 ? this.length + from : from; + return this.push.apply(this, rest); +}; + +jQuery(document).ready(function(jQuery) { + + /* * * Begin Conditional Logic JS * * */ + jQuery(document).on( 'change', '.ninja-forms-field-conditional-listen', function(){ + ninja_forms_check_conditional(this, true); + }); + + jQuery(document).on( 'keyup', '.ninja-forms-field-conditional-listen', function(){ + ninja_forms_check_conditional(this, true); + }); + + /* + Prevent submit on enter if the submit button is hidden. + */ + jQuery( ".ninja-forms-form" ).on( 'beforeSubmit.clPreventSubmit', function( e, formData, jqForm, options ) { + var form_id = jQuery( jqForm ).prop( 'id' ).replace( 'ninja_forms_form_', '' ); + if ( true != jQuery( '#nf_submit_' + form_id ).parent().data( 'visible' ) ) { + jQuery( '#nf_processing_' + form_id ).hide(); + jQuery( '#nf_submit_' + form_id ).show(); + return false; + } + } ); + + /* * * End Conditional Logic JS * * */ + +}); +function ninja_forms_check_conditional(element, action_value){ + + var form_id = ninja_forms_get_form_id(element); + var conditional = eval( 'ninja_forms_form_' + form_id + '_conditionals_settings' ); + conditional = conditional.conditionals; + + var field_id = jQuery(element).attr("rel"); + for(field in conditional){ + + var target_field = field.replace("field_", ""); + var conditional_length = jQuery(conditional[field]['conditional']).length; + for (i = 0; i < conditional_length; i++){ + if ( typeof conditional[field]['conditional'][i] !== 'undefined' ) { + var cr_length = jQuery(conditional[field]['conditional'][i]['cr']).length; + for (x = 0; x < cr_length; x++){ + if ( typeof conditional[field]['conditional'][i] !== 'undefined' ) { + if(conditional[field]['conditional'][i]['cr'][x]['field'] == field_id){ + var action_value = conditional[field]['conditional'][i]['cr'][x]['value']; + ninja_forms_conditional_change(element, target_field, action_value); //target_field, change value? + } + } + } + } + } + } +} + +function ninja_forms_conditional_change(element, target_field, action_value){ + var form_id = ninja_forms_get_form_id(element); + var conditional = eval( 'ninja_forms_form_' + form_id + '_conditionals_settings' ); + conditional = conditional.conditionals; + + var cond = conditional["field_" + target_field]['conditional']; + conditional_length = jQuery(cond).length; + var pass_array = new Array(); + var value_array = new Array(); + // We need to check our "actions" to make sure that if multiple actions are added with different conditions, any evaluating to true will fire. + var action_pass = new Object(); + + for (i = 0; i < conditional_length; i++){ + var connector = cond[i]['connector']; + var cr_row = cond[i]['cr']; + value_array[i] = cond[i]['value']; + cr_length = jQuery(cr_row).length; + var action = cond[i]['action']; + + if(connector == 'and'){ + pass_array[i] = true; + }else if(connector == 'or'){ + pass_array[i] = false; + } + + for (x = 0; x < cr_length; x++){ + + cr_field = cr_row[x]['field']; + cr_operator = cr_row[x]['operator']; + cr_value = cr_row[x]['value']; + cr_type = jQuery("#ninja_forms_field_" + cr_field + "_type").val(); + cr_visible = jQuery("#ninja_forms_field_" + cr_field + "_div_wrap").data("visible"); + if(cr_type == 'list'){ + // We are either dealing with a checkbox or radio list. + if(jQuery("#ninja_forms_field_" + cr_field + "_list_type").val() == "checkbox" ){ //This is a checkbox list. + if(jQuery(".ninja_forms_field_" + cr_field + "[value='" + cr_value + "']").prop("checked")){ + var field_value = cr_value; + }else{ + var field_value = ''; + } + jQuery(".ninja_forms_field_" + cr_field + "[value='" + cr_value + "']").each( function(){ + if(!cr_visible){ + //cr_visible = jQuery(this).is(":visible"); + } + }); + }else if( jQuery("#ninja_forms_field_" + cr_field + "_list_type").val() == "radio" ){ //This is a radio list. + var field_value = jQuery("input[name='ninja_forms_field_" + cr_field + "']:checked").val(); + jQuery("input[name='ninja_forms_field_" + cr_field + "']").each( function(){ + if(!cr_visible){ + //cr_visible = jQuery(this).is(":visible"); + } + }); + }else{ + field_value = jQuery("#ninja_forms_field_" + cr_field).val(); // This is a dropdown list. + //cr_visible = jQuery("#ninja_forms_field_" + cr_field).is(":visible"); + } + }else if(cr_type == 'checkbox'){ + if(jQuery("#ninja_forms_field_" + cr_field).prop("checked")){ + var field_value = 'checked'; + }else{ + var field_value = 'unchecked'; + } + //cr_visible = jQuery("#ninja_forms_field_" + cr_field).is(":visible"); + }else{ + field_value = jQuery("#ninja_forms_field_" + cr_field).val(); + //cr_visible = jQuery("#ninja_forms_field_" + cr_field).is(":visible"); + } + + if(is_numeric(field_value)){ + field_value = ( field_value % 1 === 0 ) ? parseInt(field_value) : parseFloat(field_value); + } + + if(is_numeric(cr_value)){ + cr_value = ( cr_value % 1 === 0 ) ? parseInt(cr_value) : parseFloat(cr_value); + } + + var tmp = ninja_forms_conditional_compare(field_value, cr_value, cr_operator); + + if( cr_visible != 1 ){ + tmp = false; + } + + if(connector == 'and'){ + if(!tmp){ + pass_array[i] = false; + } + }else if(connector == 'or'){ + if(tmp){ + pass_array[i] = true; + } + } + + } + + if ( typeof action_pass[action] === 'undefined' ) { + action_pass[action] = new Object(); + } + + if ( action == 'add_value' ) { + var value = value_array[i]; + + if(typeof value.value === "undefined" || value.value == "_ninja_forms_no_value"){ + value.value = value.label; + } + action_pass[action][value.value] = pass_array[i]; + } else { + if ( typeof action_pass[action][cond[i]['value']] === 'undefined' || action_pass[action][cond[i]['value']] === false ) { + if ( pass_array[i] ) { + action_pass[action][cond[i]['value']] = true; + } else { + action_pass[action][cond[i]['value']] = false; + } + } + } + } + + for (i = 0; i < conditional_length; i++){ + if ( typeof cond[i] === 'undefined' ) continue; + var action = cond[i]['action']; + value = value_array[i]; + if ( action == 'add_value' ) { + if(typeof value.value === "undefined" || value.value == "_ninja_forms_no_value"){ + value.value = value.label; + } + pass = action_pass[action][value.value]; + } else { + pass = action_pass[action][value]; + } + + var input_type = jQuery("#ninja_forms_field_" + target_field + "_type").val(); + var list_type = ''; + var list = false; + if(input_type == "list"){ + input_type = jQuery("#ninja_forms_field_" + target_field + "_list_type").val(); + list_type = input_type; + list = true; + } + + if(action == 'show'){ + if(pass){ + + var was_visible = jQuery( "#ninja_forms_field_" + target_field + "_div_wrap" ).data( "visible" ); + //var was_visible = jQuery("#ninja_forms_field_" + target_field + "_div_wrap").is(":visible"); + jQuery("#ninja_forms_field_" + target_field + "_div_wrap").show(10, function(e){ jQuery(document).triggerHandler('ninja_forms_conditional_show'); }); + jQuery("#ninja_forms_field_" + target_field + "_div_wrap").data("visible", true); + if ( list ) { + if ( input_type == 'checkbox' || input_type == 'radio' ) { + var target_element = jQuery("#ninja_forms_field_" + target_field + "_div_wrap").find(".ninja-forms-field:visible:first"); + } else { + var target_element = jQuery("#ninja_forms_field_" + target_field); + } + } else { + var target_element = jQuery("#ninja_forms_field_" + target_field); + } + + if ( !was_visible ) { + // Check to see if we're working with a field that's listening for a calculation. + if ( jQuery( target_element ).hasClass("ninja-forms-field-calc-listen") ) { + // Since we are going to be hiding a field upon which a calculation is based, we need to set the oldValue of our calculation to the current field's value. + jQuery(target_element).data( "oldValue", '' ); + // Now we need to prevent the value from being re-added. + // If we're working with a list, target every input + if ( list && ( input_type == 'checkbox' || input_type == 'radio' ) ) { + jQuery("#ninja_forms_field_" + target_field + "_div_wrap").find(".ninja-forms-field").each(function(){ + jQuery(this).removeClass('ninja-forms-field-calc-no-new-op'); + }); + } else { + jQuery(target_element).removeClass('ninja-forms-field-calc-no-new-op'); + } + } + + if ( jQuery( target_element ).attr('type') != 'file' ) { + if ( list && ( input_type == 'checkbox' || input_type == 'radio' ) ) { + jQuery("#ninja_forms_field_" + target_field + "_div_wrap").find(".ninja-forms-field").each(function(){ + jQuery(this).change(); + jQuery(this).removeClass('ninja-forms-field-calc-no-new-op'); + }); + } else { + jQuery(target_element).change(); + jQuery(target_element).removeClass('ninja-forms-field-calc-no-new-op'); + } + + } + } + + }else{ + if ( list ) { + if ( input_type == 'checkbox' || input_type == 'radio' ) { + var target_element = jQuery("#ninja_forms_field_" + target_field + "_div_wrap").find(".ninja-forms-field:visible:first"); + } else { + var target_element = jQuery("#ninja_forms_field_" + target_field); + } + } else { + var target_element = jQuery("#ninja_forms_field_" + target_field); + } + var was_visible = jQuery( "#ninja_forms_field_" + target_field + "_div_wrap" ).data( "visible" ); + //var was_visible = jQuery("#ninja_forms_field_" + target_field + "_div_wrap").is(":visible"); + jQuery("#ninja_forms_field_" + target_field + "_div_wrap").hide(10, function(e){ jQuery(document).triggerHandler('ninja_forms_conditional_hide'); }); + jQuery("#ninja_forms_field_" + target_field + "_div_wrap").data("visible", false); + if ( was_visible ) { + // Check to see if we're working with a field that's listening for a calculation. + if ( jQuery( target_element ).hasClass("ninja-forms-field-calc-listen") ) { + // Since we are going to be hiding a field upon which a calculation is based, we need to set the oldValue of our calculation to the current field's value. + jQuery(target_element).data( "oldValue", jQuery(target_element).val() ); + // Now we need to prevent the value from being re-added. + if ( list && ( input_type == 'checkbox' || input_type == 'radio' ) ) { + jQuery("#ninja_forms_field_" + target_field + "_div_wrap").find(".ninja-forms-field").each(function(){ + jQuery(this).addClass('ninja-forms-field-calc-no-new-op'); + }); + } else { + jQuery(target_element).addClass('ninja-forms-field-calc-no-new-op'); + } + + } + + if ( jQuery( target_element ).attr('type') != 'file' ) { + if ( list && ( input_type == 'checkbox' || input_type == 'radio' ) ) { + jQuery("#ninja_forms_field_" + target_field + "_div_wrap").find(".ninja-forms-field").each(function(){ + jQuery(this).change(); + jQuery(this).addClass('ninja-forms-field-calc-no-old-op'); + }); + } else { + jQuery(target_element).change(); + jQuery(target_element).addClass('ninja-forms-field-calc-no-old-op'); + } + } + } + } + + }else if(action == 'hide'){ + if(pass){ + if ( list ) { + if ( input_type == 'checkbox' || input_type == 'radio' ) { + var target_element = jQuery("#ninja_forms_field_" + target_field + "_div_wrap").find(".ninja-forms-field:visible:first"); + } else { + var target_element = jQuery("#ninja_forms_field_" + target_field); + } + } else { + var target_element = jQuery("#ninja_forms_field_" + target_field); + } + var was_visible = jQuery( "#ninja_forms_field_" + target_field + "_div_wrap" ).data( "visible" ); + //var was_visible = jQuery("#ninja_forms_field_" + target_field + "_div_wrap").is(":visible"); + jQuery("#ninja_forms_field_" + target_field + "_div_wrap").hide(); + jQuery("#ninja_forms_field_" + target_field + "_div_wrap").data("visible", false); + if ( was_visible ) { + // Check to see if we're working with a field that's listening for a calculation. + if ( jQuery( target_element ).hasClass("ninja-forms-field-calc-listen") ) { + // Since we are going to be hiding a field upon which a calculation is based, we need to set the oldValue of our calculation to the current field's value. + jQuery(target_element).data( "oldValue", jQuery(target_element).val() ); + // Now we need to prevent the value from being re-added. + if ( list && ( input_type == 'checkbox' || input_type == 'radio' ) ) { + jQuery("#ninja_forms_field_" + target_field + "_div_wrap").find(".ninja-forms-field").each(function(){ + jQuery(this).addClass('ninja-forms-field-calc-no-new-op'); + }); + } else { + jQuery(target_element).addClass('ninja-forms-field-calc-no-new-op'); + } + + } + + if ( jQuery( target_element ).attr('type') != 'file' ) { + if ( list && ( input_type == 'checkbox' || input_type == 'radio' ) ) { + jQuery("#ninja_forms_field_" + target_field + "_div_wrap").find(".ninja-forms-field").each(function(){ + jQuery(this).change(); + }); + } else { + jQuery(target_element).change(); + } + } + } + }else{ + var was_visible = jQuery( "#ninja_forms_field_" + target_field + "_div_wrap" ).data( "visible" ); + //var was_visible = jQuery("#ninja_forms_field_" + target_field + "_div_wrap").is(":visible"); + jQuery("#ninja_forms_field_" + target_field + "_div_wrap").show(); + jQuery("#ninja_forms_field_" + target_field + "_div_wrap").data("visible", true); + if ( list ) { + if ( input_type == 'checkbox' || input_type == 'radio' ) { + var target_element = jQuery("#ninja_forms_field_" + target_field + "_div_wrap").find(".ninja-forms-field:visible:first"); + } else { + var target_element = jQuery("#ninja_forms_field_" + target_field); + } + } else { + var target_element = jQuery("#ninja_forms_field_" + target_field); + } + + if ( !was_visible ) { + // Check to see if we're working with a field that's listening for a calculation. + if ( jQuery( target_element ).hasClass("ninja-forms-field-calc-listen") ) { + // Since we are going to be hiding a field upon which a calculation is based, we need to set the oldValue of our calculation to the current field's value. + jQuery(target_element).data( "oldValue", '' ); + // Now we need to prevent the value from being re-added. + // If we're working with a list, target every input + if ( list && ( input_type == 'checkbox' || input_type == 'radio' ) ) { + jQuery("#ninja_forms_field_" + target_field + "_div_wrap").find(".ninja-forms-field").each(function(){ + jQuery(this).removeClass('ninja-forms-field-calc-no-new-op'); + }); + } else { + jQuery(target_element).removeClass('ninja-forms-field-calc-no-new-op'); + } + } + + if ( jQuery( target_element ).attr('type') != 'file' ) { + if ( list && ( input_type == 'checkbox' || input_type == 'radio' ) ) { + jQuery("#ninja_forms_field_" + target_field + "_div_wrap").find(".ninja-forms-field").each(function(){ + jQuery(this).change(); + jQuery(this).removeClass('ninja-forms-field-calc-no-new-op'); + }); + } else { + jQuery(target_element).change(); + jQuery(target_element).removeClass('ninja-forms-field-calc-no-new-op'); + } + + } + } + } + }else if(action == 'change_value'){ + + var was_checked = jQuery( "#ninja_forms_field_" + target_field + "_div_wrap" ).data( "checked" ); + + if(input_type == 'checkbox'){ + if ( list ) { + var checked_now = jQuery("[name='ninja_forms_field_" + target_field + "\\[\\]'][value='" + value + "']").prop("checked"); + if(pass){ + jQuery("[name='ninja_forms_field_" + target_field + "\\[\\]'][value='" + value + "']").prop("checked", true); + console.log("test"); + }else{ + jQuery("[name='ninja_forms_field_" + target_field + "\\[\\]'][value='" + value + "']").prop("checked", false); + } + } else { + var checked_now = jQuery("#ninja_forms_field_" + target_field).prop("checked"); + if( pass ){ + if(value == 'checked'){ + jQuery("#ninja_forms_field_" + target_field).prop("checked", true); + }else if(value == 'unchecked'){ + jQuery("#ninja_forms_field_" + target_field).prop("checked", false); + } + // Manually trigger change + jQuery("#ninja_forms_field_" + target_field).trigger( "change" ); + } + } + + }else if(input_type == 'radio'){ + if(pass){ + jQuery("[name='ninja_forms_field_" + target_field + "'][value='" + value + "']").prop("checked", true); + }else{ + jQuery("[name='ninja_forms_field_" + target_field + "'][value='" + value + "']").prop("checked", false); + } + + }else{ + if(pass){ + jQuery("#ninja_forms_field_" + target_field).val(value); + } + } + + if ( list ) { + if ( input_type == 'checkbox' || input_type == 'radio' ) { + var target_element = jQuery("[name='ninja_forms_field_" + target_field + "\\[\\]'][value='" + value + "']"); + } else { + var target_element = jQuery("#ninja_forms_field_" + target_field); + } + } else { + var target_element = jQuery("#ninja_forms_field_" + target_field); + + } + + jQuery( "#ninja_forms_field_" + target_field + "_div_wrap" ).data( "checked", checked_now ); + + if ( i == conditional_length - 1 ) { + jQuery( target_element ).change(); + } + + + }else if(action == 'remove_value'){ + if(input_type == 'dropdown'){ + if(pass){ + var selected_var = jQuery("#ninja_forms_field_" + target_field).val(); + if(selected_var == value){ + var next_val = jQuery("#ninja_forms_field_" + target_field + " option[value='" + value + "']").next().val(); + jQuery("#ninja_forms_field_" + target_field).val(next_val); + } + jQuery("#ninja_forms_field_" + target_field + " option[value='" + value + "']").hide(); + jQuery("#ninja_forms_field_" + target_field + " option[value='" + value + "']").attr("disabled", true); + }else{ + jQuery("#ninja_forms_field_" + target_field + " option[value='" + value + "']").show(); + jQuery("#ninja_forms_field_" + target_field + " option[value='" + value + "']").attr("disabled", false); + } + }else if(input_type == 'multi'){ + if(pass){ + var selected_var = jQuery("#ninja_forms_field_" + target_field).val(); + if(!!selected_var){ + var index = selected_var.indexOf(value); + if(index != -1){ + selected_var.splice(index, 1); + jQuery("#ninja_forms_field_" + target_field).val(selected_var); + } + } + var opt_index = jQuery("#ninja_forms_field_" + target_field + " option[value='" + value + "']").prop("index"); + var clone = jQuery("#ninja_forms_field_" + target_field + " option[value='" + value + "']").clone(); + jQuery(clone).attr("title", opt_index); + jQuery("#ninja_forms_field_" + target_field + "_clone").append(clone); + jQuery("#ninja_forms_field_" + target_field + " option[value='" + value + "']").remove(); + + }else{ + var clone = jQuery("#ninja_forms_field_" + target_field + "_clone option[value='" + value + "']"); + var opt_index = jQuery(clone).attr("title"); + opt_index++; + var selected_var = jQuery("#ninja_forms_field_" + target_field).val(); + jQuery("#ninja_forms_field_" + target_field + " option:nth-child(" + opt_index + ")").before(clone); + jQuery("#ninja_forms_field_" + target_field).val(selected_var); + } + }else if(input_type == 'checkbox' || input_type == 'radio'){ + if(pass){ + jQuery("input[name^=ninja_forms_field_" + target_field + "][value='" + value + "']").attr("checked", false); + jQuery("input[name^=ninja_forms_field_" + target_field + "][value='" + value + "']").parent().hide(); + }else{ + jQuery("input[name^=ninja_forms_field_" + target_field + "][value='" + value + "']").parent().show(); + } + } + if ( list ) { + if ( input_type == 'checkbox' || input_type == 'radio' ) { + var target_element = jQuery("#ninja_forms_field_" + target_field + "_div_wrap").find(".ninja-forms-field:visible:first"); + } else { + var target_element = jQuery("#ninja_forms_field_" + target_field); + } + } else { + var target_element = jQuery("#ninja_forms_field_" + target_field); + } + //jQuery(target_element).change(); + }else if(action == 'add_value'){ + if( typeof value !== "undefined" ){ + if(typeof value.value === "undefined" || value.value == "_ninja_forms_no_value"){ + value.value = value.label; + } + var form_id = ninja_forms_get_form_id( jQuery("#ninja_forms_field_" + target_field ) ); + if ( typeof window['ninja_forms_form_' + form_id + '_calc_settings'].calc_value[ target_field ] !== 'undefined' ) { + window['ninja_forms_form_' + form_id + '_calc_settings'].calc_value[ target_field ][ value.value ] = value.calc; + } + + if(input_type == "dropdown" || input_type == "multi"){ + if(pass){ + var current_count = jQuery("#ninja_forms_field_" + target_field + " option[value='" + value.value + "']").length; + if(current_count == 0){ + jQuery("#ninja_forms_field_" + target_field).append(""); + } + }else{ + jQuery("#ninja_forms_field_" + target_field + " option[value='" + value.value + "']").remove(); + } + }else if(input_type == "checkbox" || input_type == "radio"){ + if(pass){ + var current_count = jQuery("input[name^=ninja_forms_field_" + target_field + "][value='" + value.value + "']").length; + if(current_count == 0){ + var clone = jQuery("#ninja_forms_field_" + target_field + "_template").clone(); + var count = jQuery(".ninja-forms-field-" + target_field + "-options").length; + var label_id = jQuery(clone).prop("id").replace("template", count); + jQuery(clone).prop("id", label_id); + var checkbox_id = jQuery(clone).find(":checkbox").prop("id") + count; + if(input_type == "checkbox"){ + var checkbox_name = "ninja_forms_field_" + target_field + "[]"; + }else{ + var checkbox_name = "ninja_forms_field_" + target_field; + } + + jQuery(clone).find(":" + input_type).prop("id", checkbox_id); + jQuery(clone).find(":" + input_type).attr("name", checkbox_name); + jQuery(clone).find(":" + input_type).val(value.value); + jQuery(clone).find(":" + input_type).after(value.label); + jQuery(clone).attr("style", ""); + + jQuery("#ninja_forms_field_" + target_field + "_options_span").find("ul").append(clone); + } + }else{ + jQuery("input[name^=ninja_forms_field_" + target_field + "][value='" + value.value + "']").parent().remove(); + } + } + } + if ( list ) { + if ( input_type == 'checkbox' || input_type == 'radio' ) { + var target_element = jQuery("#ninja_forms_field_" + target_field + "_div_wrap").find(".ninja-forms-field:visible:first"); + } else { + var target_element = jQuery("#ninja_forms_field_" + target_field); + } + } else { + var target_element = jQuery("#ninja_forms_field_" + target_field); + } + //jQuery(target_element).change(); + }else{ + //Put code here to call javascript function. + pass = pass_array[i]; + result = window[action](pass, target_field, element); + } + } +} + +function ninja_forms_conditional_compare(param1, param2, op){ + + switch(op) { + case "==": + return param1 == param2; + case "!=": + return param1 != param2; + case "<": + return param1 < param2; + case ">": + return param1 > param2; + } +} + +function is_numeric (mixed_var) { + return (typeof(mixed_var) === 'number' || typeof(mixed_var) === 'string') && mixed_var !== '' && !isNaN(mixed_var); +} \ No newline at end of file diff --git a/deprecated/js/min/ninja-forms-conditionals-admin.min.js b/deprecated/js/min/ninja-forms-conditionals-admin.min.js new file mode 100644 index 0000000..0d86973 --- /dev/null +++ b/deprecated/js/min/ninja-forms-conditionals-admin.min.js @@ -0,0 +1 @@ +_.templateSettings={evaluate:/<#([\s\S]+?)#>/g,interpolate:/<#=([\s\S]+?)#>/g},jQuery(document).ready(function(i){function n(n){var e=i("input[name^=ninja_forms_field_"+n+"]"),t=jQuery(e).serializeFullArray();return t}console.log("Loaded"),nf_cl.getParam=function(i){for(var n in nf_cl.cr_param_groups)for(var e in nf_cl.cr_param_groups[n])if(nf_cl.cr_param_groups[n][e].id==i)return nf_cl.cr_param_groups[n][e];return null};var e=Backbone.View.extend({el:i("#nf_cl_conditions"),events:{"click .add-condition":"addCondition","click .delete-condition":"deleteCondition","click .add-cr":"addCriteria","click .delete-cr":"deleteCriteria","change .cr-param":"changeParam"},initialize:function(){_.bindAll(this,"render"),this.render()},render:function(){if("undefined"!=typeof nf_cl.conditions)for(id in nf_cl.conditions)r.render(id)},addCondition:function(n){n.preventDefault(),"DIV"==n.target.tagName?i(n.target).parent().hide():i(n.target).hide(),r.render("new")},deleteCondition:function(n){n.preventDefault(),i(n.target).parent().parent().parent().remove(),i(this.el).find(".nf-cl-add").show()},addCriteria:function(n){n.preventDefault();var e=i(n.target).data("cond-id"),t="new",a="",r="",l="==";o.conditionEl=i("#nf_cl_condition_"+e),o.renderCriteriaRow(e,t,nf_cl.cr_param_groups,a,r,l)},deleteCriteria:function(n){n.preventDefault(),targetEl=i(n.target).parent().parent(),i(targetEl).remove()},changeParam:function(n){n.preventDefault();var e=i(n.target).data("cr-id"),t=i(n.target).data("cond-id"),a=i(n.target).val(),r="",l="";if("new"==e)var c=i(n.target).data("num"),d="conditions["+t+"][criteria][new]["+c+"]",_="nf_cr_new_"+c;else var c="",d="conditions["+t+"][criteria]["+e+"]",_="nf_cr_"+e;o.renderCriteriaCompare(e,d,nf_cl.cr_param_groups,a,l,c,_),o.renderCriteriaValue(e,d,nf_cl.cr_param_groups,a,r,c,_);var f=nf_cl.getParam(a);"date"==f.type&&i(n.target).next().next().find("input").datepicker({dateFormat:ninja_forms_settings.date_format})}}),t=Backbone.View.extend({el:i("#nf_cl_conditions"),initialize:function(){_.bindAll(this,"render")},render:function(n){if("undefined"!=typeof nf_cl.conditions[n])var e=nf_cl.conditions[n].action,t=nf_cl.conditions[n].connector;else var e="",t="";var a=_.template(i("#tmpl-nf-cl-condition").html())({cond_id:n,action:e,connector:t});i(this.el).append(a),o.renderCriteriaRows(n)}}),a=Backbone.View.extend({conditionEl:"",initialize:function(){_.bindAll(this,"render")},renderCriteriaRows:function(n){if("new"==n)return!1;this.conditionEl=i("#nf_cl_condition_"+n);var e=nf_cl.conditions[n],t=e.criteria,a=this;_.each(t,function(i){var e=i.id,t=i.param,o=i.value,r=i.compare;a.renderCriteriaRow(n,e,nf_cl.cr_param_groups,t,o,r)})},renderCriteriaRow:function(n,e,t,a,o,r){if("new"==e)var l=i(this.conditionEl).find(".single-criteria").length,c="conditions["+n+"][criteria][new]["+l+"]",d="nf_cr_new_"+l,f="new-"+l;else var l="",c="conditions["+n+"][criteria]["+e+"]",d="nf_cr_"+e,f=e;var s=_.template(i("#tmpl-nf-cl-criteria").html())({cr_id:e,cr_name:c,param_groups:t,selected_param:a,value:o,compare:r,num:l,div_id:d,data_id:f,cond_id:n});i(this.conditionEl).find(".nf-cl-criteria").append(s),this.renderCriteriaCompare(e,c,t,a,r,l,d),this.renderCriteriaValue(e,c,t,a,o,l,d)},renderCriteriaCompare:function(n,e,t,a,o,r,l){var c=i("#"+l).find(".cr-compare"),d=_.template(i("#tmpl-nf-cl-criteria-compare").html())({cr_id:n,cr_name:e,param_groups:t,selected_param:a,compare:o,nf_cl:nf_cl});i(c).html(d)},renderCriteriaValue:function(n,e,t,a,o,r,l){var c=i("#"+l).find(".cr-value"),d=_.template(i("#tmpl-nf-cl-criteria-value").html())({cr_id:n,cr_name:e,param_groups:t,selected_param:a,value:o,num:r,nf_cl:nf_cl});i(c).html(d);var f=nf_cl.getParam(a);f&&"date"==f.type&&i(c).find("input").datepicker({dateFormat:ninja_forms_settings.date_format})}}),o=new a,r=new t;new e;i(document).on("change",".ninja-forms-field-list-show-value",function(){var n=this.id.replace("ninja_forms_field_","");n=n.replace("_list_show_value","");var e=new Object;this.checked?(i(".ninja-forms-field-"+n+"-list-option-value").each(function(){var t=this.id.replace("ninja_forms_field_"+n+"_list_option_","");t=t.replace("_value_span",""),e[t]=i(this).children(".ninja-forms-field-list-option-value").val()}),i(".ninja-forms-field-"+n+"-list-option-value").show(),i(".ninja-forms-field-conditional-cr-field").each(function(){this.value==n&&i(this).nextElementInDom(".ninja-forms-field-conditional-cr-value-list:first").children("option").each(function(){this.value=e[this.title]})}),i(".ninja-forms-field-"+n+"-conditional-value").each(function(){i(this).children("option").each(function(){this.value=e[this.title]})})):(i("#ninja_forms_field_"+n+"_list_options").children(".ninja-forms-field-"+n+"-list-option").find(".ninja-forms-field-list-option-label").each(function(){var t=i(this).parent().parent().parent().parent().parent().prop("id"),a=t.replace("ninja_forms_field_"+n+"_list_option_","");e[a]=this.value}),i(".ninja-forms-field-conditional-cr-field").each(function(){this.value==n&&i(this).nextElementInDom(".ninja-forms-field-conditional-cr-value-list:first").children("option").each(function(){this.value=e[this.title]})}),i(".ninja-forms-field-"+n+"-conditional-value").each(function(){i(this).children("option").each(function(){this.value=e[this.title]})}),i(".ninja-forms-field-"+n+"-list-option-value").hide())}),i(document).on("change",".ninja-forms-field-conditional-action",function(){var e=this.id.replace("action","value"),t=(this.id.replace("action","value_label"),i("#_form_id").val()),a=i(this).parent().parent().attr("name"),o=(i("#ninja_forms_field_"+a+"_conditional_value_type").val(),i("#ninja_forms_field_"+a+"_list_show_value").prop("checked")),r=i(".ninja-forms-field-"+a+"-conditional").length;r--;var l=this.value,c=n(a);i.post(ajaxurl,{form_id:t,field_id:a,x:r,action_slug:l,field_data:c,action:"ninja_forms_change_action"},function(n){i("#ninja_forms_field_"+a+"_"+r+"_value_span").prop("innerHTML",n.new_html),"list"==n.new_type&&(i("#"+e).children().remove().end(),i(".ninja-forms-field-"+a+"-list-option").each(function(){var n=i(this).find(".ninja-forms-field-list-option-label").val();if(o)var t=i(this).find(".ninja-forms-field-list-option-value").val();else var t=n;var r=this.id.replace("ninja_forms_field_"+a+"_list_option_","");i("#"+e).append('")}))})}),i(document).on("click",".ninja-forms-field-add-conditional",function(n){n.preventDefault();var e=this.id.replace("ninja_forms_field_","");e=e.replace("_add_conditional","");var t=i("#_form_id").val(),a=i(".ninja-forms-field-"+e+"-conditional").length;i.post(ajaxurl,{form_id:t,field_id:e,x:a,action:"ninja_forms_add_conditional"},function(n){i("#ninja_forms_field_"+e+"_conditionals").append(n)})}),i(document).on("click",".ninja-forms-field-remove-conditional",function(n){n.preventDefault();var e=this.id.replace("ninja_forms_field_","");e=e.replace("_remove_conditional","");var t=this.name;i("#ninja_forms_field_"+e+"_conditional_"+t).remove()}),i(document).on("click",".ninja-forms-field-add-cr",function(n){n.preventDefault();var e=this.id.replace("ninja_forms_field_","");e=e.replace("_add_cr","");var t=i("#_form_id").val(),a=this.name,o=i(".ninja-forms-field-"+e+"-conditional-"+a+"-cr").length;i.post(ajaxurl,{form_id:t,field_id:e,x:a,y:o,action:"ninja_forms_add_cr"},function(n){i("#ninja_forms_field_"+e+"_conditional_"+a+"_cr").append(n.new_html);var t="",o="";i(".ninja-forms-field-title").each(function(){t=this.innerHTML,t.length>30&&(t=t.substring(0,30)+"..."),o=this.id.replace("ninja_forms_field_",""),o=o.replace("_title",""),i(".ninja-forms-field-conditional-cr-field > option").each(function(){this.value==o&&(this.text="ID: "+o+" - "+t)})})})}),i(document).on("click",".ninja-forms-field-remove-cr",function(n){n.preventDefault();var e=this.id.replace("ninja_forms_field_","");e=e.replace("_remove_cr","");var t=this.name,a=this.rel;i("#ninja_forms_field_"+e+"_conditional_"+t+"_cr_"+a).remove()}),i(document).on("change",".ninja-forms-field-conditional-cr-field",function(){var n=this.id.replace("ninja_forms_field_","");n=n.replace("_cr_field","");var e=this.title.split("_"),t=e[0],a=e[1],o=this.value;""!=this.value?i.post(ajaxurl,{field_id:n,field_value:o,x:t,y:a,output_options:1,action:"ninja_forms_change_cr_field"},function(e){if(i("#ninja_forms_field_"+n+"_conditional_"+t+"_cr_"+a+"_value").prop("innerHTML",e.new_html),"list"==e.new_type){var r=nfFields.get(o);if("undefined"!=typeof r.get("list")&&"undefined"!==r.get("list").options){var l=0;i('select[name="ninja_forms_field_'+n+"\\[conditional\\]\\["+t+"\\]\\[cr\\]\\["+a+'\\]\\[value\\]"]').find("option").remove().end(),_.each(r.get("list").options,function(e){var o=e.label,c=e.value;e.calc;0==r.get("list_show_value")&&(c=o),i('select[name="ninja_forms_field_'+n+"\\[conditional\\]\\["+t+"\\]\\[cr\\]\\["+a+'\\]\\[value\\]"]').append('"),l++})}i(".ninja-forms-field-"+o+"-list-option").length>0&&(i('select[name="ninja_forms_field_'+n+"\\[conditional\\]\\["+t+"\\]\\[cr\\]\\["+a+'\\]\\[value\\]"]').find("option").remove().end(),i(".ninja-forms-field-"+o+"-list-option").each(function(){var e=i(this).find(".ninja-forms-field-list-option-label").val();if(1==i("#ninja_forms_field_"+o+"_list_show_value").prop("checked"))var r=i(this).find(".ninja-forms-field-list-option-value").val();else var r=e;var l=this.id.replace("ninja_forms_field_"+o+"_list_option_","");i('select[name="ninja_forms_field_'+n+"\\[conditional\\]\\["+t+"\\]\\[cr\\]\\["+a+'\\]\\[value\\]"]').append('")}))}}):i("#ninja_forms_field_"+n+"_conditional_"+t+"_cr_"+a+"_value").prop("innerHTML","")}),i(document).on("change",".ninja-forms-notification-conditional-cr-field",function(){var n=this.id.replace("ninja_forms_field_","");n=n.replace("_cr_field","");var e=this.title.split("_"),t=e[0],a=e[1],o=this.value;""!=this.value?i.post(ajaxurl,{field_id:n,field_value:o,x:t,y:a,output_options:1,action:"ninja_forms_change_cr_field"},function(e){i("#ninja_forms_field_"+n+"_conditional_"+t+"_cr_"+a+"_value").prop("innerHTML",e.new_html),"list"==e.new_type&&i(".ninja-forms-field-"+o+"-list-option").each(function(){var e=i(this).find(".ninja-forms-field-list-option-label").val();if(1==i("#ninja_forms_field_"+o+"_list_show_value").prop("checked"))var r=i(this).find(".ninja-forms-field-list-option-value").val();else var r=e;var l=this.id.replace("ninja_forms_field_"+o+"_list_option_","");i('select[name="ninja_forms_field_'+n+"\\[conditional\\]\\["+t+"\\]\\[cr\\]\\["+a+'\\]\\[value\\]"]').append('")})}):i("#ninja_forms_field_"+n+"_conditional_"+t+"_cr_"+a+"_value").prop("innerHTML","")}),i(document).on("addField.clAdd",function(n,e){i(".ninja-forms-field-conditional-cr-field").each(function(){i(this).append('")})}),i(document).on("removeField.clRemove",function(n,e){i(".ninja-forms-field-conditional-cr-field").each(function(){i(this).children("option").each(function(){this.value==e&&i(this).remove()})})})}); \ No newline at end of file diff --git a/deprecated/js/min/ninja-forms-conditionals-display.min.js b/deprecated/js/min/ninja-forms-conditionals-display.min.js new file mode 100644 index 0000000..5aba454 --- /dev/null +++ b/deprecated/js/min/ninja-forms-conditionals-display.min.js @@ -0,0 +1 @@ +function ninja_forms_check_conditional(element,action_value){var form_id=ninja_forms_get_form_id(element),conditional=eval("ninja_forms_form_"+form_id+"_conditionals_settings");conditional=conditional.conditionals;var field_id=jQuery(element).attr("rel");for(field in conditional){var target_field=field.replace("field_",""),conditional_length=jQuery(conditional[field].conditional).length;for(i=0;i"+value.label+"")}else jQuery("#ninja_forms_field_"+target_field+" option[value='"+value.value+"']").remove();else if("checkbox"==input_type||"radio"==input_type)if(pass){var current_count=jQuery("input[name^=ninja_forms_field_"+target_field+"][value='"+value.value+"']").length;if(0==current_count){var clone=jQuery("#ninja_forms_field_"+target_field+"_template").clone(),count=jQuery(".ninja-forms-field-"+target_field+"-options").length,label_id=jQuery(clone).prop("id").replace("template",count);jQuery(clone).prop("id",label_id);var checkbox_id=jQuery(clone).find(":checkbox").prop("id")+count;if("checkbox"==input_type)var checkbox_name="ninja_forms_field_"+target_field+"[]";else var checkbox_name="ninja_forms_field_"+target_field;jQuery(clone).find(":"+input_type).prop("id",checkbox_id),jQuery(clone).find(":"+input_type).attr("name",checkbox_name),jQuery(clone).find(":"+input_type).val(value.value),jQuery(clone).find(":"+input_type).after(value.label),jQuery(clone).attr("style",""),jQuery("#ninja_forms_field_"+target_field+"_options_span").find("ul").append(clone)}}else jQuery("input[name^=ninja_forms_field_"+target_field+"][value='"+value.value+"']").parent().remove()}if(list)if("checkbox"==input_type||"radio"==input_type)var target_element=jQuery("#ninja_forms_field_"+target_field+"_div_wrap").find(".ninja-forms-field:visible:first");else var target_element=jQuery("#ninja_forms_field_"+target_field);else var target_element=jQuery("#ninja_forms_field_"+target_field)}else pass=pass_array[i],result=window[action](pass,target_field,element)}}function ninja_forms_conditional_compare(e,i,a){switch(a){case"==":return e==i;case"!=":return e!=i;case"<":return i>e;case">":return e>i}}function is_numeric(e){return("number"==typeof e||"string"==typeof e)&&""!==e&&!isNaN(e)}Array.prototype.remove=function(e,i){var a=this.slice((i||e)+1||this.length);return this.length=0>e?this.length+e:e,this.push.apply(this,a)},jQuery(document).ready(function(e){e(document).on("change",".ninja-forms-field-conditional-listen",function(){ninja_forms_check_conditional(this,!0)}),e(document).on("keyup",".ninja-forms-field-conditional-listen",function(){ninja_forms_check_conditional(this,!0)}),e(".ninja-forms-form").on("beforeSubmit.clPreventSubmit",function(i,a,n){var t=e(n).prop("id").replace("ninja_forms_form_","");return 1!=e("#nf_submit_"+t).parent().data("visible")?(e("#nf_processing_"+t).hide(),e("#nf_submit_"+t).show(),!1):void 0})}); \ No newline at end of file diff --git a/deprecated/lang/ninja-forms-conditionals.po b/deprecated/lang/ninja-forms-conditionals.po new file mode 100644 index 0000000..fdf9c22 --- /dev/null +++ b/deprecated/lang/ninja-forms-conditionals.po @@ -0,0 +1,173 @@ +msgid "" +msgstr "" +"Project-Id-Version: ninja-forms-conditionals\n" +"POT-Creation-Date: 2014-11-15 15:22-0500\n" +"PO-Revision-Date: 2014-11-15 15:22-0500\n" +"Last-Translator: \n" +"Language-Team: WP Ninjas \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 1.5.7\n" +"X-Poedit-KeywordsList: _;gettext;gettext_noop;__;_e;_x;_n\n" +"X-Poedit-Basepath: .\n" +"X-Poedit-SearchPath-0: .\n" + +#: classes/trigger-base.php:45 +#: includes/admin/register-edit-field-section.php:162 +#: includes/admin/scripts.php:67 +msgid "Equal To" +msgstr "" + +#: classes/trigger-base.php:46 +#: includes/admin/register-edit-field-section.php:163 +#: includes/admin/scripts.php:68 +msgid "Not Equal To" +msgstr "" + +#: classes/trigger-base.php:47 +#: includes/admin/register-edit-field-section.php:164 +#: includes/admin/scripts.php:69 +msgid "Less Than" +msgstr "" + +#: classes/trigger-base.php:48 +#: includes/admin/register-edit-field-section.php:165 +#: includes/admin/scripts.php:70 +msgid "Greater Than" +msgstr "" + +#: classes/trigger-base.php:49 includes/admin/scripts.php:71 +msgid "Contains" +msgstr "" + +#: classes/trigger-base.php:50 includes/admin/scripts.php:72 +msgid "Does Not Contain" +msgstr "" + +#: classes/trigger-base.php:51 includes/admin/scripts.php:73 +msgid "On" +msgstr "" + +#: classes/trigger-base.php:52 includes/admin/scripts.php:74 +msgid "Before" +msgstr "" + +#: classes/trigger-base.php:53 includes/admin/scripts.php:75 +msgid "After" +msgstr "" + +#: classes/trigger-date-submitted.php:21 +msgid "Date Submitted" +msgstr "" + +#: classes/trigger-sub-count.php:21 +msgid "Number Of Submissions" +msgstr "" + +#: includes/admin/notifications.php:12 +msgid "Conditional Processing" +msgstr "" + +#: includes/admin/notifications.php:15 +msgid "Add" +msgstr "" + +#: includes/admin/notifications.php:25 +msgid "Process This" +msgstr "" + +#: includes/admin/notifications.php:26 +msgid "Do Not Process This" +msgstr "" + +#: includes/admin/notifications.php:28 +#: includes/admin/register-edit-field-section.php:90 +msgid "If" +msgstr "" + +#: includes/admin/notifications.php:30 +#: includes/admin/register-edit-field-section.php:92 +msgid "All" +msgstr "" + +#: includes/admin/notifications.php:31 +#: includes/admin/register-edit-field-section.php:93 +msgid "Any" +msgstr "" + +#: includes/admin/notifications.php:33 +msgid "of the following criteria are met" +msgstr "" + +#: includes/admin/notifications.php:33 +#: includes/admin/register-edit-field-section.php:95 +msgid "Add Criteria" +msgstr "" + +#: includes/admin/notifications.php:44 +msgid "Select One" +msgstr "" + +#: includes/admin/register-edit-field-section.php:20 +msgid "Conditional Statements" +msgstr "" + +#: includes/admin/register-edit-field-section.php:20 +msgid "Add Conditional Statement" +msgstr "" + +#: includes/admin/register-edit-field-section.php:59 +msgid "Remove condition" +msgstr "" + +#: includes/admin/register-edit-field-section.php:61 +msgid "-- Action" +msgstr "" + +#: includes/admin/register-edit-field-section.php:71 +msgid "Show This" +msgstr "" + +#: includes/admin/register-edit-field-section.php:72 +msgid "Hide This" +msgstr "" + +#: includes/admin/register-edit-field-section.php:73 +msgid "Change Value" +msgstr "" + +#: includes/admin/register-edit-field-section.php:95 +msgid "of the following critera are met" +msgstr "" + +#: includes/admin/register-edit-field-section.php:133 +msgid "-- Field" +msgstr "" + +#: includes/admin/register-edit-field-section.php:154 +msgid "ID" +msgstr "" + +#: includes/admin/scripts.php:99 +msgid "Checked" +msgstr "" + +#: includes/admin/scripts.php:100 +msgid "Unchecked" +msgstr "" + +#: includes/admin/scripts.php:149 +msgid "Triggers" +msgstr "" + +#: includes/admin/scripts.php:150 +msgid "Fields" +msgstr "" + +#: includes/admin/upgrades/nf-update-notice.php:18 +#, php-format +msgid "" +"This version of Conditional Logic requires at least version 2.8.6 of Ninja " +"Forms. Please visit your plugins page to update." +msgstr "" diff --git a/deprecated/lang/ninja-forms-conditionals.pot b/deprecated/lang/ninja-forms-conditionals.pot new file mode 100644 index 0000000..fdf9c22 --- /dev/null +++ b/deprecated/lang/ninja-forms-conditionals.pot @@ -0,0 +1,173 @@ +msgid "" +msgstr "" +"Project-Id-Version: ninja-forms-conditionals\n" +"POT-Creation-Date: 2014-11-15 15:22-0500\n" +"PO-Revision-Date: 2014-11-15 15:22-0500\n" +"Last-Translator: \n" +"Language-Team: WP Ninjas \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 1.5.7\n" +"X-Poedit-KeywordsList: _;gettext;gettext_noop;__;_e;_x;_n\n" +"X-Poedit-Basepath: .\n" +"X-Poedit-SearchPath-0: .\n" + +#: classes/trigger-base.php:45 +#: includes/admin/register-edit-field-section.php:162 +#: includes/admin/scripts.php:67 +msgid "Equal To" +msgstr "" + +#: classes/trigger-base.php:46 +#: includes/admin/register-edit-field-section.php:163 +#: includes/admin/scripts.php:68 +msgid "Not Equal To" +msgstr "" + +#: classes/trigger-base.php:47 +#: includes/admin/register-edit-field-section.php:164 +#: includes/admin/scripts.php:69 +msgid "Less Than" +msgstr "" + +#: classes/trigger-base.php:48 +#: includes/admin/register-edit-field-section.php:165 +#: includes/admin/scripts.php:70 +msgid "Greater Than" +msgstr "" + +#: classes/trigger-base.php:49 includes/admin/scripts.php:71 +msgid "Contains" +msgstr "" + +#: classes/trigger-base.php:50 includes/admin/scripts.php:72 +msgid "Does Not Contain" +msgstr "" + +#: classes/trigger-base.php:51 includes/admin/scripts.php:73 +msgid "On" +msgstr "" + +#: classes/trigger-base.php:52 includes/admin/scripts.php:74 +msgid "Before" +msgstr "" + +#: classes/trigger-base.php:53 includes/admin/scripts.php:75 +msgid "After" +msgstr "" + +#: classes/trigger-date-submitted.php:21 +msgid "Date Submitted" +msgstr "" + +#: classes/trigger-sub-count.php:21 +msgid "Number Of Submissions" +msgstr "" + +#: includes/admin/notifications.php:12 +msgid "Conditional Processing" +msgstr "" + +#: includes/admin/notifications.php:15 +msgid "Add" +msgstr "" + +#: includes/admin/notifications.php:25 +msgid "Process This" +msgstr "" + +#: includes/admin/notifications.php:26 +msgid "Do Not Process This" +msgstr "" + +#: includes/admin/notifications.php:28 +#: includes/admin/register-edit-field-section.php:90 +msgid "If" +msgstr "" + +#: includes/admin/notifications.php:30 +#: includes/admin/register-edit-field-section.php:92 +msgid "All" +msgstr "" + +#: includes/admin/notifications.php:31 +#: includes/admin/register-edit-field-section.php:93 +msgid "Any" +msgstr "" + +#: includes/admin/notifications.php:33 +msgid "of the following criteria are met" +msgstr "" + +#: includes/admin/notifications.php:33 +#: includes/admin/register-edit-field-section.php:95 +msgid "Add Criteria" +msgstr "" + +#: includes/admin/notifications.php:44 +msgid "Select One" +msgstr "" + +#: includes/admin/register-edit-field-section.php:20 +msgid "Conditional Statements" +msgstr "" + +#: includes/admin/register-edit-field-section.php:20 +msgid "Add Conditional Statement" +msgstr "" + +#: includes/admin/register-edit-field-section.php:59 +msgid "Remove condition" +msgstr "" + +#: includes/admin/register-edit-field-section.php:61 +msgid "-- Action" +msgstr "" + +#: includes/admin/register-edit-field-section.php:71 +msgid "Show This" +msgstr "" + +#: includes/admin/register-edit-field-section.php:72 +msgid "Hide This" +msgstr "" + +#: includes/admin/register-edit-field-section.php:73 +msgid "Change Value" +msgstr "" + +#: includes/admin/register-edit-field-section.php:95 +msgid "of the following critera are met" +msgstr "" + +#: includes/admin/register-edit-field-section.php:133 +msgid "-- Field" +msgstr "" + +#: includes/admin/register-edit-field-section.php:154 +msgid "ID" +msgstr "" + +#: includes/admin/scripts.php:99 +msgid "Checked" +msgstr "" + +#: includes/admin/scripts.php:100 +msgid "Unchecked" +msgstr "" + +#: includes/admin/scripts.php:149 +msgid "Triggers" +msgstr "" + +#: includes/admin/scripts.php:150 +msgid "Fields" +msgstr "" + +#: includes/admin/upgrades/nf-update-notice.php:18 +#, php-format +msgid "" +"This version of Conditional Logic requires at least version 2.8.6 of Ninja " +"Forms. Please visit your plugins page to update." +msgstr "" diff --git a/gulpfile.js b/gulpfile.js new file mode 100644 index 0000000..cddfb10 --- /dev/null +++ b/gulpfile.js @@ -0,0 +1,103 @@ +/** + * Gulpfile + * + * Rename and Minify JavaScript... and more (later). + * + * Install Command: + * npm install gulp gulp-rename gulp-uglify + */ + +var gulp = require('gulp'); +var rename = require('gulp-rename'); +var uglify = require('gulp-uglify'); +var requirejsOptimize = require('gulp-requirejs-optimize'); +var sass = require('gulp-sass'); +var sourcemaps = require('gulp-sourcemaps'); +var autoprefixer = require('gulp-autoprefixer'); + +gulp.task('js', function () { + gulp.src('assets/js/builder/main.js') + .pipe(sourcemaps.init()) + .pipe(requirejsOptimize(function(file) { + return { + name: '../lib/almond', + optimize: 'uglify2', + wrap: true, + baseUrl: 'assets/js/builder/', + include: ['main'], + preserveLicenseComments: false + }; + })) + .pipe(rename('builder.js')) + .pipe(sourcemaps.write('/')) + .pipe(gulp.dest('assets/js/min/')); + + gulp.src('assets/js/front-end/main.js') + .pipe(sourcemaps.init()) + .pipe(requirejsOptimize(function(file) { + return { + name: '../lib/almond', + optimize: 'uglify2', + wrap: true, + baseUrl: 'assets/js/front-end/', + include: ['main'], + preserveLicenseComments: false + }; + })) + .pipe(rename('front-end.js')) + .pipe(sourcemaps.write('/')) + .pipe(gulp.dest('assets/js/min/')); +}); + +gulp.task('sass', function () { + gulp.src('assets/scss/admin/builder.scss') + .pipe(sourcemaps.init()) + .pipe(sass().on('error', sass.logError)) + .pipe(autoprefixer()) + .pipe(sourcemaps.write('/')) + .pipe(gulp.dest('assets/css')); + + gulp.src('assets/scss/front-end/display-structure.scss') + .pipe(sourcemaps.init()) + .pipe(sass().on('error', sass.logError)) + .pipe(autoprefixer()) + .pipe(sourcemaps.write('/')) + .pipe(gulp.dest('assets/css')); + + gulp.src('assets/scss/front-end/display-opinions.scss') + .pipe(sourcemaps.init()) + .pipe(sass().on('error', sass.logError)) + .pipe(autoprefixer()) + .pipe(sourcemaps.write('/')) + .pipe(gulp.dest('assets/css')); + + gulp.src('assets/scss/front-end/display-opinions-light.scss') + .pipe(sourcemaps.init()) + .pipe(sass().on('error', sass.logError)) + .pipe(autoprefixer()) + .pipe(sourcemaps.write('/')) + .pipe(gulp.dest('assets/css')); + + gulp.src('assets/scss/front-end/display-opinions-dark.scss') + .pipe(sourcemaps.init()) + .pipe(sass().on('error', sass.logError)) + .pipe(autoprefixer()) + .pipe(sourcemaps.write('/')) + .pipe(gulp.dest('assets/css')); +}); + +// Watch Files For Changes +gulp.task('watch', function() { + gulp.watch('assets/js/builder/**/*.js', ['js']); + gulp.watch('assets/js/front-end/**/*.js', ['js']); + gulp.watch('assets/scss/**/*.scss', ['sass']); +}); + +// Default Task +gulp.task('default', ['js', 'sass', 'watch']); + +function swallowError (error) { + //If you want details of the error in the console + console.log(error.toString()); + this.emit('end'); +} diff --git a/includes/Admin/Settings.php b/includes/Admin/Settings.php new file mode 100644 index 0000000..9c504cf --- /dev/null +++ b/includes/Admin/Settings.php @@ -0,0 +1,24 @@ +is_not_case_sensitive( $value ) ) { + $value = trim( $value, '"' ); + $value = strtolower( $value ); + $comparison = strtolower( $comparison ); + } + + return ( false !== strpos( $comparison, $value ) ); + } + + private function is_not_case_sensitive( $value ) + { + return ( 0 !== stripos( $value, '"' ) ); + } +} \ No newline at end of file diff --git a/includes/Comparators/Equal.php b/includes/Comparators/Equal.php new file mode 100644 index 0000000..7471489 --- /dev/null +++ b/includes/Comparators/Equal.php @@ -0,0 +1,9 @@ + $value ); + } +} \ No newline at end of file diff --git a/includes/Comparators/GreaterEqual.php b/includes/Comparators/GreaterEqual.php new file mode 100644 index 0000000..8730238 --- /dev/null +++ b/includes/Comparators/GreaterEqual.php @@ -0,0 +1,9 @@ += $value ); + } +} diff --git a/includes/Comparators/Less.php b/includes/Comparators/Less.php new file mode 100644 index 0000000..09eeec0 --- /dev/null +++ b/includes/Comparators/Less.php @@ -0,0 +1,9 @@ +is_not_case_sensitive( $value ) ) { + $value = trim( $value, '"' ); + $value = strtolower( $value ); + $comparison = strtolower( $comparison ); + } + + return ( false === strpos( $comparison, $value ) ); + } + + private function is_not_case_sensitive( $value ) + { + return ( 0 !== stripos( $value, '"' ) ); + } +} \ No newline at end of file diff --git a/includes/Comparators/NotEqual.php b/includes/Comparators/NotEqual.php new file mode 100644 index 0000000..8006601 --- /dev/null +++ b/includes/Comparators/NotEqual.php @@ -0,0 +1,9 @@ +when = $condition[ 'when' ]; + } + + if( isset( $condition[ 'then' ] ) ) { + $this->then = $condition[ 'then' ]; + } + + if( isset( $condition[ 'else' ] ) ) { + $this->else = $condition[ 'else' ]; + } + + $this->result = $default; + + $this->fields = $fieldsCollection; + $this->data = $data; + } + + public function process() + { + array_walk( $this->when, array( $this, 'compare' ) ); + $result = array_reduce( $this->when, array( $this, 'evaluate' ), $this->result ); + + $triggers = ( $result ) ? $this->then : $this->else; + array_map( array( $this, 'trigger' ), $triggers ); + + return $result; + } + + private function compare( &$when ) + { + if( ! $when[ 'key' ] ) return; + + /** + * This was originally written only with fields in mind. + * To handle calcs, we are using the Calcs Merge Tag global (since the values aren't passed in). + */ + switch( $when[ 'type' ] ){ + case 'field': + $fieldModel = $this->fields->get_field( $when[ 'key' ] ); + $value = $fieldModel->get_setting( 'value' ); + // If we have a checkbox.... + if( 'checkbox' == $fieldModel->get_setting( 'type' ) ) { + // Check the value and change it to check or unchecked. + if( 0 == $value ) { + $value = 'unchecked'; + } else { + $value = 'checked'; + } + } else if ( 'date' == $fieldModel->get_setting( 'type' ) ) { + /* + * Turn our date into a timestamp. + */ + if ( is_array( $value ) ) { // If $value is an array, we have either date_and_time or time_only. + if ( empty( $value[ 'date' ] ) ) { + $date = '1970/01/01'; + } else { + $date = $value[ 'date' ]; + } + + if ( empty( $value[ 'hour' ] ) ) { + $hour = '00'; + } else { + $hour = $value[ 'hour' ]; + } + + if ( empty ( $value[ 'minute' ] ) ) { + $minute = '00'; + } else { + $minute = $value[ 'minute' ]; + } + } else { // If $value is a string, we have date_only. + $date = $value; + $hour = '00'; + $minute = '00'; + } + + $date_string = $date. ' ' . $hour . ':' . $minute; + $value = strtotime( $date_string ); + } + break; + case 'calc': + try { + $value = Ninja_Forms()->merge_tags[ 'calcs' ]->get_calc_value( $when[ 'key' ] ); + }catch( Exception $e ){ + $value = false; + } + break; + default: + $value = false; + } + + $when[ 'result' ] = NF_ConditionalLogic()->comparator( $when[ 'comparator' ] )->compare( $value, $when[ 'value' ] ); + } + + private function evaluate( $current, $when ) + { + if( ! isset( $when[ 'result' ] ) ) return true; + return ( 'AND' == $when[ 'connector' ] ) ? $current && $when[ 'result' ] : $current || $when[ 'result' ]; + } + + private function trigger( $trigger ) + { + $triggerModel = NF_ConditionalLogic()->trigger( $trigger[ 'trigger' ] ); + + if( ! $triggerModel ) return; + + switch( $trigger[ 'type' ] ) { + case 'field': + $target = $this->fields->get_field( $trigger['key'] ); + break; + default: + $target = apply_filters( 'ninja_forms_conditional_logic_trigger_type_' . $trigger[ 'type' ], $trigger[ 'key' ], $this->data ); + } + + if( ! $target ) return; + + $triggerModel->process( $target, $this->fields, $this->data ); + } + +} diff --git a/includes/Config/ActionSettings.php b/includes/Config/ActionSettings.php new file mode 100644 index 0000000..c0829e1 --- /dev/null +++ b/includes/Config/ActionSettings.php @@ -0,0 +1,17 @@ + array( + 'name' => 'conditions', + 'type' => 'action_conditions', + 'group' => 'conditional_logic', + 'label' => __( 'Conditions', 'ninja-forms-conditional-logic' ), + 'placeholder' => '', + 'width' => 'full', + 'value' => '', + ), + +)); diff --git a/includes/Config/ActionSettingsGroups.php b/includes/Config/ActionSettingsGroups.php new file mode 100644 index 0000000..213892e --- /dev/null +++ b/includes/Config/ActionSettingsGroups.php @@ -0,0 +1,9 @@ + array( + 'id' => 'conditional_logic', + 'label' => __( 'Conditional Logic', 'ninja-forms-conditional-logic' ), + 'priority' => 800 + ), +); diff --git a/includes/Config/AdvancedSettings.php b/includes/Config/AdvancedSettings.php new file mode 100644 index 0000000..2823a64 --- /dev/null +++ b/includes/Config/AdvancedSettings.php @@ -0,0 +1,15 @@ + array( + 'conditions' => array( + 'name' => 'conditions', + 'type' => 'advanced_conditions', + 'label' => __( 'Conditional Logic', 'ninja-forms-conditional-logic' ), + 'width' => 'full', + 'group' => 'primary', + ), + ) + +); diff --git a/includes/Config/AdvancedSettingsTypes.php b/includes/Config/AdvancedSettingsTypes.php new file mode 100644 index 0000000..aec1957 --- /dev/null +++ b/includes/Config/AdvancedSettingsTypes.php @@ -0,0 +1,10 @@ + array( + 'id' => 'conditional_logic', + 'nicename' => __( 'Conditional Logic', 'ninja-forms-conditional-logic' ), + ) + +); diff --git a/includes/Config/Comparators.php b/includes/Config/Comparators.php new file mode 100644 index 0000000..9133005 --- /dev/null +++ b/includes/Config/Comparators.php @@ -0,0 +1,71 @@ + array( + 'key' => 'equal', + 'label' => __( 'Equals', 'ninja-forms-conditional-logic' ), + 'instance' => new NF_ConditionalLogic_Comparators_Equal() + ), + + 'notequal' => array( + 'key' => 'notequal', + 'label' => __( 'Does Not Equal', 'ninja-forms-conditional-logic' ), + 'instance' => new NF_ConditionalLogic_Comparators_NotEqual() + ), + + /* + |-------------------------------------------------------------------------- + | Contains / Does Not Contain + |-------------------------------------------------------------------------- + */ + + 'contains' => array( + 'key' => 'contains', + 'label' => __( 'Contains', 'ninja-forms-conditional-logic' ), + 'instance' => new NF_ConditionalLogic_Comparators_Contains() + ), + + 'notcontains' => array( + 'key' => 'notcontains', + 'label' => __( 'Does Not Contain', 'ninja-forms-conditional-logic' ), + 'instance' => new NF_ConditionalLogic_Comparators_NotContains() + ), + + /* + |-------------------------------------------------------------------------- + | Greater Than / Less Than + |-------------------------------------------------------------------------- + */ + + 'greater' => array( + 'key' => 'greater', + 'label' => __( 'Greater Than', 'ninja-forms-conditional-logic' ), + 'instance' => new NF_ConditionalLogic_Comparators_Greater() + ), + + 'greaterequal' => array( + 'key' => 'greaterequal', + 'label' => __( 'Greater Than Or Equal', 'ninja-forms-conditional-logic' ), + 'instance' => new NF_ConditionalLogic_Comparators_GreaterEqual() + ), + + 'less' => array( + 'key' => 'less', + 'label' => __( 'Less Than', 'ninja-forms-conditional-logic' ), + 'instance' => new NF_ConditionalLogic_Comparators_Less() + ), + + 'lessequal' => array( + 'key' => 'lessequal', + 'label' => __( 'Less Than Or Equal', 'ninja-forms-conditional-logic' ), + 'instance' => new NF_ConditionalLogic_Comparators_LessEqual() + ), + +)); diff --git a/includes/Config/Triggers.php b/includes/Config/Triggers.php new file mode 100644 index 0000000..4256e96 --- /dev/null +++ b/includes/Config/Triggers.php @@ -0,0 +1,59 @@ + array( + 'key' => 'hide_field', + 'label' => __( 'Hide Field', 'ninja-forms-conditional-logic' ), + 'instance' => new NF_ConditionalLogic_Triggers_HideField() + ), + + /* + |-------------------------------------------------------------------------- + | Show Field + |-------------------------------------------------------------------------- + */ + + 'show_field' => array( + 'key' => 'show_field', + 'label' => __( 'Show Field', 'ninja-forms-conditional-logic' ), + 'instance' => new NF_ConditionalLogic_Triggers_ShowField() + ), + + /* + |-------------------------------------------------------------------------- + | Change Value + |-------------------------------------------------------------------------- + */ + + 'change_value' => array( + 'key' => 'change_value', + 'label' => __( 'Change Value', 'ninja-forms-conditional-logic' ), + 'instance' => '' + ), + + /* + |-------------------------------------------------------------------------- + | Required Field + |-------------------------------------------------------------------------- + */ + + 'set_required' => array( + 'key' => 'set_required', + 'label' => __( 'Is Required', 'ninja-forms-conditional-logic' ), + 'instance' => '' + ), + + 'unset_required' => array( + 'key' => 'unset_required', + 'label' => __( 'Not Required', 'ninja-forms-conditional-logic' ), + 'instance' => '' + ), + +)); diff --git a/includes/Config/i18nCLBuilder.php b/includes/Config/i18nCLBuilder.php new file mode 100644 index 0000000..bb0940f --- /dev/null +++ b/includes/Config/i18nCLBuilder.php @@ -0,0 +1,55 @@ + __( 'Condition', 'ninja-forms-conditional-logic' ), + 'clickControlsConditionWhen' => __( 'Condition - When', 'ninja-forms-conditional-logic' ), + 'clickControlsConditionThen' => __( 'Condition - Then', 'ninja-forms-conditional-logic' ), + 'clickControlsConditionElse' => __( 'Condition - Else', 'ninja-forms-conditional-logic' ), + 'clickControlsConditionWhenCriteron' => __( 'Condition - Criteron', 'ninja-forms-conditional-logic' ), + 'clickControlsConditionDoItem' => __( 'Condition - Do Item', 'ninja-forms-conditional-logic' ), + 'clickControlsConditionElseItem' => __( 'Condition - Else Item', 'ninja-forms-conditional-logic' ), + 'coreComparatorsIs' => __( 'Is', 'ninja-forms-conditional-logic' ), + 'coreComparatorsIsNot' => __( 'Is Not', 'ninja-forms-conditional-logic' ), + 'coreComparatorsHasSelected' => __( 'Has Selected', 'ninja-forms-conditional-logic' ), + 'coreComparatorsDoesNotHaveSelected' => __( 'Does Not Have Selected', 'ninja-forms-conditional-logic' ), + 'coreComparatorsBefore' => __( 'Before', 'ninja-forms-conditional-logic' ), + 'coreComparatorsOnOrBefore' => __( 'On or Before', 'ninja-forms-conditional-logic' ), + 'coreComparatorsOnOrAfter' => __( 'On or After', 'ninja-forms-conditional-logic' ), + 'coreComparatorsAfter' => __( 'After', 'ninja-forms-conditional-logic' ), + 'coreTriggersSelectOption' => __( 'Select Option', 'ninja-forms-conditional-logic' ), + 'coreTriggersDeselectOption' => __( 'De-Select Option', 'ninja-forms-conditional-logic' ), + 'coreTriggersShowOption' => __( 'Show Option', 'ninja-forms-conditional-logic' ), + 'coreTriggersHideOption' => __( 'Hide Option', 'ninja-forms-conditional-logic' ), + 'newConditionCondition' => __( 'Condition', 'ninja-forms-conditional-logic' ), + 'templateHelperEquals' => __( 'Equals', 'ninja-forms-conditional-logic' ), + 'templateHelperDoesNotEqual' => __( 'Does Not Equal', 'ninja-forms-conditional-logic' ), + 'templateHelperContains' => __( 'Contains', 'ninja-forms-conditional-logic' ), + 'templateHelperDoesNotContain' => __( 'Does Not Contain', 'ninja-forms-conditional-logic' ), + 'templateHelperGreaterThan' => __( 'Greater Than', 'ninja-forms-conditional-logic' ), + 'templateHelperLessThan' => __( 'Less Than', 'ninja-forms-conditional-logic' ), + 'templateHelperLessThanOrEqual' => __( 'Less Than or Equal', 'ninja-forms-conditional-logic' ), + 'templateHelperGreaterThanOrEqual' => __( 'Greater Than or Equal', 'ninja-forms-conditional-logic' ), + 'templateHelperShowField' => __( 'Show Field', 'ninja-forms-conditional-logic' ), + 'templateHelperHideField' => __( 'Hide Field', 'ninja-forms-conditional-logic' ), + 'templateHelperSetRequired' => __( 'Is Required', 'ninja-forms-conditional-logic' ), + 'templateHelperUnsetRequired' => __( 'Not Required', 'ninja-forms-conditional-logic' ), + 'templateHelperChangeValue' => __( 'Change Value', 'ninja-forms-conditional-logic'), + 'updateSettingsCondition' => __( 'Condition', 'ninja-forms-conditional-logic' ), + 'updateSettingsTitleAddNewCondition' => __( 'Add New Condition', 'ninja-forms-conditional-logic' ), + 'updateSettingsAddNewCondition' => __( 'Add New Condition', 'ninja-forms-conditional-logic' ), + 'builderEditSettingsTitleDone' => __( 'Done', 'ninja-forms-conditional-logic' ), + 'builderEditSettingsDone' => __( 'Done', 'ninja-forms-conditional-logic' ), + 'builderEditSettingsWhen' => __( 'When', 'ninja-forms-conditional-logic' ), + 'builderEditSettingsDo' => __( 'Do', 'ninja-forms-conditional-logic' ), + 'builderEditSettingsConditionNotMet' => __( 'If the Condition Is Not Met', 'ninja-forms-conditional-logic' ), + 'builderEditSettingsAnd' => __( 'AND', 'ninja-forms-conditional-forms' ), + 'builderEditSettingsOr' => __( 'OR', 'ninja-forms-conditional-forms' ), + 'builderEditSettingsProcessThis' => __( 'Process This', 'ninja-forms-conditional-forms' ), + 'builderEditSettingsDoNotProcessThis' => __( 'Don't Process This', 'ninja-forms-conditional-forms' ), + 'builderEditSettingsAll' => __( 'All', 'ninja-forms-conditional-forms' ), + 'builderEditSettingsAny' => __( 'Any', 'ninja-forms-conditional-forms' ), + 'builderEditSettingsChecked' => __( 'Checked', 'ninja-forms-conditional-forms' ), + 'builderEditSettingsUnchecked' => __( 'Unchecked', 'ninja-forms-conditional-forms' ), + 'builderEditSettingsSelectOne' => __( 'Select One', 'ninja-forms-conditional-forms' ), +)); \ No newline at end of file diff --git a/includes/FieldsCollection.php b/includes/FieldsCollection.php new file mode 100644 index 0000000..a55311f --- /dev/null +++ b/includes/FieldsCollection.php @@ -0,0 +1,88 @@ +form( $form_id )->get_fields(); + } + + $user_supplied_values = apply_filters( 'ninja_forms_user_submitted_data_types', array( + 'value', + 'files' + ) ); + + // loop over our DB fields and update the "value" setting for our submitted values. + foreach( $field_list as $field ){ + // If we don't already have a field object, fetch one. + if ( is_object( $field ) ) { + $fieldModel = $field; + } else { + $fieldModel = Ninja_Forms()->form( $form_id )->get_field( $field[ 'id' ] ); + } + $field_settings = $fieldModel->get_settings(); // Initialized field settings from the database, if needed. + $field_id = $fieldModel->get_id(); + + foreach( $user_supplied_values as $key ) { + + // Check for a submitted value. If that doesn't exist, fallback to default value. Otherwise, set to NULL. + if( isset( $submitted_fields[ $field_id ][ $key ] ) ){ + $field_value = $submitted_fields[ $field_id ][ $key ]; + } elseif( isset( $field_settings[ $key ] ) ){ + $field_value = $field_settings[ $key ]; + } else { + $field_value = null; + } + + $fieldModel->update_setting( $key, $field_value ); + } + + // If we are in preview mode, trust the data sent by the user. + // This allows us to preview changes before they are saved to the DB. + if ( $is_preview ) { + if( $fieldModel->get_tmp_id() && isset( $field[ 'key' ] ) ){ + $fieldModel->update_setting( 'key', $field[ 'key' ] ); + } + + if( isset( $field[ 'settings' ] ) ) { + $settings = array_merge( $field[ 'settings' ], $fieldModel->get_settings()); + } else { + $settings = array_merge( $field, $fieldModel->get_settings()); + } + + $fieldModel->update_settings( $settings ); + } + + $this->fields[] = $fieldModel; + } + } + + public function get_field( $key_or_id ) + { + $property = ( is_numeric( $key_or_id ) ) ? 'id' : 'key'; + foreach( $this->fields as $field ){ + $setting = $field->get_setting( $property ); + if( $key_or_id == $setting ) return $field; + } + return Ninja_Forms()->form()->field()->get(); + } + + public function to_array() + { + $fields = array(); + foreach( $this->fields as $field ){ + $settings = $field->get_settings(); + $settings[ 'id' ] = ( $field->get_tmp_id() ) ? $field->get_tmp_id() : $field->get_id(); + $fields[ $field->get_id() ] = $settings; + } + return $fields; + } +} diff --git a/includes/Integrations/MultiPart.php b/includes/Integrations/MultiPart.php new file mode 100644 index 0000000..112de23 --- /dev/null +++ b/includes/Integrations/MultiPart.php @@ -0,0 +1,80 @@ + 'hide_part', + 'label' => __( 'Hide Part', 'ninja-forms-conditional-logic' ), + 'instance' => new NF_ConditionalLogic_Triggers_HidePart() + ); + + $triggers[ 'show_part' ] = array( + 'key' => 'show_part', + 'label' => __( 'Show Part', 'ninja-forms-conditional-logic' ), + 'instance' => new NF_ConditionalLogic_Triggers_ShowPart() + ); + + return $triggers; + } + + public function get_part( $key, $data ) + { + $form = Ninja_Forms()->form( $data['id'] )->get(); + + if( isset( $data[ 'settings' ][ 'is_preview' ] ) && $data[ 'settings' ][ 'is_preview' ] ){ + $form_settings = get_user_option( 'nf_form_preview_' . $data['form_id'] ); + $form->update_settings( $form_settings ); + } + + $formContentData = $form->get_setting( 'formContentData' ); + + if( ! $formContentData ) return false; + + $parts = array(); + foreach( $formContentData as $content ){ + if( 'part' != $content[ 'type' ] || $key != $content[ 'key' ] ) continue; + array_push( $parts, $content ); + } + + if( ! is_array( $parts ) ) return false; + + return array_shift( $parts ); + } + + public static function extract_field_keys( $part ) + { + if( ! isset( $part[ 'formContentData' ] ) ) return array(); + $field_keys = array(); + foreach( $part[ 'formContentData' ] as $content ){ + if( is_string( $content ) ){ + array_push( $field_keys, $content ); + } else { + $field_keys = array_merge($field_keys, self::extract_field_keys_from_layout( $content ) ); + } + } + return $field_keys; + } + + public static function extract_field_keys_from_layout( $content ) + { + if( ! isset( $content[ 'cells' ] ) ) return array(); + + $field_keys = array(); + foreach( $content[ 'cells' ] as $cell ){ + if( ! isset( $cell[ 'fields' ] ) ) continue; + $field_keys = array_merge( $field_keys, $cell[ 'fields' ] ); + } + + return $field_keys; + } + + +} diff --git a/includes/Submission.php b/includes/Submission.php new file mode 100644 index 0000000..0385684 --- /dev/null +++ b/includes/Submission.php @@ -0,0 +1,177 @@ +form( $form_id )->get_settings(); + } + + // Make sure we build the fieldsCollection, in case there are conditionally triggered Actions. + $this->fieldsCollection = new NF_ConditionalLogic_FieldsCollection( $data[ 'fields' ], $data[ 'id' ], $is_preview ); + + // If we don't have any conditions set on this form, return $data. + if( ! isset( $form_settings[ 'conditions' ] ) || empty( $form_settings[ 'conditions' ] ) ) { + return $data; + } + + foreach( $data[ 'settings' ][ 'conditions' ] as $condition ){ + $condition = new NF_ConditionalLogic_ConditionModel( $condition, $this->fieldsCollection, $data ); + $condition->process(); + } + + $this->fieldsCollection = apply_filters( 'ninja_forms_conditional_logic_parse_fields', $this->fieldsCollection ); + $data[ 'fields' ] = $this->fieldsCollection->to_array(); + + return $data; + } + + public function before_validate_field( $field_settings ) + { + if( ! isset( $field_settings[ 'conditionally_required' ] ) ) return $field_settings; + + $field_settings[ 'required' ] = $field_settings[ 'conditionally_required' ]; + + unset( $field_settings[ 'conditionally_required' ] ); + + return $field_settings; + } + + public function parse_actions( $actions, $form_data ) + { + // If we don't have a fieldsCollection (such as if this is a resume) skip this filter. + if ( ! empty( $this->fieldsCollection ) ) { + array_walk( $actions, array( $this, 'parse_action' ), $this->fieldsCollection ); + } + + return $actions; + } + + public function parse_action( &$action, $key, $fieldsCollection ) + { + if( ! isset( $action[ 'settings' ][ 'active' ] ) || ! $action[ 'settings' ][ 'active' ] ) return; + + // Checks to see if conditional setting is in the default position and returns early if it is. + if( 0 !== intval( $action[ 'settings' ][ 'conditions' ][ 'process' ] ) + && 1 !== intval( $action[ 'settings' ][ 'conditions' ][ 'process' ] ) ) return; + if( empty( $action[ 'settings' ][ 'conditions' ][ 'when' ] ) ) return; + + $action_condition = ( is_object( $action ) ) ? $action->get_setting( 'conditions' ) : $action[ 'settings' ][ 'conditions' ]; + + if( ! $action_condition ) return; + + unset( $action_condition[ 'then' ] ); + unset( $action_condition[ 'else' ] ); + + $valid_conditions = array(); + + $input_field_types = array( + 'address', + 'address2', + 'confirm', + 'date', + 'email', + 'firstname', + 'lastname', + 'number', + 'password', + 'passwordconfirm', + 'phone', + 'textbox', + 'textarea', + 'hidden', + ); + + $field_collection_array = $this->fieldsCollection->to_array(); + + foreach( $action_condition[ 'when' ] as &$when ){ + $when[ 'connector' ] = ( 'all' == $action_condition[ 'connector' ] ) ? 'AND' : 'OR'; + $field_type = $this->get_field_type( $when[ 'key' ], $field_collection_array ); + + // Input field conditions are valid, even with empty values + $is_input_field = in_array( $field_type, $input_field_types ); + $is_valid = $this->validate_action_when_value( $when ); + + if ( $is_input_field || $is_valid ) { + $valid_conditions[] = $when; + } + } + + // Merge our valid conditions back into our action conditions array. + $action_condition[ 'when' ] = $valid_conditions; + + $default = ( 'all' == $action_condition[ 'connector' ] ); + + $condition = new NF_ConditionalLogic_ConditionModel( $action_condition, $fieldsCollection, array(), $default ); + $result = $condition->process(); + + if( 1 != $action_condition[ 'process' ] ) { + $result = ! $result; + } + + if( is_object( $action ) ){ + $action->update_setting( 'active', $result ); + } else { + $action[ 'settings' ][ 'active' ] = $result; + } + } + + /** + * Validates if our when condition has a valid value. + * + * @return bool + */ + public function validate_action_when_value( $when_condition ) + { + if( empty( $when_condition[ 'value' ] ) ) { + return false; + } else { + return true; + } + } + + /** + * Compares our field key to our field model to get the field type. + * + * @return string - field type. + */ + public function get_field_type( $field_key, $field_models ) + { + foreach( $field_models as $field ) { + if( $field_key == $field[ 'key' ] ) { + return $field[ 'type' ]; + } + } + } +} \ No newline at end of file diff --git a/includes/Templates/builder-edit-settings-old.html.php b/includes/Templates/builder-edit-settings-old.html.php new file mode 100644 index 0000000..2f7bf94 --- /dev/null +++ b/includes/Templates/builder-edit-settings-old.html.php @@ -0,0 +1,316 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/includes/Templates/builder-edit-settings.html.php b/includes/Templates/builder-edit-settings.html.php new file mode 100644 index 0000000..86b8a5d --- /dev/null +++ b/includes/Templates/builder-edit-settings.html.php @@ -0,0 +1,316 @@ +' ) ) { + /* + * If we're using the RC codebase of Ninja Forms, load the older, <% %> style templates. + */ + require_once( NF_ConditionalLogic::$dir . 'includes/Templates/builder-edit-settings-old.html.php' ); + return false; +} +?> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/includes/Trigger.php b/includes/Trigger.php new file mode 100644 index 0000000..4971732 --- /dev/null +++ b/includes/Trigger.php @@ -0,0 +1,6 @@ +get_setting( 'value' ); + $field->update_setting( 'value', false ); + $field->update_setting( 'submitted_value', $value ); + $field->update_setting( 'visible', false ); + + // Hidden fields should NOT be validated for required. + if( 1 == $field->get_setting( 'required' ) ) { + + // Set bypass flag. + $field->update_setting( 'conditionally_required', false ); + } + } +} \ No newline at end of file diff --git a/includes/Triggers/HidePart.php b/includes/Triggers/HidePart.php new file mode 100644 index 0000000..f717dc4 --- /dev/null +++ b/includes/Triggers/HidePart.php @@ -0,0 +1,16 @@ +trigger( 'hide_field' ); + $field_keys = NF_ConditionalLogic_Integrations_MultiPart::extract_field_keys( $target ); + + foreach( $field_keys as $field_key ) + { + $field = $fieldCollection->get_field( $field_key ); + $field_trigger->process( $field, $fieldCollection, $data ); + } + } +} \ No newline at end of file diff --git a/includes/Triggers/ShowField.php b/includes/Triggers/ShowField.php new file mode 100644 index 0000000..1ebb44d --- /dev/null +++ b/includes/Triggers/ShowField.php @@ -0,0 +1,22 @@ +get_setting( 'value' ) ) return; + $submitted_value = $field->get_setting( 'submitted_value' ); + + if( ! is_null( $submitted_value ) ) { + $field->update_setting( 'value', $submitted_value ); + $field->update_setting( 'visible', true ); + } + + // Visible fields should be validated for required. + if( 1 == $field->get_setting( 'required' ) ) { + + // Clear bypass flag. + $field->update_setting('conditionally_required', true); + } + } +} \ No newline at end of file diff --git a/includes/Triggers/ShowPart.php b/includes/Triggers/ShowPart.php new file mode 100644 index 0000000..11c99ca --- /dev/null +++ b/includes/Triggers/ShowPart.php @@ -0,0 +1,16 @@ +trigger( 'show_field' ); + $field_keys = NF_ConditionalLogic_Integrations_MultiPart::extract_field_keys( $target ); + + foreach( $field_keys as $field_key ) + { + $field = $fieldCollection->get_field( $field_key ); + $field_trigger->process( $field, $fieldCollection, $data ); + } + } +} \ No newline at end of file diff --git a/lib/conversion.php b/lib/conversion.php new file mode 100644 index 0000000..14bdb0e --- /dev/null +++ b/lib/conversion.php @@ -0,0 +1,437 @@ +actions = $form_data[ 'actions' ]; + $this->fields = $form_data[ 'fields' ]; + + $all_actions = $this->extract_action_conditions( $this->actions, array() ); + $all_fields = $this->extract_field_conditions( $this->fields, array() ); + + $form_data[ 'actions' ] = $all_actions; + $form_data[ 'fields' ] = $all_fields; + $form_data[ 'settings' ][ 'conditions' ] = $this->field_conditions; + + // echo "
";
+	    // print_r( $form_data[ 'actions' ] );
+	    // echo "
"; + // die(); + + return $form_data; + } + + function extract_action_conditions( $actions, $all_actions ) { + /* + * Pop the first action off of our array and check to see if it has any conditions. + */ + $this->current_action = array_shift( $actions ); + + if ( isset ( $this->current_action[ 'conditions' ] ) && ! empty( $this->current_action[ 'conditions' ] ) && isset( $this->current_action[ 'conditions' ][ 0 ] ) ) { + + /* + * If we have a condition, convert it for 3.0. + */ + $old_condition = $this->current_action[ 'conditions' ][ 0 ]; + + $new_condition = array(); + $new_condition[ 'process' ] = ( 'process' == $old_condition[ 'action' ] ) ? 1 : 0; + $new_condition[ 'connector' ] = ( 'and' == $old_condition[ 'connector' ] ) ? 'all' : 'any'; + $connector = ( 'all' == $new_condition[ 'connector' ] ) ? 'AND' : 'OR'; + $new_condition[ 'when' ] = $this->extract_when( $old_condition[ 'criteria' ], array(), $connector ); + + $this->current_action[ 'conditions' ] = $new_condition; + } + + /* + * Add our action to our all actions var. + */ + $all_actions[] = $this->current_action; + $this->current_action = array(); + + /* + * If there aren't any more actions, we are at the end of our array. + * return our conditions and all actions vars. + */ + if ( 0 == count( $actions ) ) { + return $all_actions; + } + + /* + * Recurse. + */ + return $this->extract_action_conditions( $actions, $all_actions ); + } + + /** + * Rather than loop through our array, we'll use a recursive function to update everything. + * @since 3.0 + * @param array $fields fields array that gets modified as we recurse. + * @param array $conditions array of conditions. + * @param array $all_fields array of fields. + * @return array + */ + function extract_field_conditions( $fields, $all_fields ) + { + /* + * Pop the first field off of our array and check to see if it has any conditions. + */ + $this->current_field = array_shift( $fields ); + + if ( isset ( $this->current_field[ 'conditional' ] ) && ! empty( $this->current_field[ 'conditional' ] ) ) { + /* + * If we have conditions, add them to the new condition array we are building. + */ + array_walk( $this->current_field[ 'conditional' ], array( $this, 'update_field_conditions' ) ); + } + + /* + * Remove the conditional settings from this field. + */ + unset( $this->current_field[ 'conditional' ] ); + + /* + * Add our field to our all fields var. + */ + $all_fields[] = $this->current_field; + $this->current_field = array(); + + /* + * If there aren't any more fields, we are at the end of our array. + * return our conditions and all fields vars. + */ + if ( 0 == count( $fields ) ) { + return $all_fields; + } + + /* + * Recurse. + */ + return $this->extract_field_conditions( $fields, $all_fields ); + } + + /** + * Step through each field condition and update our $this->field_conditions with each. + * + * @since 3.0 + * @param array $field_condition 2.9.x Field Condition + * @param int $index array index + * @return void + */ + function update_field_conditions( $field_condition ) + { + /* + * Convert criteria to the 'when' statement of our condition. + */ + $when = $this->extract_when( $field_condition[ 'cr' ], array(), $field_condition[ 'connector' ] ); + /* + * Get our then and possibly else statements. + */ + $tmp = $this->extract_then_else( $field_condition ); + $then = $tmp[ 'then' ]; + $else = $tmp[ 'else' ]; + + /* + * Check to see if this when statement exists already. + */ + $condition_index = $this->find_when( $when, $this->field_conditions ); + + /* + * If it does, add this field's then/else. + * + * If it doesn't, add a new condition using this field's when/then/else. + */ + if ( false !== $condition_index ) { + $this->field_conditions[ $condition_index ][ 'then' ][] = $then; + if ( ! empty ( $else ) ) { + $this->field_conditions[ $condition_index ][ 'else' ][] = $else; + } + } else { + $this->field_conditions[] = array( + 'when' => $when, + 'then' => array( $then ), + 'else' => array( $else ), + ); + } + + } + + /** + * Returns a 3.0 formatted array from 2.9.x criteria. + * + * @since 3.0 + * @param array $cr_array 2.9.x Criteria Array + * @param array $when Used to create a mult-dimensional when array + * @param string $connector 2.9.x connector var + * @return array + */ + function extract_when( $cr_array, $when, $connector = '' ) + { + if ( ! is_array( $cr_array ) ) return false; + $cr = array_shift( $cr_array ); + /* + * Replace our field target with the appropriate key + * + * This criterion could contain either a 'field' key or a 'param' key. + */ + + $field_id = ( isset ( $cr[ 'field' ] ) ) ? $cr[ 'field' ] : $cr[ 'param' ]; + $field_key = $this->get_key( $field_id ); + $comparator = ( isset ( $cr[ 'operator' ] ) ) ? $cr[ 'operator' ] : $cr[ 'compare' ]; + + $when[] = array( + 'connector' => strtoupper( $connector ), + 'key' => $field_key, + 'comparator' => $this->convert_comparator( $comparator, $field_id ), + 'value' => $this->convert_value( $cr[ 'value' ] ), + 'type' => $this->get_when_type( $field_id ) + ); + + if ( 0 == count( $cr_array ) ) { + /* + * Return our when array + */ + return $when; + } + + /* + * Recurse + */ + return $this->extract_when( $cr_array, $when, $connector ); + } + + /** + * Return 3.0 formatted then/else arrays. + * + * @since 3.0 + * @param array $condition 2.9.x formatted condition array + * @return array 3.0 formatted then/else + */ + function extract_then_else( $condition ) + { + /* + * We have new names for some of our actions. + */ + switch( $condition[ 'action' ] ) { + case 'show': + $trigger = 'show_field'; + $else_trigger = 'hide_field'; + break; + case 'hide': + $trigger = 'hide_field'; + $else_trigger = 'show_field'; + break; + case 'add_value': + $trigger = $this->convert_trigger( $condition ); + $else_trigger = 'hide_option'; + break; + case 'remove_value': + $trigger = $this->convert_trigger( $Condition ); + $else_trigger = 'show_option'; + default: + $trigger = $condition[ 'action' ]; + $else_trigger = false; + break; + } + + $value = $this->convert_value( $condition[ 'value' ] ); + + $then = array( 'key' => $this->current_field[ 'key' ], 'trigger' => $trigger, 'value' => $value, 'type' => 'field' ); + + if ( $else_trigger ) { + $else = array( 'key' => $this->current_field[ 'key' ], 'trigger' => $else_trigger, 'value' => $value, 'type' => 'field' ); + } else { + $else = array(); + } + + return array( 'then' => $then, 'else' => $else ); + + } + + /** + * Search $conditions array for $when + * + * @since 3.0 + * @param array $when Needle + * @param array $conditions Haystack + * @return int/bool Index or Boolean + */ + function find_when( $when, $conditions ) + { + foreach( $conditions as $index => $condition ) { + if ( $condition[ 'when' ] == $when ) { + return $index; + } + } + + return false; + } + + /** + * Get our 3.0 field key from our 2.9.x field ID + * + * @since 3.0 + * @param int $id 2.9.x field ID + * @return string + */ + function get_key( $id ) + { + return ( isset( $this->known_keys[ $id ] ) ) ? $this->known_keys[ $id ] : $this->find_key( $id ); + } + + /** + * Search for a field key by ID + * + * @since 3.0 + * @param int $id 2.9.x field ID + * @return string + */ + function find_key( $id ) + { + $this->current_id = $id; + $field = array_filter( $this->fields, array( $this, 'filter_by_id' ) ); + $field = array_shift( $field ); + $this->current_id = 0; + $this->known_keys[ $id ] = $field[ 'key' ]; + return isset( $field[ 'key' ] ) ? $field[ 'key' ] : false; + } + + /** + * Filter function used by the array_filter call inside find_key + * + * @since 3.0 + * @param array $val field array + * @return bool + */ + function filter_by_id( $val ) + { + return $val[ 'id' ] == $this->current_id; + } + + /** + * Convert 2.9.x comparators to 3.0 format. + * + * @since 3.0 + * @param string $comparator 2.9.x format comparator + * @return string + */ + function convert_comparator( $comparator, $field_id ) + { + $current_id = $this->current_id; + $this->current_id = $field_id; + $field = array_filter( $this->fields, array( $this, 'filter_by_id' ) ); + $field = array_shift( $field ); + $this->current_id = $current_id; + + switch ( $comparator ) { + case '==': + /* + * If we have a list field, we want to use "contains" and "notcontains" instead of "equal" and "notequal" + */ + if ( 'listselect' == $field[ 'type' ] || 'listradio' == $field[ 'type' ] || 'listcheckbox' == $field[ 'type' ] || 'listmultiselect' == $field[ 'type' ] ) { + return 'contains'; + } else { + return 'equal'; + } + case '!=': + /* + * If we have a list field, we want to use "contains" and "notcontains" instead of "equal" and "notequal" + */ + if ( 'listselect' == $field[ 'type' ] || 'listradio' == $field[ 'type' ] || 'listcheckbox' == $field[ 'type' ] || 'listmultiselect' == $field[ 'type' ] ) { + return 'notcontains'; + } else { + return 'notequal'; + } + case '<': + return 'less'; + case '>': + return 'greater'; + default: + return $comparator; + } + } + + /** + * Some of our values, like checkboxes, should be 1 or 0 instead of checked or unchecked. + * + * @since 3.0 + * @param mixed $value + * @return mixed + */ + function convert_value( $value ) + { + switch( $value ) { + case 'checked': + $value = 1; + break; + case 'unchecked': + $value = 0; + break; + } + + return $value; + } + + /** + * Some actions, like "change_value" for list fields need to be converted to new triggers. + * @since 3.0 + * @param string $action 2.9.x action + * @return string 3.0 trigger + */ + function convert_trigger( &$condition ) { + if ( + 'listselect' != $this->current_field[ 'type' ] && + 'listradio' != $this->current_field[ 'type' ] && + 'listcheckbox' != $this->current_field[ 'type' ] + ) { + return $condition[ 'action' ]; + } + + switch( $condition[ 'action' ] ) { + case 'change_value': + return 'select_option'; + case 'add_value': + $this->current_field[ 'options' ][] = array( + 'label' => $condition[ 'value' ][ 'label' ], + 'value' => $condition[ 'value' ][ 'label' ], + 'calc' => $condition[ 'value' ][ 'calc' ], + 'selected' => $condition[ 'value' ][ 'selected' ] + ); + $condition[ 'value' ] = $condition[ 'value' ][ 'label' ]; + return 'show_option'; + case 'remove_value': + return 'hide_option'; + } + } + + function get_when_type( $field_id ) + { + foreach( $this->fields as $field ){ + if( $field_id != $field[ 'id' ] ) continue; + return ( 'calc' != $field[ 'type' ] ) ? 'field' : 'calc'; + } + + return 'field'; + } +} + +new NF_ConditionalLogic_Conversion(); \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..5b28c3a --- /dev/null +++ b/package-lock.json @@ -0,0 +1,3843 @@ +{ + "name": "ninja-forms", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@gulp-sourcemaps/map-sources": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@gulp-sourcemaps/map-sources/-/map-sources-1.0.0.tgz", + "integrity": "sha1-iQrnxdjId/bThIYCFazp1+yUW9o=", + "requires": { + "normalize-path": "^2.0.1", + "through2": "^2.0.3" + } + }, + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + }, + "acorn": { + "version": "4.0.13", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", + "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=" + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "align-text": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", + "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", + "requires": { + "kind-of": "^3.0.2", + "longest": "^1.0.1", + "repeat-string": "^1.5.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "amdefine": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=" + }, + "ansi-gray": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-gray/-/ansi-gray-0.1.1.tgz", + "integrity": "sha1-KWLPVOyXksSFEKPetSRDaGHvclE=", + "requires": { + "ansi-wrap": "0.1.0" + } + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" + }, + "ansi-wrap": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz", + "integrity": "sha1-qCJQ3bABXponyoLoLqYDu/pF768=" + }, + "aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" + }, + "archy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", + "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=" + }, + "are-we-there-yet": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", + "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=" + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==" + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=" + }, + "array-differ": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-1.0.0.tgz", + "integrity": "sha1-7/UuN1gknTO+QCuLuOVkuytdQDE=" + }, + "array-each": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz", + "integrity": "sha1-p5SvDAWrF1KEbudTofIRoFugxE8=" + }, + "array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=" + }, + "array-slice": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz", + "integrity": "sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w==" + }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=" + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=" + }, + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + }, + "assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=" + }, + "async": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", + "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=" + }, + "async-foreach": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/async-foreach/-/async-foreach-0.1.3.tgz", + "integrity": "sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI=" + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==" + }, + "autoprefixer": { + "version": "6.7.7", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-6.7.7.tgz", + "integrity": "sha1-Hb0cg1ZY41zj+ZhAmdsAWFx4IBQ=", + "requires": { + "browserslist": "^1.7.6", + "caniuse-db": "^1.0.30000634", + "normalize-range": "^0.1.2", + "num2fraction": "^1.2.2", + "postcss": "^5.2.16", + "postcss-value-parser": "^3.2.3" + } + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" + }, + "aws4": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.10.1.tgz", + "integrity": "sha512-zg7Hz2k5lI8kb7U32998pRRFin7zJlkfezGJjUc2heaD4Pw2wObakCDVzkKztTm/Ln7eiVvYsjqak0Ed4LkMDA==" + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "requires": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "beeper": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/beeper/-/beeper-1.1.1.tgz", + "integrity": "sha1-5tXqjF2tABMEpwsiY4RH9pyy+Ak=" + }, + "block-stream": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", + "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=", + "requires": { + "inherits": "~2.0.0" + } + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "browserslist": { + "version": "1.7.7", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz", + "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=", + "requires": { + "caniuse-db": "^1.0.30000639", + "electron-to-chromium": "^1.2.7" + } + }, + "cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "requires": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + } + }, + "camelcase": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=" + }, + "camelcase-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", + "requires": { + "camelcase": "^2.0.0", + "map-obj": "^1.0.0" + } + }, + "caniuse-db": { + "version": "1.0.30001151", + "resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30001151.tgz", + "integrity": "sha512-uIflPNeE7YWzYZ13tqk5Em6YiR6bIqZC8JFkQwZGCadOiZJhPrff1AD9XiPDlE2RS7Ffoo39w9MBd5zjA9J2lw==" + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + }, + "center-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", + "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", + "requires": { + "align-text": "^0.1.3", + "lazy-cache": "^1.0.3" + } + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "dependencies": { + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" + } + } + }, + "class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "requires": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" + } + }, + "clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=" + }, + "clone-stats": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz", + "integrity": "sha1-uI+UqCzzi4eR1YBG6kAprYjKmdE=" + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" + }, + "collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "requires": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + } + }, + "color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==" + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" + }, + "convert-source-map": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", + "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=" + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "cosmiconfig": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-2.2.2.tgz", + "integrity": "sha512-GiNXLwAFPYHy25XmTPpafYvn3CLAkJ8FLsscq78MQd1Kh0OU6Yzhn4eV2MVF4G9WEQZoWEGltatdR+ntGPMl5A==", + "requires": { + "is-directory": "^0.3.1", + "js-yaml": "^3.4.3", + "minimist": "^1.2.0", + "object-assign": "^4.1.0", + "os-homedir": "^1.0.1", + "parse-json": "^2.2.0", + "require-from-string": "^1.1.0" + }, + "dependencies": { + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + } + } + }, + "cross-spawn": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-3.0.1.tgz", + "integrity": "sha1-ElYDfsufDF9549bvE14wdwGEuYI=", + "requires": { + "lru-cache": "^4.0.1", + "which": "^1.2.9" + }, + "dependencies": { + "lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + } + } + }, + "css": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/css/-/css-2.2.4.tgz", + "integrity": "sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw==", + "requires": { + "inherits": "^2.0.3", + "source-map": "^0.6.1", + "source-map-resolve": "^0.5.2", + "urix": "^0.1.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, + "currently-unhandled": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", + "requires": { + "array-find-index": "^1.0.1" + } + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "dateformat": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-2.2.0.tgz", + "integrity": "sha1-QGXiATz5+5Ft39gu+1Bq1MZ2kGI=" + }, + "deap": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/deap/-/deap-1.0.1.tgz", + "integrity": "sha512-k75KYNZMvwAwes2xIPry/QTffXIchjD8QfABvvfTr80P85jv5ZcKqcoDo+vMe71nNnVnXYe8MA28weyqcf/DKw==" + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "debug-fabulous": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/debug-fabulous/-/debug-fabulous-0.0.4.tgz", + "integrity": "sha1-+gccXYdIRoVCSAdCHKSxawsaB2M=", + "requires": { + "debug": "2.X", + "lazy-debug-legacy": "0.0.X", + "object-assign": "4.1.0" + }, + "dependencies": { + "object-assign": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.0.tgz", + "integrity": "sha1-ejs9DpgGPUP0wD8uiubNUahog6A=" + } + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" + }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=" + }, + "defaults": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", + "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", + "requires": { + "clone": "^1.0.2" + } + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "requires": { + "object-keys": "^1.0.12" + } + }, + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "requires": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "dependencies": { + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" + }, + "deprecated": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/deprecated/-/deprecated-0.0.1.tgz", + "integrity": "sha1-+cmvVGSvoeepcUWKi97yqpTVuxk=" + }, + "detect-file": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", + "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=" + }, + "detect-newline": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz", + "integrity": "sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I=" + }, + "duplexer2": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.0.2.tgz", + "integrity": "sha1-xhTc9n4vsUmVqRcR5aYX6KYKMds=", + "requires": { + "readable-stream": "~1.1.9" + } + }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "electron-to-chromium": { + "version": "1.3.584", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.584.tgz", + "integrity": "sha512-NB3DzrTzJFhWkUp+nl2KtUtoFzrfGXTir2S+BU4tXGyXH9vlluPuFpE3pTKeH7+PY460tHLjKzh6K2+TWwW+Ww==" + }, + "end-of-stream": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-0.1.5.tgz", + "integrity": "sha1-jhdyBsPICDfYVjLouTWd/osvbq8=", + "requires": { + "once": "~1.3.0" + } + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "es-abstract": { + "version": "1.18.0-next.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", + "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-negative-zero": "^2.0.0", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.1", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "expand-tilde": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", + "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", + "requires": { + "homedir-polyfill": "^1.0.1" + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" + }, + "fancy-log": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.3.tgz", + "integrity": "sha512-k9oEhlyc0FrVh25qYuSELjr8oxsCoc4/LEZfg2iJJrfEk/tZL9bCoJE47gqAvI2m/AUjluCS4+3I0eTx8n3AEw==", + "requires": { + "ansi-gray": "^0.1.1", + "color-support": "^1.1.3", + "parse-node-version": "^1.0.0", + "time-stamp": "^1.0.0" + } + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "find-index": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/find-index/-/find-index-0.1.1.tgz", + "integrity": "sha1-Z101iyyjiS15Whq0cjL4tuLg3eQ=" + }, + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "requires": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "findup-sync": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-2.0.0.tgz", + "integrity": "sha1-kyaxSIwi0aYIhlCoaQGy2akKLLw=", + "requires": { + "detect-file": "^1.0.0", + "is-glob": "^3.1.0", + "micromatch": "^3.0.4", + "resolve-dir": "^1.0.1" + } + }, + "fined": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fined/-/fined-1.2.0.tgz", + "integrity": "sha512-ZYDqPLGxDkDhDZBjZBb+oD1+j0rA4E0pXY50eplAAOPg2N/gUBSSk5IM1/QhPfyVo19lJ+CvXpqfvk+b2p/8Ng==", + "requires": { + "expand-tilde": "^2.0.2", + "is-plain-object": "^2.0.3", + "object.defaults": "^1.1.0", + "object.pick": "^1.2.0", + "parse-filepath": "^1.0.1" + } + }, + "first-chunk-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/first-chunk-stream/-/first-chunk-stream-1.0.0.tgz", + "integrity": "sha1-Wb+1DNkF9g18OUzT2ayqtOatk04=" + }, + "flagged-respawn": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-1.0.1.tgz", + "integrity": "sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q==" + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=" + }, + "for-own": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", + "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", + "requires": { + "for-in": "^1.0.1" + } + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" + }, + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "requires": { + "map-cache": "^0.2.2" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "fstream": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", + "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", + "requires": { + "graceful-fs": "^4.1.2", + "inherits": "~2.0.0", + "mkdirp": ">=0.5 0", + "rimraf": "2" + }, + "dependencies": { + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==" + } + } + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + }, + "dependencies": { + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + } + } + }, + "gaze": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/gaze/-/gaze-0.5.2.tgz", + "integrity": "sha1-QLcJU30k0dRXZ9takIaJ3+aaxE8=", + "requires": { + "globule": "~0.1.0" + } + }, + "get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==" + }, + "get-stdin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", + "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=" + }, + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=" + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "glob": { + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-4.5.3.tgz", + "integrity": "sha1-xstz0yJsHv7wTePFbQEvAzd+4V8=", + "requires": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^2.0.1", + "once": "^1.3.0" + } + }, + "glob-stream": { + "version": "3.1.18", + "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-3.1.18.tgz", + "integrity": "sha1-kXCl8St5Awb9/lmPMT+PeVT9FDs=", + "requires": { + "glob": "^4.3.1", + "glob2base": "^0.0.12", + "minimatch": "^2.0.1", + "ordered-read-streams": "^0.1.0", + "through2": "^0.6.1", + "unique-stream": "^1.0.0" + }, + "dependencies": { + "readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "through2": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", + "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", + "requires": { + "readable-stream": ">=1.0.33-1 <1.1.0-0", + "xtend": ">=4.0.0 <4.1.0-0" + } + } + } + }, + "glob-watcher": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/glob-watcher/-/glob-watcher-0.0.6.tgz", + "integrity": "sha1-uVtKjfdLOcgymLDAXJeLTZo7cQs=", + "requires": { + "gaze": "^0.5.1" + } + }, + "glob2base": { + "version": "0.0.12", + "resolved": "https://registry.npmjs.org/glob2base/-/glob2base-0.0.12.tgz", + "integrity": "sha1-nUGbPijxLoOjYhZKJ3BVkiycDVY=", + "requires": { + "find-index": "^0.1.1" + } + }, + "global-modules": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", + "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", + "requires": { + "global-prefix": "^1.0.1", + "is-windows": "^1.0.1", + "resolve-dir": "^1.0.0" + } + }, + "global-prefix": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", + "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=", + "requires": { + "expand-tilde": "^2.0.2", + "homedir-polyfill": "^1.0.1", + "ini": "^1.3.4", + "is-windows": "^1.0.1", + "which": "^1.2.14" + } + }, + "globule": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/globule/-/globule-0.1.0.tgz", + "integrity": "sha1-2cjt3h2nnRJaFRt5UzuXhnY0auU=", + "requires": { + "glob": "~3.1.21", + "lodash": "~1.0.1", + "minimatch": "~0.2.11" + }, + "dependencies": { + "glob": { + "version": "3.1.21", + "resolved": "https://registry.npmjs.org/glob/-/glob-3.1.21.tgz", + "integrity": "sha1-0p4KBV3qUTj00H7UDomC6DwgZs0=", + "requires": { + "graceful-fs": "~1.2.0", + "inherits": "1", + "minimatch": "~0.2.11" + } + }, + "graceful-fs": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-1.2.3.tgz", + "integrity": "sha1-FaSAaldUfLLS2/J/QuiajDRRs2Q=" + }, + "inherits": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-1.0.2.tgz", + "integrity": "sha1-ykMJ2t7mtUzAuNJH6NfHoJdb3Js=" + }, + "minimatch": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", + "integrity": "sha1-x054BXT2PG+aCQ6Q775u9TpqdWo=", + "requires": { + "lru-cache": "2", + "sigmund": "~1.0.0" + } + } + } + }, + "glogg": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/glogg/-/glogg-1.0.2.tgz", + "integrity": "sha512-5mwUoSuBk44Y4EshyiqcH95ZntbDdTQqA3QYSrxmzj28Ai0vXBGMH1ApSANH14j2sIRtqCEyg6PfsuP7ElOEDA==", + "requires": { + "sparkles": "^1.0.0" + } + }, + "graceful-fs": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-3.0.12.tgz", + "integrity": "sha512-J55gaCS4iTTJfTXIxSVw3EMQckcqkpdRv3IR7gu6sq0+tbC363Zx6KH/SEwXASK9JRbhyZmVjJEVJIOxYsB3Qg==", + "requires": { + "natives": "^1.1.3" + } + }, + "gulp": { + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/gulp/-/gulp-3.9.1.tgz", + "integrity": "sha1-VxzkWSjdQK9lFPxAEYZgFsE4RbQ=", + "requires": { + "archy": "^1.0.0", + "chalk": "^1.0.0", + "deprecated": "^0.0.1", + "gulp-util": "^3.0.0", + "interpret": "^1.0.0", + "liftoff": "^2.1.0", + "minimist": "^1.1.0", + "orchestrator": "^0.3.0", + "pretty-hrtime": "^1.0.0", + "semver": "^4.1.0", + "tildify": "^1.0.0", + "v8flags": "^2.0.2", + "vinyl-fs": "^0.3.0" + } + }, + "gulp-autoprefixer": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/gulp-autoprefixer/-/gulp-autoprefixer-3.1.1.tgz", + "integrity": "sha1-dSMAUc0NFxND14O36bXREg7u+bA=", + "requires": { + "autoprefixer": "^6.0.0", + "gulp-util": "^3.0.0", + "postcss": "^5.0.4", + "through2": "^2.0.0", + "vinyl-sourcemaps-apply": "^0.2.0" + } + }, + "gulp-postcss": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/gulp-postcss/-/gulp-postcss-6.4.0.tgz", + "integrity": "sha1-eKMuPIeqbNzsWuHJBeGW1HjoxdU=", + "requires": { + "gulp-util": "^3.0.8", + "postcss": "^5.2.12", + "postcss-load-config": "^1.2.0", + "vinyl-sourcemaps-apply": "^0.2.1" + } + }, + "gulp-rename": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/gulp-rename/-/gulp-rename-1.4.0.tgz", + "integrity": "sha512-swzbIGb/arEoFK89tPY58vg3Ok1bw+d35PfUNwWqdo7KM4jkmuGA78JiDNqR+JeZFaeeHnRg9N7aihX3YPmsyg==" + }, + "gulp-requirejs-optimize": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/gulp-requirejs-optimize/-/gulp-requirejs-optimize-0.3.2.tgz", + "integrity": "sha1-/xX+2Oq1otcAS2WWaC1EUv+6kwo=", + "requires": { + "chalk": "^1.1.1", + "gulp-util": "^3.0.7", + "lodash.defaults": "^4.0.1", + "requirejs": "^2.1.22", + "through2": "^2.0.1", + "vinyl-sourcemaps-apply": "^0.2.1" + } + }, + "gulp-sass": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/gulp-sass/-/gulp-sass-2.3.2.tgz", + "integrity": "sha1-grerkP6QLNw0wE8YDZLyw0kC3VI=", + "requires": { + "gulp-util": "^3.0", + "lodash.clonedeep": "^4.3.2", + "node-sass": "^3.4.2", + "through2": "^2.0.0", + "vinyl-sourcemaps-apply": "^0.2.0" + } + }, + "gulp-sourcemaps": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/gulp-sourcemaps/-/gulp-sourcemaps-1.12.1.tgz", + "integrity": "sha1-tDfR89mAzyboEYSCNxjOFa5ll7Y=", + "requires": { + "@gulp-sourcemaps/map-sources": "1.X", + "acorn": "4.X", + "convert-source-map": "1.X", + "css": "2.X", + "debug-fabulous": "0.0.X", + "detect-newline": "2.X", + "graceful-fs": "4.X", + "source-map": "~0.6.0", + "strip-bom": "2.X", + "through2": "2.X", + "vinyl": "1.X" + }, + "dependencies": { + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "requires": { + "is-utf8": "^0.2.0" + } + }, + "vinyl": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-1.2.0.tgz", + "integrity": "sha1-XIgDbPVl5d8FVYv8kR+GVt8hiIQ=", + "requires": { + "clone": "^1.0.0", + "clone-stats": "^0.0.1", + "replace-ext": "0.0.1" + } + } + } + }, + "gulp-uglify": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/gulp-uglify/-/gulp-uglify-1.5.4.tgz", + "integrity": "sha1-UkeI2HZm0J+dDCH7IXf5ADmmWMk=", + "requires": { + "deap": "^1.0.0", + "fancy-log": "^1.0.0", + "gulp-util": "^3.0.0", + "isobject": "^2.0.0", + "through2": "^2.0.0", + "uglify-js": "2.6.4", + "uglify-save-license": "^0.4.1", + "vinyl-sourcemaps-apply": "^0.2.0" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "requires": { + "isarray": "1.0.0" + } + } + } + }, + "gulp-util": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/gulp-util/-/gulp-util-3.0.8.tgz", + "integrity": "sha1-AFTh50RQLifATBh8PsxQXdVLu08=", + "requires": { + "array-differ": "^1.0.0", + "array-uniq": "^1.0.2", + "beeper": "^1.0.0", + "chalk": "^1.0.0", + "dateformat": "^2.0.0", + "fancy-log": "^1.1.0", + "gulplog": "^1.0.0", + "has-gulplog": "^0.1.0", + "lodash._reescape": "^3.0.0", + "lodash._reevaluate": "^3.0.0", + "lodash._reinterpolate": "^3.0.0", + "lodash.template": "^3.0.0", + "minimist": "^1.1.0", + "multipipe": "^0.1.2", + "object-assign": "^3.0.0", + "replace-ext": "0.0.1", + "through2": "^2.0.0", + "vinyl": "^0.5.0" + } + }, + "gulplog": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/gulplog/-/gulplog-1.0.0.tgz", + "integrity": "sha1-4oxNRdBey77YGDY86PnFkmIp/+U=", + "requires": { + "glogg": "^1.0.0" + } + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" + }, + "har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "requires": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + } + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=" + }, + "has-gulplog": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/has-gulplog/-/has-gulplog-0.1.0.tgz", + "integrity": "sha1-ZBTIKRNpfaUVkDl9r7EvIpZ4Ec4=", + "requires": { + "sparkles": "^1.0.0" + } + }, + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" + }, + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" + }, + "has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "requires": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + } + }, + "has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "requires": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "dependencies": { + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "homedir-polyfill": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", + "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", + "requires": { + "parse-passwd": "^1.0.0" + } + }, + "hosted-git-info": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", + "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==" + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "in-publish": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/in-publish/-/in-publish-2.0.1.tgz", + "integrity": "sha512-oDM0kUSNFC31ShNxHKUyfZKy8ZeXZBWMjMdZHKLOk13uvT27VTL/QzRGfRUcevJhpkZAvlhPYuXkF7eNWrtyxQ==" + }, + "indent-string": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", + "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", + "requires": { + "repeating": "^2.0.0" + } + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" + }, + "interpret": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", + "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==" + }, + "invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=" + }, + "is-absolute": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz", + "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==", + "requires": { + "is-relative": "^1.0.0", + "is-windows": "^1.0.1" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + }, + "is-callable": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz", + "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==" + }, + "is-core-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.0.0.tgz", + "integrity": "sha512-jq1AH6C8MuteOoBPwkxHafmByhL9j5q4OaPGdbuD+ZtQJVzH+i6E3BJDQcBA09k57i2Hh2yQbEG8yObZ0jdlWw==", + "requires": { + "has": "^1.0.3" + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-date-object": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", + "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==" + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" + } + } + }, + "is-directory": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", + "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=" + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=" + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" + }, + "is-finite": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz", + "integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==" + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "requires": { + "is-extglob": "^2.1.0" + } + }, + "is-negative-zero": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.0.tgz", + "integrity": "sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE=" + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "requires": { + "isobject": "^3.0.1" + } + }, + "is-regex": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", + "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", + "requires": { + "has-symbols": "^1.0.1" + } + }, + "is-relative": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz", + "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==", + "requires": { + "is-unc-path": "^1.0.0" + } + }, + "is-symbol": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", + "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", + "requires": { + "has-symbols": "^1.0.1" + } + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + }, + "is-unc-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz", + "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==", + "requires": { + "unc-path-regex": "^0.1.2" + } + }, + "is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=" + }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==" + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + }, + "js-base64": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.6.4.tgz", + "integrity": "sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ==" + }, + "js-yaml": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", + "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" + }, + "lazy-cache": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", + "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=" + }, + "lazy-debug-legacy": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/lazy-debug-legacy/-/lazy-debug-legacy-0.0.1.tgz", + "integrity": "sha1-U3cWwHduTPeePtG2IfdljCkRsbE=" + }, + "lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", + "requires": { + "invert-kv": "^1.0.0" + } + }, + "liftoff": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/liftoff/-/liftoff-2.5.0.tgz", + "integrity": "sha1-IAkpG7Mc6oYbvxCnwVooyvdcMew=", + "requires": { + "extend": "^3.0.0", + "findup-sync": "^2.0.0", + "fined": "^1.0.1", + "flagged-respawn": "^1.0.0", + "is-plain-object": "^2.0.4", + "object.map": "^1.0.0", + "rechoir": "^0.6.2", + "resolve": "^1.1.7" + } + }, + "load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" + }, + "dependencies": { + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==" + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "requires": { + "is-utf8": "^0.2.0" + } + } + } + }, + "lodash": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-1.0.2.tgz", + "integrity": "sha1-j1dWDIO1n8JwvT1WG2kAQ0MOJVE=" + }, + "lodash._basecopy": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz", + "integrity": "sha1-jaDmqHbPNEwK2KVIghEd08XHyjY=" + }, + "lodash._basetostring": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash._basetostring/-/lodash._basetostring-3.0.1.tgz", + "integrity": "sha1-0YYdh3+CSlL2aYMtyvPuFVZqB9U=" + }, + "lodash._basevalues": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._basevalues/-/lodash._basevalues-3.0.0.tgz", + "integrity": "sha1-W3dXYoAr3j0yl1A+JjAIIP32Ybc=" + }, + "lodash._getnative": { + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz", + "integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=" + }, + "lodash._isiterateecall": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz", + "integrity": "sha1-UgOte6Ql+uhCRg5pbbnPPmqsBXw=" + }, + "lodash._reescape": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._reescape/-/lodash._reescape-3.0.0.tgz", + "integrity": "sha1-Kx1vXf4HyKNVdT5fJ/rH8c3hYWo=" + }, + "lodash._reevaluate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._reevaluate/-/lodash._reevaluate-3.0.0.tgz", + "integrity": "sha1-WLx0xAZklTrgsSTYBpltrKQx4u0=" + }, + "lodash._reinterpolate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", + "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=" + }, + "lodash._root": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash._root/-/lodash._root-3.0.1.tgz", + "integrity": "sha1-+6HEUkwZ7ppfgTa0YJ8BfPTe1pI=" + }, + "lodash.assign": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz", + "integrity": "sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=" + }, + "lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=" + }, + "lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=" + }, + "lodash.escape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-3.2.0.tgz", + "integrity": "sha1-mV7g3BjBtIzJLv+ucaEKq1tIdpg=", + "requires": { + "lodash._root": "^3.0.0" + } + }, + "lodash.isarguments": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", + "integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=" + }, + "lodash.isarray": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz", + "integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=" + }, + "lodash.keys": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", + "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=", + "requires": { + "lodash._getnative": "^3.0.0", + "lodash.isarguments": "^3.0.0", + "lodash.isarray": "^3.0.0" + } + }, + "lodash.restparam": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/lodash.restparam/-/lodash.restparam-3.6.1.tgz", + "integrity": "sha1-k2pOMJ7zMKdkXtQUWYbIWuWyCAU=" + }, + "lodash.template": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-3.6.2.tgz", + "integrity": "sha1-+M3sxhaaJVvpCYrosMU9N4kx0U8=", + "requires": { + "lodash._basecopy": "^3.0.0", + "lodash._basetostring": "^3.0.0", + "lodash._basevalues": "^3.0.0", + "lodash._isiterateecall": "^3.0.0", + "lodash._reinterpolate": "^3.0.0", + "lodash.escape": "^3.0.0", + "lodash.keys": "^3.0.0", + "lodash.restparam": "^3.0.0", + "lodash.templatesettings": "^3.0.0" + } + }, + "lodash.templatesettings": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-3.1.1.tgz", + "integrity": "sha1-+zB4RHU7Zrnxr6VOJix0UwfbqOU=", + "requires": { + "lodash._reinterpolate": "^3.0.0", + "lodash.escape": "^3.0.0" + } + }, + "longest": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", + "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=" + }, + "loud-rejection": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", + "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", + "requires": { + "currently-unhandled": "^0.4.1", + "signal-exit": "^3.0.0" + } + }, + "lru-cache": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz", + "integrity": "sha1-bUUk6LlV+V1PW1iFHOId1y+06VI=" + }, + "make-iterator": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz", + "integrity": "sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw==", + "requires": { + "kind-of": "^6.0.2" + } + }, + "map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=" + }, + "map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=" + }, + "map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "requires": { + "object-visit": "^1.0.0" + } + }, + "meow": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", + "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", + "requires": { + "camelcase-keys": "^2.0.0", + "decamelize": "^1.1.2", + "loud-rejection": "^1.0.0", + "map-obj": "^1.0.1", + "minimist": "^1.1.3", + "normalize-package-data": "^2.3.4", + "object-assign": "^4.0.1", + "read-pkg-up": "^1.0.1", + "redent": "^1.0.0", + "trim-newlines": "^1.0.0" + }, + "dependencies": { + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + } + } + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "mime-db": { + "version": "1.44.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", + "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==" + }, + "mime-types": { + "version": "2.1.27", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", + "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", + "requires": { + "mime-db": "1.44.0" + } + }, + "minimatch": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.10.tgz", + "integrity": "sha1-jQh8OcazjAAbl/ynzm0OHoCvusc=", + "requires": { + "brace-expansion": "^1.0.0" + } + }, + "minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==" + }, + "mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "requires": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "requires": { + "minimist": "^1.2.5" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "multipipe": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/multipipe/-/multipipe-0.1.2.tgz", + "integrity": "sha1-Ko8t33Du1WTf8tV/HhoTfZ8FB4s=", + "requires": { + "duplexer2": "0.0.2" + } + }, + "nan": { + "version": "2.14.2", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz", + "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==" + }, + "nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + } + }, + "natives": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/natives/-/natives-1.1.6.tgz", + "integrity": "sha512-6+TDFewD4yxY14ptjKaS63GVdtKiES1pTPyxn9Jb0rBqPMZ7VcCiooEhPNsr+mqHtMGxa/5c/HhcC4uPEUw/nA==" + }, + "node-gyp": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.8.0.tgz", + "integrity": "sha512-3g8lYefrRRzvGeSowdJKAKyks8oUpLEd/DyPV4eMhVlhJ0aNaZqIrNUIPuEWWTAoPqyFkfGrM67MC69baqn6vA==", + "requires": { + "fstream": "^1.0.0", + "glob": "^7.0.3", + "graceful-fs": "^4.1.2", + "mkdirp": "^0.5.0", + "nopt": "2 || 3", + "npmlog": "0 || 1 || 2 || 3 || 4", + "osenv": "0", + "request": "^2.87.0", + "rimraf": "2", + "semver": "~5.3.0", + "tar": "^2.0.0", + "which": "1" + }, + "dependencies": { + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==" + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "semver": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", + "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=" + } + } + }, + "node-sass": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-3.13.1.tgz", + "integrity": "sha1-ckD7v/I5YwS0IjUn7TAgWJwAT8I=", + "requires": { + "async-foreach": "^0.1.3", + "chalk": "^1.1.1", + "cross-spawn": "^3.0.0", + "gaze": "^1.0.0", + "get-stdin": "^4.0.1", + "glob": "^7.0.3", + "in-publish": "^2.0.0", + "lodash.assign": "^4.2.0", + "lodash.clonedeep": "^4.3.2", + "meow": "^3.7.0", + "mkdirp": "^0.5.1", + "nan": "^2.3.2", + "node-gyp": "^3.3.1", + "npmlog": "^4.0.0", + "request": "^2.61.0", + "sass-graph": "^2.1.1" + }, + "dependencies": { + "gaze": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz", + "integrity": "sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==", + "requires": { + "globule": "^1.0.0" + } + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "globule": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/globule/-/globule-1.3.2.tgz", + "integrity": "sha512-7IDTQTIu2xzXkT+6mlluidnWo+BypnbSoEVVQCGfzqnl5Ik8d3e1d4wycb8Rj9tWW+Z39uPWsdlquqiqPCd/pA==", + "requires": { + "glob": "~7.1.1", + "lodash": "~4.17.10", + "minimatch": "~3.0.2" + } + }, + "lodash": { + "version": "4.17.20", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + } + } + }, + "nopt": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", + "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", + "requires": { + "abbrev": "1" + } + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "requires": { + "remove-trailing-separator": "^1.0.1" + } + }, + "normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=" + }, + "npmlog": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "num2fraction": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", + "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=" + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" + }, + "object-assign": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz", + "integrity": "sha1-m+3VygiXlJvKR+f/QIBi1Un1h/I=" + }, + "object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "requires": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "object-inspect": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", + "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==" + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" + }, + "object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "requires": { + "isobject": "^3.0.0" + } + }, + "object.assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.1.tgz", + "integrity": "sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.0", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + } + }, + "object.defaults": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/object.defaults/-/object.defaults-1.1.0.tgz", + "integrity": "sha1-On+GgzS0B96gbaFtiNXNKeQ1/s8=", + "requires": { + "array-each": "^1.0.1", + "array-slice": "^1.0.0", + "for-own": "^1.0.0", + "isobject": "^3.0.0" + } + }, + "object.map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object.map/-/object.map-1.0.1.tgz", + "integrity": "sha1-z4Plncj8wK1fQlDh94s7gb2AHTc=", + "requires": { + "for-own": "^1.0.0", + "make-iterator": "^1.0.0" + } + }, + "object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "requires": { + "isobject": "^3.0.1" + } + }, + "once": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/once/-/once-1.3.3.tgz", + "integrity": "sha1-suJhVXzkwxTsgwTz+oJmPkKXyiA=", + "requires": { + "wrappy": "1" + } + }, + "orchestrator": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/orchestrator/-/orchestrator-0.3.8.tgz", + "integrity": "sha1-FOfp4nZPcxX7rBhOUGx6pt+UrX4=", + "requires": { + "end-of-stream": "~0.1.5", + "sequencify": "~0.0.7", + "stream-consume": "~0.1.0" + } + }, + "ordered-read-streams": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-0.1.0.tgz", + "integrity": "sha1-/VZamvjrRHO6abbtijQ1LLVS8SY=" + }, + "os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" + }, + "os-locale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", + "requires": { + "lcid": "^1.0.0" + } + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" + }, + "osenv": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", + "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "parse-filepath": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz", + "integrity": "sha1-pjISf1Oq89FYdvWHLz/6x2PWyJE=", + "requires": { + "is-absolute": "^1.0.0", + "map-cache": "^0.2.0", + "path-root": "^0.1.1" + } + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "requires": { + "error-ex": "^1.2.0" + } + }, + "parse-node-version": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz", + "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==" + }, + "parse-passwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", + "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=" + }, + "pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=" + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "requires": { + "pinkie-promise": "^2.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" + }, + "path-root": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz", + "integrity": "sha1-mkpoFMrBwM1zNgqV8yCDyOpHRbc=", + "requires": { + "path-root-regex": "^0.1.0" + } + }, + "path-root-regex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz", + "integrity": "sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0=" + }, + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "requires": { + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "dependencies": { + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==" + } + } + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=" + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "requires": { + "pinkie": "^2.0.0" + } + }, + "posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=" + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "requires": { + "chalk": "^1.1.3", + "js-base64": "^2.1.9", + "source-map": "^0.5.6", + "supports-color": "^3.2.3" + } + }, + "postcss-load-config": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-1.2.0.tgz", + "integrity": "sha1-U56a/J3chiASHr+djDZz4M5Q0oo=", + "requires": { + "cosmiconfig": "^2.1.0", + "object-assign": "^4.1.0", + "postcss-load-options": "^1.2.0", + "postcss-load-plugins": "^2.3.0" + }, + "dependencies": { + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + } + } + }, + "postcss-load-options": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postcss-load-options/-/postcss-load-options-1.2.0.tgz", + "integrity": "sha1-sJixVZ3awt8EvAuzdfmaXP4rbYw=", + "requires": { + "cosmiconfig": "^2.1.0", + "object-assign": "^4.1.0" + }, + "dependencies": { + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + } + } + }, + "postcss-load-plugins": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/postcss-load-plugins/-/postcss-load-plugins-2.3.0.tgz", + "integrity": "sha1-dFdoEWWZrKLwCfrUJrABdQSdjZI=", + "requires": { + "cosmiconfig": "^2.1.1", + "object-assign": "^4.1.0" + }, + "dependencies": { + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + } + } + }, + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "pretty-hrtime": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", + "integrity": "sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=" + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" + }, + "psl": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==" + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + }, + "read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "requires": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + } + }, + "read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "requires": { + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" + } + }, + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", + "requires": { + "resolve": "^1.1.6" + } + }, + "redent": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", + "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", + "requires": { + "indent-string": "^2.1.0", + "strip-indent": "^1.0.1" + } + }, + "regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "requires": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + } + }, + "remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=" + }, + "repeat-element": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", + "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==" + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" + }, + "repeating": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", + "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", + "requires": { + "is-finite": "^1.0.0" + } + }, + "replace-ext": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-0.0.1.tgz", + "integrity": "sha1-KbvZIHinOfC8zitO5B6DeVNSKSQ=" + }, + "request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" + }, + "require-from-string": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-1.2.1.tgz", + "integrity": "sha1-UpyczvJzgK3+yaL5ZbZJu+5jZBg=" + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=" + }, + "requirejs": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/requirejs/-/requirejs-2.3.6.tgz", + "integrity": "sha512-ipEzlWQe6RK3jkzikgCupiTbTvm4S0/CAU5GlgptkN5SO6F3u0UD0K18wy6ErDqiCyP4J4YYe1HuAShvsxePLg==" + }, + "resolve": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.18.1.tgz", + "integrity": "sha512-lDfCPaMKfOJXjy0dPayzPdF1phampNWr3qFCjAu+rw/qbQmr5jWH5xN2hwh9QKfw9E5v4hwV7A+jrCmL8yjjqA==", + "requires": { + "is-core-module": "^2.0.0", + "path-parse": "^1.0.6" + } + }, + "resolve-dir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", + "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=", + "requires": { + "expand-tilde": "^2.0.0", + "global-modules": "^1.0.0" + } + }, + "resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=" + }, + "ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==" + }, + "right-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", + "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", + "requires": { + "align-text": "^0.1.1" + } + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "requires": { + "glob": "^7.1.3" + }, + "dependencies": { + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + } + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "requires": { + "ret": "~0.1.10" + } + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "sass-graph": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-2.2.6.tgz", + "integrity": "sha512-MKuEYXFSGuRSi8FZ3A7imN1CeVn9Gpw0/SFJKdL1ejXJneI9a5rwlEZrKejhEFAA3O6yr3eIyl/WuvASvlT36g==", + "requires": { + "glob": "^7.0.0", + "lodash": "^4.0.0", + "scss-tokenizer": "^0.2.3", + "yargs": "^7.0.0" + }, + "dependencies": { + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "lodash": { + "version": "4.17.20", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + } + } + }, + "scss-tokenizer": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz", + "integrity": "sha1-jrBtualyMzOCTT9VMGQRSYR85dE=", + "requires": { + "js-base64": "^2.1.8", + "source-map": "^0.4.2" + }, + "dependencies": { + "source-map": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", + "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", + "requires": { + "amdefine": ">=0.0.4" + } + } + } + }, + "semver": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/semver/-/semver-4.3.6.tgz", + "integrity": "sha1-MAvG4OhjdPe6YQaLWx7NV/xlMto=" + }, + "sequencify": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/sequencify/-/sequencify-0.0.7.tgz", + "integrity": "sha1-kM/xnQLgcCf9dn9erT57ldHnOAw=" + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + }, + "set-value": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "sigmund": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", + "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=" + }, + "signal-exit": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" + }, + "snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "requires": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "requires": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "requires": { + "kind-of": "^3.2.0" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + }, + "source-map-resolve": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", + "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", + "requires": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "source-map-url": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=" + }, + "sparkles": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sparkles/-/sparkles-1.0.1.tgz", + "integrity": "sha512-dSO0DDYUahUt/0/pD/Is3VIm5TGJjludZ0HVymmhYF6eNA53PVLhnUk0znSYbH8IYBuJdCE+1luR22jNLMaQdw==" + }, + "spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==" + }, + "spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.6.tgz", + "integrity": "sha512-+orQK83kyMva3WyPf59k1+Y525csj5JejicWut55zeTWANuN17qSiSLUXWtzHeNWORSvT7GLDJ/E/XiIWoXBTw==" + }, + "split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "requires": { + "extend-shallow": "^3.0.0" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" + }, + "sshpk": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, + "static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "requires": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "stream-consume": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/stream-consume/-/stream-consume-0.1.1.tgz", + "integrity": "sha512-tNa3hzgkjEP7XbCkbRXe1jpg+ievoa0O4SCFlMOYEscGSS4JJsckGL8swUyAa/ApGU3Ae4t6Honor4HhL+tRyg==" + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "string.prototype.trimend": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.2.tgz", + "integrity": "sha512-8oAG/hi14Z4nOVP0z6mdiVZ/wqjDtWSLygMigTzAb+7aPEDTleeFf+WrF+alzecxIRkckkJVn+dTlwzJXORATw==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.1" + } + }, + "string.prototype.trimstart": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.2.tgz", + "integrity": "sha512-7F6CdBTl5zyu30BJFdzSTlSlLPwODC23Od+iLoVH8X6+3fvDPPuBVVj9iaB1GOsSTSIgVfsfm27R2FGrAPznWg==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.1" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-bom": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-1.0.0.tgz", + "integrity": "sha1-hbiGLzhEtabV7IRnqTWYFzo295Q=", + "requires": { + "first-chunk-stream": "^1.0.0", + "is-utf8": "^0.2.0" + } + }, + "strip-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", + "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", + "requires": { + "get-stdin": "^4.0.1" + } + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "requires": { + "has-flag": "^1.0.0" + } + }, + "tar": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.2.tgz", + "integrity": "sha512-FCEhQ/4rE1zYv9rYXJw/msRqsnmlje5jHP6huWeBZ704jUTy02c5AZyWujpMR1ax6mVw9NyJMfuK2CMDWVIfgA==", + "requires": { + "block-stream": "*", + "fstream": "^1.0.12", + "inherits": "2" + } + }, + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "tildify": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/tildify/-/tildify-1.2.0.tgz", + "integrity": "sha1-3OwD9V3Km3qj5bBPIYF+tW5jWIo=", + "requires": { + "os-homedir": "^1.0.0" + } + }, + "time-stamp": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-1.1.0.tgz", + "integrity": "sha1-dkpaEa9QVhkhsTPztE5hhofg9cM=" + }, + "to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "requires": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + }, + "tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + }, + "trim-newlines": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", + "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=" + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" + }, + "uglify-js": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.6.4.tgz", + "integrity": "sha1-ZeovswWck5RpLxX+2HwrNsFrmt8=", + "requires": { + "async": "~0.2.6", + "source-map": "~0.5.1", + "uglify-to-browserify": "~1.0.0", + "yargs": "~3.10.0" + }, + "dependencies": { + "camelcase": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", + "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=" + }, + "cliui": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", + "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", + "requires": { + "center-align": "^0.1.1", + "right-align": "^0.1.1", + "wordwrap": "0.0.2" + } + }, + "yargs": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", + "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", + "requires": { + "camelcase": "^1.0.2", + "cliui": "^2.1.0", + "decamelize": "^1.0.0", + "window-size": "0.1.0" + } + } + } + }, + "uglify-save-license": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/uglify-save-license/-/uglify-save-license-0.4.1.tgz", + "integrity": "sha1-lXJsF8xv0XHDYX479NjYKqjEzOE=" + }, + "uglify-to-browserify": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", + "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=" + }, + "unc-path-regex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", + "integrity": "sha1-5z3T17DXxe2G+6xrCufYxqadUPo=" + }, + "union-value": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "requires": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" + } + }, + "unique-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-1.0.0.tgz", + "integrity": "sha1-1ZpKdUJ0R9mqbJHnAmP40mpLEEs=" + }, + "unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "requires": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "dependencies": { + "has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "requires": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "dependencies": { + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "requires": { + "isarray": "1.0.0" + } + } + } + }, + "has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=" + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + } + } + }, + "uri-js": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz", + "integrity": "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g==", + "requires": { + "punycode": "^2.1.0" + } + }, + "urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=" + }, + "use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==" + }, + "user-home": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/user-home/-/user-home-1.1.1.tgz", + "integrity": "sha1-K1viOjK2Onyd640PKNSFcko98ZA=" + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" + }, + "v8flags": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-2.1.1.tgz", + "integrity": "sha1-qrGh+jDUX4jdMhFIh1rALAtV5bQ=", + "requires": { + "user-home": "^1.1.1" + } + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "vinyl": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.5.3.tgz", + "integrity": "sha1-sEVbOPxeDPMNQyUTLkYZcMIJHN4=", + "requires": { + "clone": "^1.0.0", + "clone-stats": "^0.0.1", + "replace-ext": "0.0.1" + } + }, + "vinyl-fs": { + "version": "0.3.14", + "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-0.3.14.tgz", + "integrity": "sha1-mmhRzhysHBzqX+hsCTHWIMLPqeY=", + "requires": { + "defaults": "^1.0.0", + "glob-stream": "^3.1.5", + "glob-watcher": "^0.0.6", + "graceful-fs": "^3.0.0", + "mkdirp": "^0.5.0", + "strip-bom": "^1.0.0", + "through2": "^0.6.1", + "vinyl": "^0.4.0" + }, + "dependencies": { + "clone": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/clone/-/clone-0.2.0.tgz", + "integrity": "sha1-xhJqkK1Pctv1rNskPMN3JP6T/B8=" + }, + "readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "through2": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", + "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", + "requires": { + "readable-stream": ">=1.0.33-1 <1.1.0-0", + "xtend": ">=4.0.0 <4.1.0-0" + } + }, + "vinyl": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.4.6.tgz", + "integrity": "sha1-LzVsh6VQolVGHza76ypbqL94SEc=", + "requires": { + "clone": "^0.2.0", + "clone-stats": "^0.0.1" + } + } + } + }, + "vinyl-sourcemaps-apply": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/vinyl-sourcemaps-apply/-/vinyl-sourcemaps-apply-0.2.1.tgz", + "integrity": "sha1-q2VJ1h0XLCsbh75cUI0jnI74dwU=", + "requires": { + "source-map": "^0.5.1" + } + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", + "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=" + }, + "wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "requires": { + "string-width": "^1.0.2 || 2" + } + }, + "window-size": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", + "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=" + }, + "wordwrap": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", + "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=" + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" + }, + "y18n": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", + "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=" + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" + }, + "yargs": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.1.tgz", + "integrity": "sha512-huO4Fr1f9PmiJJdll5kwoS2e4GqzGSsMT3PPMpOwoVkOK8ckqAewMTZyA6LXVQWflleb/Z8oPBEvNsMft0XE+g==", + "requires": { + "camelcase": "^3.0.0", + "cliui": "^3.2.0", + "decamelize": "^1.1.1", + "get-caller-file": "^1.0.1", + "os-locale": "^1.4.0", + "read-pkg-up": "^1.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^1.0.2", + "which-module": "^1.0.0", + "y18n": "^3.2.1", + "yargs-parser": "5.0.0-security.0" + }, + "dependencies": { + "camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=" + } + } + }, + "yargs-parser": { + "version": "5.0.0-security.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0-security.0.tgz", + "integrity": "sha512-T69y4Ps64LNesYxeYGYPvfoMTt/7y1XtfpIslUeK4um+9Hu7hlGoRtaDLvdXb7+/tfq4opVa2HRY5xGip022rQ==", + "requires": { + "camelcase": "^3.0.0", + "object.assign": "^4.1.0" + }, + "dependencies": { + "camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=" + } + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..c2f056c --- /dev/null +++ b/package.json @@ -0,0 +1,18 @@ +{ + "name": "ninja-forms", + "version": "1.0.0", + "scripts": { + "gulp": "gulp" + }, + "dependencies": { + "autoprefixer": "^6.3.6", + "gulp": "^3.9.1", + "gulp-autoprefixer": "^3.1.0", + "gulp-postcss": "^6.1.1", + "gulp-rename": "^1.2.2", + "gulp-requirejs-optimize": "^0.3.2", + "gulp-sass": "^2.2.0", + "gulp-sourcemaps": "^1.6.0", + "gulp-uglify": "^1.5.3" + } +} diff --git a/readme.txt b/readme.txt new file mode 100644 index 0000000..f7633c2 --- /dev/null +++ b/readme.txt @@ -0,0 +1,528 @@ +=== Ninja Forms - Conditionals Extension === +Contributors: kstover, jameslaws, kbjohnson, klhall1987, Much2tall, deckerweb +Donate link: http://ninjaforms.com +Tags: form, forms +Requires at least: 5.2 +Tested up to: 5.4 +Stable tag: 3.1 + +License: GPLv2 or later + +== Description == +The Ninja Forms Conditionals Extension allows you to create "smart" forms that can change dynamically based upon user input. Options can be added to dropdown lists based upon other input, or fields can be hidden or shown. + +== Screenshots == + +To see up to date screenshots, visit [NinjaForms.com](http://ninjaforms.com). + +== Installation == + +This section describes how to install the plugin and get it working. + +1. Upload the `ninja-forms` directory to your `/wp-content/plugins/` directory +2. Activate the plugin through the 'Plugins' menu in WordPress +3. Visit the 'Forms' menu item in your admin sidebar +4. When you create a form, you can now add conditionals on the field edit page. + +== Use == + +For help and video tutorials, please visit our website: [Ninja Forms Documentation](http://ninjaforms.com/documentation/intro/) + +== Changelog == + += 3.1 (21 April 2021) = + +*Changes:* + +* Conditions can now compare date fields. This allows users to create conditions that trigger when someone selects or enters a specific date or range. + += 3.0.28 (4 August 2020) = + +*Bugs:* + +* Resolved an issue that was preventing file uploads from being saved properly upon submission. +* Conditions should no longer lose reference to fields that have been renamed in the form builder. + += 3.0.27 (23 July 2020) = + +*Bugs:* + +* Resolved an issue that was causing conditions on actions to be ignored. + += 3.0.26.2 (21 July 2020) = + +*Bugs:* + +* Resolved an issue with the plugin auto-updater. + += 3.0.26.1 (20 July 2020) = + +*Security:* + +* Patched a data spoofing vulnerability that allowed required fields to be bypassed. + += 3.0.26 (25 September 2019) = + +*Bugs:* + +* Resolved an issue that sometimes prevented actions from firing, even when they had no attached conditions. +* Forms with a Stripe or PayPal action should now properly complete once returning from the payment screen. + += 3.0.25 (16 September 2019) = + +*Bugs:* + +* Hidden fields should now properly evaluate against empty. +* Recaptcha fields that are hidden will now properly render once shown. + +*Changes:* + +* Fields can now be conditionally set as required. + += 3.0.24 (23 January 2019) = + +*Bugs:* + +* Conditions based on calculations should now be properly triggered on form load. +* Resolved an issue that was sometimes causing actions to always fire, regardless of conditions. + +*Changes:* + +* Several incompatible field types have been removed from the list of fields that conditions can be based on. +* Inverse statements will no longer be created by default on new do statements. + += 3.0.23 (11 January 2019) = + +*Bugs:* + +* Action processing will now ignore incomplete conditional statements, which previously prevented the action from firing. + += 3.0.22 (14 June 2018) = + +*Changes:* + +* Fields now display admin labels (if they exist) instead of labels in condition blocks. + += 3.0.21 (3 May 2018) = + +*Bugs:* + +* Equals conditions based on calculations should now work properly. + += 3.0.20 (19 April 2018) = + +*Bugs:* + +* Duplicating a field should no longer cause conditional logic to lose track of it as a conditional trigger. + += 3.0.19 (26 March 2018) = + +*Bugs:* + +* Conditions based on the selection of single checkbox fields should now function properly. + += 3.0.18 (24 February 2018) = + +*Bugs:* + +* Checkbox values can now be updated via conditional logic again. + += 3.0.17 (22 August 2017) = + +*Bugs:* + +* Actions that use the greaterthan and lessthan comparators should work properly. +* Incorrectly setup conditions should no longer cause form display to crash. + += 3.0.16 (02 August 2017) = + +*Bugs:* + +* Action conditions should now properly support calculations. +* Fixed a bug that could cause calculations to fail when using Conditional Logic. + += 3.0.15 (27 June 2017) = + +*Changes:* + +* When setting up conditions, fields should now appear in alphabetical order within the field list. + +*Bugs:* + +* Conditional Logic should now work properly with the Save Progress add-on. + += 3.0.14 (31 May 2017) = + +*Bugs:* + +* Tabbing through a checkbox list that has conditions will no longer trigger those conditions incorrectly. + += 3.0.13 (02 May 2017) = + +*Bugs:* + +* Fixed a fatal error with PHP version 7.1 and higher. + += 3.0.12 (11 April 2017) = + +*Changes:* + +* Actions like Stripe can now be conditionally ran. + +*Bugs:* + +* Fixed a bug that caused some conditions to evaluate improperly. + += 3.0.11 (19 January 2017) = + +*Changes:* + +* Textbox fields can now be compared to an empty string. + +*Bugs:* + +* Help text should render properly for conditionally shown/hidden fields. + += 3.0.10 (09 December 2016) = + +*Bugs:* + +* Fixed a bug that could cause the condition drawer to fail to open if a field was deleted. +* Conditional Logic shouldn't prevent or enable actions that are otherwise disabled. + += 3.0.9 (15 November 2016) = + +*Bugs:* + +* Fixed a bug with list field options incorrectly triggering conditions based on partial matches. +* Fixed a bug with missing field values causing the form to not submit properly. +* Fixed a bug with false-positives when tabbing through a checkbox field. + +*Changes:* + +* Use the form cache for getting field data. +* Corrected processing for different data structures. +* Added a check for manually disabled actions, so as to not re-enable with conditions. + += 3.0.8 (25 October 2016) = + +*Bugs:* + +* The "any" operator in actions should work properly in all cases. +* Fixed a bug that caused fatal errors when conditions weren't configured properly. + += 3.0.7 (13 October 2016) = + +*Bugs:* + +* Creating conditions can now properly be based upon calculations. +* Fixed a bug with radio lists and the select option trigger. + +*Changes:* + +* When building conditions, fields should now show up with their admin label if one is set. + += 3.0.6 (03 October 2016) = + +*Bugs:* + +* Required fields should no longer attempt to valide upon show. +* Country fields can now be used in conditions. +* Fixed a couple of conversion issues with older form imports. +* Conditionally shown/hidden fields should all show properly in submission data. + +*Changes:* + +* Conditions can now be created using > and < with textboxes and textareas. + += 3.0.5 (28 September 2016) = + +*Bugs:* + +* File Uploads should now show in Conditional Logic conditions. + += 3.0.4 (22 September 2016) = + +*Bugs:* + +* Fixed a bug that could cause the builder to crash when fields were removed if there was a condition based upon that field. + += 3.0.3 (11 September 2016) = + +* Bugs:* + +* Fixed a bug that caused the condition edit drawer to fail to open. + += 3.0.2 (09 September 2016) = + +* Fixed a bug with conversion. + += 3.0.1 (06 September 2016) = + +* Updated with Ninja Forms v3.x compatibility + += 3.0 (06 September 2016) = + +* Updated with Ninja Forms v3.x compatibility +* Deprecated Ninja Forms v2.9.x compatible code + += 1.4.0 (13 April 2016) = + +*Changes:* + +* Update for compatibility with WordPress 4.5 ( specifically the underscore.js update ). + += 1.3.9 (26 May 2015) = + +*Bugs:* + +* Changed values should now reset to defaults when using the "clear form" setting. + += 1.3.8 (12 May 2015) = + +*Bugs:* + +* Array elements should now work properly with the "Contains" action conditionals. +* Fixed a PHP Notice. +* Decimals should now be compared properly. +* Fixed a bug that could cause a PHP error if asp style tags are enabled in PHP. + += 1.3.7 (18 March 2015) = + +*Bugs:* + +* Fixed a bug that could cause conditional field data to submit improperly. + += 1.3.6 (17 March 2015) = + +*Bugs:* + +* Fixed a bug that could cause JavaScript to load older versions of files. + += 1.3.5 (4 March 2015) = + +*Bugs:* + +* Fixed a bug that could cause conditionally hidden calculations to fail. +* List options should work properly in version 2.9 of Ninja Forms. + += 1.3.4 (3 March 2015) = + +*Bugs:* + +* Fixed a bug that could prevent new conditions from being added. + += 1.3.3 (27 February 2015) = + +*Changes:* + +* Preparing for the release of Ninja Forms version 2.9. + += 1.3.1 (17 November 2014) = + +*Bugs:* + +* Fixing bad domain/translation issues. +* Fixed a bug with checkbox lists and notification conditions. +* Duplicating a page with conditions using multi-part forms should now properly duplicate those conditions. +* Fixed several issues related to i18n. + += 1.3 (28 October 2014) = + +*Features:* + +* Conditional Logic now supports conditional notifications. +* Only show, display, or send a notification when a user submits specific form data. + +*Changes:* + +* Custom conditional triggers can be added for notifications. + +*Bugs:* + +* Fixed a bug that caused conditionals based upon other conditional fields to work improperly. +* Conditionally hidden totals should now be properly removed from the all fields table. + += 1.2.7 (24 July 2014) = + +*Changes:* + +* Compatibility with Ninja Forms 2.7. + += 1.2.6 = + +*Bugs:* + +* Fixed a bug that prevented some users from getting automatic updates. + += 1.2.5 = + +*Bugs:* + +* Fixed a bug that could cause conditions not to work in some AJAX setups. + += 1.2.4 = + +*Changes:* + +* Conditionals should now not be applied when editing a form in the wp-admin. + +*Bugs:* + +* Fixed a bug with the change value setting. + += 1.2.3 = + +*Bugs:* + +* Fixed a bug that prevented the 'add_value' and 'change_value' actions from working properly in some instances. + += 1.2.2 = + +*Bugs:* + +* Fixed a bug with required fields that were conditionally hidden. +* Removed console logs that were causing problems in IE9. +* Fixed a bug that caused the Add Value setting not to work properly. + += 1.2.1 = + +*Bugs:* + +* Fixed several bugs that related to pre-populating conditional fields with multi-part forms. + += 1.2 = + +*Changes:* + +* Added support for the new Ninja Forms loading system. This should significantly improve loading speed for forms that use conditionals. + += 1.1.1 = + +*Bugs:* + +* Fixed a bug that could prevent conditionals from working properly with required fields. +* Fixed a bug that could cause conditional logic to break when labels contained long strings of HTML. + +*Changes:* + +* Updating the JS so that when an element is shown/hidden, a jQuery event is fired after the show/hide is complete. +* Removed old licensing file. + += 1.1 = + +*Bugs:* + +* Fixed a bug that caused the "Change Value" conditional action to fail in some cases. +* Fixed a bug that prevented conditionals from working properly with hidden fields. +* Fixed several PHP Notices. + += 1.0.10 = + +*Bugs:* + +* Fixed a bug that prevented calculations from working properly when a field that the calculation was based upon was hidden with conditional logic. + += 1.0.9 = + +*Changes:* + +* Added a "visible" data attribute. +* Moved functions from Ninja Forms core to this extension. + +*Bugs:* + +* Fixed several bugs related to using calculation fields and conditionals. + += 1.0.8 = + +*Changes:* + +* Changed the license and auto-update system to the one available in Ninja Forms 2.2.47. + += 1.0.7 = + +*Changes:* + +* Changed references to wpninjas.com to ninjaforms.com. + += 1.0.6 = + +*Bugs:* + +* Fixed a bug that prevented conditionals from working properly in some installs. + += 1.0.5 = + +* Fixed a bug that caused Conditionals to break calculation fields if they were hidden. + += 1.0.4 = + +*Changes:* + +* Updates for compatibility with WordPress 3.6 + += 1.0.3 = + +*Bugs:* + +* Fixed a bug that prevented conditionals from working properly with calculation fields. + += 1.0.2 = + +*Bugs:* + +* Fixed a bug that caused conditionals with multiple criteria to fail when connected with the "All" parameter. + += 1.0.1 = + +*Changes:* + +* The Conditionals Extension can now be used with the Multi-Part Extension to show or hide entire pages. + += 1.0 = + +*Bugs:* + +* Fixed a bug that was causing dropdown list fields to work improperly with Conditional Logic. + += 0.9 = + +*Bugs:* + +* Fixed a bug that prevented conditionals from working properly with multi-checkbox lists and multi-radio button lists. + += 0.8 = + +*Changes:* + +* Changed the display JS slightly to be more efficient. + += 0.7 = + +*Bugs:* + +* Conditional fields should now behave as expected when editing user submissions. + += 0.6 = + +*Bugs:* + +* Fixed a bug that prevented conditionals from working properly with checkbox and radio list types. + += 0.5 = + +*Changes:* + +* Moved a JS function from ninja-forms-conditionals-admin.js to the ninja-forms-admin.js. + += 0.4 = +* Fixed a bug that prevented multiple forms with conditionals from being placed on the same page. + += 0.3 = +* Various bug fixes including: +* Adding multiple forms with conditions to a single page will now work normally. + += 0.2 = +* Various bug fixes. +* Changed the way that javascript and css files are loaded in extensions. \ No newline at end of file