├── .gitignore ├── README.md ├── assets ├── css │ ├── wpjsfsp-admin.css │ ├── wpjsfsp-admin.css.map │ └── wpjsfsp-admin.min.css ├── images │ ├── ahoy1.png │ └── ahoy2.png ├── js │ ├── src │ │ └── admin │ │ │ ├── general.js │ │ │ ├── plugins │ │ │ ├── colorpicker.js │ │ │ ├── conditions.js │ │ │ ├── forms.js │ │ │ ├── js-wp-editor.js │ │ │ ├── links.js │ │ │ ├── modals.js │ │ │ ├── models.js │ │ │ ├── rangesliders.js │ │ │ ├── select2.js │ │ │ ├── selectors.js │ │ │ ├── serialize-object.js │ │ │ ├── settings.js │ │ │ ├── tabs.js │ │ │ ├── templates.js │ │ │ └── utils.js │ │ │ └── vendor │ │ │ └── select2.full.custom.js │ ├── wpjsfsp-admin.js │ └── wpjsfsp-admin.min.js └── sass │ ├── admin.scss │ ├── modules │ ├── _fields.scss │ ├── _general.scss │ ├── _modal.scss │ ├── _select2.scss │ └── _tabs.scss │ ├── partials │ └── admin │ │ └── _settings.scss │ └── vendor │ └── select2 │ ├── _dropdown.scss │ ├── _multiple.scss │ ├── _single.scss │ ├── core.scss │ ├── mixins │ └── _gradients.scss │ └── theme │ ├── classic │ ├── _defaults.scss │ ├── _multiple.scss │ ├── _single.scss │ └── layout.scss │ └── default │ ├── _multiple.scss │ ├── _single.scss │ └── layout.scss ├── classes ├── Admin.php ├── Admin │ ├── Ajax.php │ ├── Assets.php │ ├── Footer_Templates.php │ └── Settings.php ├── Conditions.php ├── Helpers.php └── Options.php ├── gulpfile.js ├── includes ├── compat.php └── options.php ├── index.php ├── languages └── wpjsfsp.pot ├── package.json ├── readme.txt ├── screenshots ├── 1.png ├── 2.png ├── 3.png ├── 4.png ├── 5.png ├── 6.png ├── 7.png ├── 8.png └── 9.png └── wp-js-form-sample-plugin.php /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | release 3 | build -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![standard field types](https://raw.githubusercontent.com/danieliser/WP-JS-Form-Sample-Plugin/master/screenshots/3.png) 2 | 3 | Having built many interfaces in WordPress over the past 7 years I recently built this as a quick start to any project when admin forms are needed. It is built mostly in JavaScript but fields are easily passed from PHP as in this example. Features are too many to name but include: 4 | 5 | - Easy form / settings management. Add fields with a few extra lines in an array. 6 | - If you already manage fields in an array (EDD, Popup Maker) you can easily port to this new rendering method. 7 | - Lots of powerful custom fields including post type search fields, link pickers, license keys, color pickers, rangesliders and even a boolean based targerting/conditions manager. 8 | - Includes modal forms that also allow tab/subtab combinations (example coming soon). These are generated on the fly from the same types of field arrays as this form. 9 | - Field dependency management, inline documentation and great default styles mean you spend less time crafting forms and more time creating awesome features. 10 | - Also included is a simple to use Options class for storing and retrieving your settings in single wp_option row. Though this interface can easily be manipulated for post meta boxes as well. 11 | 12 | ## Tabbed Forms 13 | 14 | Besides offering field management, this library offers multiple variations of tabs, subtabs and sections to organize your forms. 15 | 16 | - Tabs and Subtabs can be set as vertical, horizontal or link tabs. 17 | - Styled to match the WordPress Admin interface, but can easily be customized to match your existing setup. 18 | - Forms can be organized with just fields, tabs of fields or tabs of subtabs of fields etc. 19 | 20 | 21 | ## Credits 22 | 23 | This is the culmination of work put in over the last year to build or rebuild the interfaces of several plugins including: 24 | 25 | - [@daniel_iser](https://twitter.com/daniel_iser) 26 | - Popup Maker 27 | - Ahoy! -------------------------------------------------------------------------------- /assets/images/ahoy1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danieliser/WP-JS-Form-Sample-Plugin/ce1a1d62979b884110ac2b47edac0b6e4f0d5e70/assets/images/ahoy1.png -------------------------------------------------------------------------------- /assets/images/ahoy2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danieliser/WP-JS-Form-Sample-Plugin/ce1a1d62979b884110ac2b47edac0b6e4f0d5e70/assets/images/ahoy2.png -------------------------------------------------------------------------------- /assets/js/src/admin/general.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @copyright Copyright (c) 2017, Code Atlantic 3 | * @author Daniel Iser 4 | */ 5 | (function ($) { 6 | "use strict"; 7 | 8 | var $document = $(document); 9 | 10 | window.WPJSFSP = window.WPJSFSP || {}; 11 | window.WPJSFSP.I10n = wpjsfsp_admin_vars.I10n; 12 | 13 | $.fn.wp_editor = WPJSFSP.wp_editor || {}; 14 | 15 | // Kick things off & initialize various modules. 16 | $document 17 | .ready(function () { 18 | $document.trigger('wpjsfsp_init'); 19 | 20 | // TODO Can't figure out why this is needed, but it looks stupid otherwise when the first condition field defaults to something other than the placeholder. 21 | $('#wpjsfsp-first-condition') 22 | .val(null) 23 | .trigger('change'); 24 | }) 25 | .on('keydown', '#message-headline', function (event) { 26 | var keyCode = event.keyCode || event.which; 27 | if (9 === keyCode) { 28 | event.preventDefault(); 29 | $('#title').focus(); 30 | } 31 | }) 32 | .on('keydown', '#title, #message-headline', function (event) { 33 | var keyCode = event.keyCode || event.which, 34 | target; 35 | if (!event.shiftKey && 9 === keyCode) { 36 | event.preventDefault(); 37 | target = $(this).attr('id') === 'title' ? '#message-headline' : '#insert-media-button'; 38 | $(target).focus(); 39 | } 40 | }) 41 | .on('keydown', '#message-headline, #insert-media-button', function (event) { 42 | var keyCode = event.keyCode || event.which, 43 | target; 44 | if (event.shiftKey && 9 === keyCode) { 45 | event.preventDefault(); 46 | target = $(this).attr('id') === 'message-headline' ? '#title' : '#message-headline'; 47 | $(target).focus(); 48 | } 49 | }); 50 | 51 | 52 | }(jQuery)); -------------------------------------------------------------------------------- /assets/js/src/admin/plugins/colorpicker.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @copyright Copyright (c) 2017, Code Atlantic 3 | * @author Daniel Iser 4 | */ 5 | (function ($) { 6 | "use strict"; 7 | 8 | var colorpicker = { 9 | init: function () { 10 | $('.wpjsfsp-color-picker').filter(':not(.wpjsfsp-color-picker-initialized)') 11 | .addClass('wpjsfsp-color-picker-initialized') 12 | .wpColorPicker({ 13 | change: function (event, ui) { 14 | $(event.target).trigger('colorchange', ui); 15 | }, 16 | clear: function (event) { 17 | $(event.target).prev().trigger('colorchange').wpColorPicker('close'); 18 | } 19 | }); 20 | } 21 | }; 22 | 23 | // Import this module. 24 | window.WPJSFSP = window.WPJSFSP || {}; 25 | window.WPJSFSP.colorpicker = colorpicker; 26 | 27 | $(document) 28 | .on('click', '.iris-palette', function () { 29 | $(this).parents('.wp-picker-active').find('input.wpjsfsp-color-picker').trigger('change'); 30 | }) 31 | .on('colorchange', function (event, ui) { 32 | var $input = $(event.target), 33 | $opacity = $input.parents('tr').next('tr.background-opacity'), 34 | color = ''; 35 | 36 | if (ui !== undefined && ui.color !== undefined) { 37 | color = ui.color.toString(); 38 | } 39 | 40 | if ($input.hasClass('background-color')) { 41 | if (typeof color === 'string' && color.length) { 42 | $opacity.show(); 43 | } else { 44 | $opacity.hide(); 45 | } 46 | } 47 | 48 | $input.val(color); 49 | }) 50 | .on('wpjsfsp_init', colorpicker.init); 51 | }(jQuery)); -------------------------------------------------------------------------------- /assets/js/src/admin/plugins/conditions.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @copyright Copyright (c) 2017, Code Atlantic 3 | * @author Daniel Iser 4 | */ 5 | (function ($) { 6 | "use strict"; 7 | 8 | var conditions = { 9 | get_conditions: function () { 10 | return window.wpjsfsp_settings_editor.conditions_selectlist; 11 | }, 12 | not_operand_checkbox: function ($element) { 13 | 14 | $element = $element || $('.wpjsfsp-not-operand'); 15 | 16 | return $element.each(function () { 17 | var $this = $(this), 18 | $input = $this.find('input'); 19 | 20 | $input.prop('checked', !$input.is(':checked')); 21 | 22 | WPJSFSP.conditions.toggle_not_operand($this); 23 | }); 24 | 25 | }, 26 | toggle_not_operand: function ($element) { 27 | $element = $element || $('.wpjsfsp-not-operand'); 28 | 29 | return $element.each(function () { 30 | var $this = $(this), 31 | $input = $this.find('input'), 32 | $is = $this.find('.is'), 33 | $not = $this.find('.not'), 34 | $container = $this.parents('.facet-target'); 35 | 36 | if ($input.is(':checked')) { 37 | $is.hide(); 38 | $not.show(); 39 | $container.addClass('not-operand-checked'); 40 | } else { 41 | $is.show(); 42 | $not.hide(); 43 | $container.removeClass('not-operand-checked'); 44 | } 45 | }); 46 | }, 47 | template: { 48 | editor: function (args) { 49 | var data = $.extend(true, {}, { 50 | groups: [] 51 | }, args); 52 | 53 | data.groups = WPJSFSP.utils.object_to_array(data.groups); 54 | 55 | return WPJSFSP.templates.render('wpjsfsp-condition-editor', data); 56 | }, 57 | group: function (args) { 58 | var data = $.extend(true, {}, { 59 | index: '', 60 | facets: [] 61 | }, args), 62 | i; 63 | 64 | data.facets = WPJSFSP.utils.object_to_array(data.facets); 65 | 66 | for (i = 0; data.facets.length > i; i++) { 67 | data.facets[i].index = i; 68 | data.facets[i].group = data.index; 69 | } 70 | 71 | return WPJSFSP.templates.render('wpjsfsp-condition-group', data); 72 | }, 73 | facet: function (args) { 74 | var data = $.extend(true, {}, { 75 | group: '', 76 | index: '', 77 | target: '', 78 | not_operand: false, 79 | settings: {} 80 | }, args); 81 | 82 | return WPJSFSP.templates.render('wpjsfsp-condition-facet', data); 83 | }, 84 | settings: function (args, values) { 85 | var fields = [], 86 | data = $.extend(true, {}, { 87 | index: '', 88 | group: '', 89 | target: null, 90 | fields: [] 91 | }, args); 92 | 93 | if (!data.fields.length && wpjsfsp_settings_editor.conditions[args.target] !== undefined) { 94 | data.fields = wpjsfsp_settings_editor.conditions[args.target].fields; 95 | } 96 | 97 | if (undefined === values) { 98 | values = {}; 99 | } 100 | 101 | // Replace the array with rendered fields. 102 | _.each(data.fields, function (field, fieldID) { 103 | 104 | field = WPJSFSP.models.field(field); 105 | 106 | if (typeof field.meta !== 'object') { 107 | field.meta = {}; 108 | } 109 | 110 | if (undefined !== values[fieldID]) { 111 | field.value = values[fieldID]; 112 | } 113 | 114 | field.name = 'wpjsfsp_settings[conditions][' + data.group + '][' + data.index + '][settings][' + fieldID + ']'; 115 | 116 | if (field.id === '') { 117 | field.id = 'conditions_' + data.group + '_' + data.index + '_settings_' + fieldID; 118 | } 119 | 120 | fields.push(WPJSFSP.templates.field(field)); 121 | }); 122 | 123 | // Render the section. 124 | return WPJSFSP.templates.section({ 125 | fields: fields 126 | }); 127 | }, 128 | selectbox: function (args) { 129 | var data = $.extend(true, {}, { 130 | id: null, 131 | name: null, 132 | type: 'select', 133 | group: '', 134 | index: '', 135 | value: null, 136 | select2: true, 137 | classes: ['facet-target', 'facet-select'], 138 | options: WPJSFSP.conditions.get_conditions() 139 | }, args); 140 | 141 | if (data.id === null) { 142 | data.id = 'conditions_' + data.group + '_' + data.index + '_target'; 143 | } 144 | 145 | if (data.name === null) { 146 | data.name = 'wpjsfsp_settings[conditions][' + data.group + '][' + data.index + '][target]'; 147 | } 148 | 149 | return WPJSFSP.templates.field(data); 150 | } 151 | }, 152 | groups: { 153 | add: function (editor, target, not_operand) { 154 | var $editor = $(editor), 155 | data = { 156 | index: $editor.find('.facet-group-wrap').length, 157 | facets: [ 158 | { 159 | target: target || null, 160 | not_operand: not_operand || false, 161 | settings: {} 162 | } 163 | ] 164 | }; 165 | 166 | 167 | $editor.find('.facet-groups').append(WPJSFSP.conditions.template.group(data)); 168 | $editor.addClass('has-conditions'); 169 | }, 170 | remove: function ($group) { 171 | var $editor = $group.parents('.facet-builder'); 172 | 173 | $group.prev('.facet-group-wrap').find('.and .add-facet').removeClass('disabled'); 174 | $group.remove(); 175 | 176 | WPJSFSP.conditions.renumber(); 177 | 178 | if ($editor.find('.facet-group-wrap').length === 0) { 179 | $editor.removeClass('has-conditions'); 180 | 181 | $('#wpjsfsp-first-condition') 182 | .val(null) 183 | .trigger('change'); 184 | } 185 | } 186 | }, 187 | facets: { 188 | add: function ($group, target, not_operand) { 189 | var data = { 190 | group: $group.data('index'), 191 | index: $group.find('.facet').length, 192 | target: target || null, 193 | not_operand: not_operand || false, 194 | settings: {} 195 | }; 196 | 197 | $group.find('.facet-list').append(WPJSFSP.conditions.template.facet(data)); 198 | }, 199 | remove: function ($facet) { 200 | var $group = $facet.parents('.facet-group-wrap'); 201 | 202 | $facet.remove(); 203 | 204 | if ($group.find('.facet').length === 0) { 205 | WPJSFSP.conditions.groups.remove($group); 206 | } else { 207 | WPJSFSP.conditions.renumber(); 208 | } 209 | } 210 | }, 211 | renumber: function () { 212 | $('.facet-builder .facet-group-wrap').each(function () { 213 | var $group = $(this), 214 | groupIndex = $group.parent().children().index($group); 215 | 216 | $group 217 | .data('index', groupIndex) 218 | .find('.facet').each(function () { 219 | var $facet = $(this), 220 | facetIndex = $facet.parent().children().index($facet); 221 | 222 | $facet 223 | .data('index', facetIndex) 224 | .find('[name]').each(function () { 225 | var replace_with = "wpjsfsp_settings[conditions][" + groupIndex + "][" + facetIndex + "]"; 226 | this.name = this.name.replace(/wpjsfsp_settings\[conditions\]\[\d*?\]\[\d*?\]/, replace_with); 227 | this.id = this.name; 228 | }); 229 | }); 230 | }); 231 | } 232 | }; 233 | 234 | // Import this module. 235 | window.WPJSFSP = window.WPJSFSP || {}; 236 | window.WPJSFSP.conditions = conditions; 237 | 238 | $(document) 239 | .on('wpjsfsp_init', function () { 240 | WPJSFSP.conditions.renumber(); 241 | WPJSFSP.conditions.toggle_not_operand(); 242 | }) 243 | .on('select2:select wpjsfselect2:select', '#wpjsfsp-first-condition', function (event) { 244 | var $field = $(this), 245 | $editor = $field.parents('.facet-builder').eq(0), 246 | target = $field.val(), 247 | $operand = $editor.find('#wpjsfsp-first-facet-operand'), 248 | not_operand = $operand.is(':checked'); 249 | 250 | WPJSFSP.conditions.groups.add($editor, target, not_operand); 251 | 252 | $field 253 | .val(null) 254 | .trigger('change'); 255 | 256 | $operand.prop('checked', false).parents('.facet-target').removeClass('not-operand-checked'); 257 | $(document).trigger('wpjsfsp_init'); 258 | }) 259 | .on('click', '.facet-builder .wpjsfsp-not-operand', function () { 260 | WPJSFSP.conditions.not_operand_checkbox($(this)); 261 | }) 262 | .on('change', '.facet-builder .facet-target select', function (event) { 263 | var $this = $(this), 264 | $facet = $this.parents('.facet'), 265 | target = $this.val(), 266 | data = { 267 | target: target 268 | }; 269 | 270 | if (target === '' || target === $facet.data('target')) { 271 | return; 272 | } 273 | 274 | $facet.data('target', target).find('.facet-settings').html(WPJSFSP.conditions.template.settings(data)); 275 | $(document).trigger('wpjsfsp_init'); 276 | }) 277 | .on('click', '.facet-builder .facet-group-wrap:last-child .and .add-facet', function () { 278 | WPJSFSP.conditions.groups.add($(this).parents('.facet-builder').eq(0)); 279 | $(document).trigger('wpjsfsp_init'); 280 | }) 281 | .on('click', '.facet-builder .add-or .add-facet:not(.disabled)', function () { 282 | WPJSFSP.conditions.facets.add($(this).parents('.facet-group-wrap').eq(0)); 283 | $(document).trigger('wpjsfsp_init'); 284 | }) 285 | .on('click', '.facet-builder .remove-facet', function () { 286 | WPJSFSP.conditions.facets.remove($(this).parents('.facet').eq(0)); 287 | $(document).trigger('wpjsfsp_init'); 288 | }); 289 | 290 | }(jQuery)); -------------------------------------------------------------------------------- /assets/js/src/admin/plugins/forms.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | (function ($) { 3 | 4 | var forms = { 5 | init: function () { 6 | forms.checkDependencies(); 7 | }, 8 | /** 9 | * dependencies should look like this: 10 | * 11 | * { 12 | * field_name_1: value, // Select, radio etc. 13 | * field_name_2: true // Checkbox 14 | * } 15 | * 16 | * Support for Multiple possible values of one field 17 | * 18 | * { 19 | * field_name_1: [ value_1, value_2 ] 20 | * } 21 | * 22 | */ 23 | checkDependencies: function ($dependent_fields) { 24 | var _fields = $($dependent_fields); 25 | 26 | // If no fields passed, only do those not already initialized. 27 | $dependent_fields = _fields.length ? _fields : $("[data-wpjsfsp-dependencies]:not([data-wpjsfsp-processed-dependencies])"); 28 | 29 | $dependent_fields.each(function () { 30 | var $dependent = $(this), 31 | dependentID = $dependent.data('id'), 32 | // The dependency object for this field. 33 | dependencies = $dependent.data("wpjsfsp-processed-dependencies") || {}, 34 | // Total number of fields this :input is dependent on. 35 | requiredCount = Object.keys(dependencies).length, 36 | // Current count of fields this :input matched properly. 37 | count = 0, 38 | // An array of fields this :input is dependent on. 39 | dependentFields = $dependent.data("wpjsfsp-dependent-fields"), 40 | // Early declarations. 41 | key; 42 | 43 | // Clean up & pre-process dependencies so we don't need to rebuild each time. 44 | if (!$dependent.data("wpjsfsp-processed-dependencies")) { 45 | dependencies = $dependent.data("wpjsfsp-dependencies"); 46 | if (typeof dependencies === 'string') { 47 | dependencies = JSON.parse(dependencies); 48 | } 49 | 50 | // Convert each key to an array of acceptable values. 51 | for (key in dependencies) { 52 | if (dependencies.hasOwnProperty(key)) { 53 | if (typeof dependencies[key] === "string") { 54 | // Leave boolean values alone as they are for checkboxes or checking if an input has any value. 55 | 56 | if (dependencies[key].indexOf(',') !== -1) { 57 | dependencies[key] = dependencies[key].split(','); 58 | } else { 59 | dependencies[key] = [dependencies[key]]; 60 | } 61 | } 62 | } 63 | } 64 | 65 | // Update cache & counts. 66 | requiredCount = Object.keys(dependencies).length; 67 | $dependent.data("wpjsfsp-processed-dependencies", dependencies).attr("data-wpjsfsp-processed-dependencies", dependencies); 68 | } 69 | 70 | if (!dependentFields) { 71 | dependentFields = $.map(dependencies, function (value, index) { 72 | var $wrapper = $('.wpjsfsp-field[data-id="' + index + '"]'); 73 | 74 | return $wrapper.length ? $wrapper.eq(0) : null; 75 | }); 76 | 77 | $dependent.data("wpjsfsp-dependent-fields", dependentFields); 78 | } 79 | 80 | $(dependentFields).each(function () { 81 | var $wrapper = $(this), 82 | $field = $wrapper.find(':input:first'), 83 | id = $wrapper.data("id"), 84 | value = $field.val(), 85 | required = dependencies[id], 86 | matched, 87 | // Used for limiting the fields that get updated when this field is changed. 88 | all_this_fields_dependents = $wrapper.data('wpjsfsp-field-dependents') || []; 89 | 90 | if (all_this_fields_dependents.indexOf(dependentID) === -1) { 91 | all_this_fields_dependents.push(dependentID); 92 | $wrapper.data('wpjsfsp-field-dependents', all_this_fields_dependents); 93 | } 94 | 95 | // If no required values found bail early. 96 | if (typeof required === 'undefined' || required === null) { 97 | $dependent.hide(); 98 | // Effectively breaks the .each for this $dependent and hides it. 99 | return false; 100 | } 101 | 102 | if ($wrapper.hasClass('wpjsfsp-field-radio')) { 103 | value = $wrapper.find(':input:checked').val(); 104 | } 105 | 106 | // Check if the value matches required values. 107 | if ($wrapper.hasClass('wpjsfsp-field-select') || $wrapper.hasClass('wpjsfsp-field-radio')) { 108 | matched = required && required.indexOf(value) !== -1; 109 | } else if ($wrapper.hasClass('wpjsfsp-field-checkbox')) { 110 | matched = required === $field.is(':checked'); 111 | } else { 112 | matched = Array.isArray(required) ? required.indexOf(value) !== -1 : required == value; 113 | } 114 | 115 | if (matched) { 116 | count++; 117 | } else { 118 | $dependent.hide(); 119 | // Effectively breaks the .each for this $dependent and hides it. 120 | return false; 121 | } 122 | 123 | if (count === requiredCount) { 124 | $dependent.show(); 125 | } 126 | }); 127 | }); 128 | }, 129 | form_check: function () { 130 | $(document).trigger('wpjsfsp_form_check'); 131 | }, 132 | is_field: function (data) { 133 | if (typeof data !== 'object') { 134 | return false; 135 | } 136 | 137 | var field_tests = [ 138 | data.id !== undefined, 139 | data.label !== undefined, 140 | data.type !== undefined, 141 | data.options !== undefined, 142 | data.desc !== undefined 143 | ]; 144 | 145 | return field_tests.indexOf(true) >= 0; 146 | }, 147 | parseFields: function (fields, values) { 148 | 149 | values = values || {}; 150 | 151 | _.each(fields, function (field, fieldID) { 152 | 153 | fields[fieldID] = WPJSFSP.models.field(field); 154 | 155 | if (typeof fields[fieldID].meta !== 'object') { 156 | fields[fieldID].meta = {}; 157 | } 158 | 159 | if (undefined !== values[fieldID]) { 160 | fields[fieldID].value = values[fieldID]; 161 | } 162 | 163 | if (fields[fieldID].id === '') { 164 | fields[fieldID].id = fieldID; 165 | } 166 | }); 167 | 168 | return fields; 169 | }, 170 | renderTab: function () { 171 | 172 | }, 173 | renderSection: function () { 174 | 175 | }, 176 | render: function (args, values, $container) { 177 | var form, 178 | sections = {}, 179 | section = [], 180 | form_fields = {}, 181 | data = $.extend(true, { 182 | id: "", 183 | tabs: {}, 184 | sections: {}, 185 | fields: {}, 186 | maintabs: {}, 187 | subtabs: {} 188 | }, args), 189 | maintabs = $.extend({ 190 | id: data.id, 191 | classes: [], 192 | tabs: {}, 193 | vertical: true, 194 | form: true, 195 | meta: { 196 | 'data-min-height': 250 197 | } 198 | }, data.maintabs), 199 | subtabs = $.extend({ 200 | classes: ['link-tabs', 'sub-tabs'], 201 | tabs: {} 202 | }, data.subtabs), 203 | container_classes = ['wpjsfsp-dynamic-form']; 204 | 205 | values = values || {}; 206 | 207 | if (Object.keys(data.tabs).length && Object.keys(data.sections).length) { 208 | container_classes.push('tabbed-content'); 209 | 210 | // Loop Tabs 211 | _.each(data.fields, function (subTabs, tabID) { 212 | 213 | // If not a valid tab or no subsections skip it. 214 | if (typeof subTabs !== 'object' || !Object.keys(subTabs).length) { 215 | return; 216 | } 217 | 218 | // Define this tab. 219 | if (undefined === maintabs.tabs[tabID]) { 220 | maintabs.tabs[tabID] = { 221 | label: data.tabs[tabID], 222 | content: '' 223 | }; 224 | } 225 | 226 | // Define the sub tabs model. 227 | subtabs = $.extend(subtabs, { 228 | id: data.id + '-' + tabID + '-subtabs', 229 | tabs: {} 230 | }); 231 | 232 | // Loop Tab Sections 233 | _.each(subTabs, function (subTabFields, subTabID) { 234 | 235 | // If not a valid subtab or no fields skip it. 236 | if (typeof subTabFields !== 'object' || !Object.keys(subTabFields).length) { 237 | return; 238 | } 239 | 240 | // Move single fields into the main subtab. 241 | if (forms.is_field(subTabFields)) { 242 | var newSubTabFields = {}; 243 | newSubTabFields[subTabID] = subTabFields; 244 | subTabID = 'main'; 245 | subTabFields = newSubTabFields; 246 | } 247 | 248 | // Define this subtab model. 249 | if (undefined === subtabs.tabs[subTabID]) { 250 | subtabs.tabs[subTabID] = { 251 | label: data.sections[tabID][subTabID], 252 | content: '' 253 | }; 254 | } 255 | 256 | subTabFields = forms.parseFields(subTabFields, values); 257 | 258 | // Loop Tab Section Fields 259 | _.each(subTabFields, function (field) { 260 | // Store the field by id for easy lookup later. 261 | form_fields[field.id] = field; 262 | 263 | // Push rendered fields into the subtab content. 264 | subtabs.tabs[subTabID].content += WPJSFSP.templates.field(field); 265 | }); 266 | 267 | // Remove any empty tabs. 268 | if ("" === subtabs.tabs[subTabID].content) { 269 | delete subtabs.tabs[subTabID]; 270 | } 271 | }); 272 | 273 | // If there are subtabs, then render them into the main tabs content, otherwise remove this main tab. 274 | if (Object.keys(subtabs.tabs).length) { 275 | maintabs.tabs[tabID].content = WPJSFSP.templates.tabs(subtabs); 276 | } else { 277 | delete maintabs.tabs[tabID]; 278 | } 279 | }); 280 | 281 | if (Object.keys(maintabs.tabs).length) { 282 | form = WPJSFSP.templates.tabs(maintabs); 283 | } 284 | } 285 | else if (Object.keys(data.tabs).length) { 286 | container_classes.push('tabbed-content'); 287 | 288 | // Loop Tabs 289 | _.each(data.fields, function (tabFields, tabID) { 290 | 291 | // If not a valid tab or no subsections skip it. 292 | if (typeof tabFields !== 'object' || !Object.keys(tabFields).length) { 293 | return; 294 | } 295 | 296 | // Define this tab. 297 | if (undefined === maintabs.tabs[tabID]) { 298 | maintabs.tabs[tabID] = { 299 | label: data.tabs[tabID], 300 | content: '' 301 | }; 302 | } 303 | 304 | section = []; 305 | 306 | tabFields = forms.parseFields(tabFields, values); 307 | 308 | // Loop Tab Fields 309 | _.each(tabFields, function (field) { 310 | // Store the field by id for easy lookup later. 311 | form_fields[field.id] = field; 312 | 313 | // Push rendered fields into the subtab content. 314 | section.push(WPJSFSP.templates.field(field)); 315 | }); 316 | 317 | // Push rendered tab into the tab. 318 | if (section.length) { 319 | // Push rendered sub tabs into the main tabs if not empty. 320 | maintabs.tabs[tabID].content = WPJSFSP.templates.section({ 321 | fields: section 322 | }); 323 | } else { 324 | delete (maintabs.tabs[tabID]); 325 | } 326 | }); 327 | 328 | if (Object.keys(maintabs.tabs).length) { 329 | form = WPJSFSP.templates.tabs(maintabs); 330 | } 331 | } 332 | else if (Object.keys(data.sections).length) { 333 | 334 | // Loop Sections 335 | _.each(data.fields, function (sectionFields, sectionID) { 336 | section = []; 337 | 338 | section.push(WPJSFSP.templates.field({ 339 | type: 'heading', 340 | desc: data.sections[sectionID] || '' 341 | })); 342 | 343 | sectionFields = forms.parseFields(sectionFields, values); 344 | 345 | // Loop Tab Section Fields 346 | _.each(sectionFields, function (field) { 347 | // Store the field by id for easy lookup later. 348 | form_fields[field.id] = field; 349 | 350 | // Push rendered fields into the section. 351 | section.push(WPJSFSP.templates.field(field)); 352 | }); 353 | 354 | // Push rendered sections into the form. 355 | form += WPJSFSP.templates.section({ 356 | fields: section 357 | }); 358 | }); 359 | } 360 | else { 361 | data.fields = forms.parseFields(data.fields, values); 362 | 363 | // Replace the array with rendered fields. 364 | _.each(data.fields, function (field) { 365 | // Store the field by id for easy lookup later. 366 | form_fields[field.id] = field; 367 | 368 | // Push rendered fields into the section. 369 | section.push(WPJSFSP.templates.field(field)); 370 | }); 371 | 372 | // Render the section. 373 | form = WPJSFSP.templates.section({ 374 | fields: section 375 | }); 376 | } 377 | 378 | if ($container !== undefined && $container.length) { 379 | $container 380 | .addClass(container_classes.join(' ')) 381 | .data('form_fields', form_fields) 382 | .html(form) 383 | .trigger('wpjsfsp_init'); 384 | } 385 | 386 | return form; 387 | 388 | } 389 | }; 390 | 391 | // Import this module. 392 | window.WPJSFSP = window.WPJSFSP || {}; 393 | window.WPJSFSP.forms = forms; 394 | 395 | $(document) 396 | .on('wpjsfsp_init wpjsfsp_form_check', function () { 397 | WPJSFSP.forms.init(); 398 | }) 399 | .on('wpjsfspFieldChanged', '.wpjsfsp-field', function () { 400 | var $wrapper = $(this), 401 | dependent_field_ids = $wrapper.data('wpjsfsp-field-dependents') || [], 402 | $dependent_fields = $(), 403 | i; 404 | 405 | if (!dependent_field_ids || dependent_field_ids.length <= 0) { 406 | return; 407 | } 408 | 409 | for (i = 0; i < dependent_field_ids.length; i++) { 410 | $dependent_fields = $dependent_fields.add('.wpjsfsp-field[data-id="' + dependent_field_ids[i] + '"]'); 411 | } 412 | 413 | WPJSFSP.forms.checkDependencies($dependent_fields); 414 | }) 415 | .on('wpjsfspFieldChanged', '.wpjsfsp-field-dynamic-desc', function () { 416 | var $this = $(this), 417 | $input = $this.find(':input'), 418 | $container = $this.parents('.wpjsfsp-dynamic-form:first'), 419 | val = $input.val(), 420 | form_fields = $container.data('form_fields') || {}, 421 | field = form_fields[$this.data('id')] || {}, 422 | $desc = $this.find('.wpjsfsp-desc'), 423 | desc = $this.data('wpjsfsp-dynamic-desc'); 424 | 425 | switch (field.type) { 426 | case 'radio': 427 | val = $this.find(':input:checked').val(); 428 | break; 429 | } 430 | 431 | field.value = val; 432 | 433 | if (desc && desc.length) { 434 | $desc.html(WPJSFSP.templates.renderInline(desc, field)); 435 | } 436 | }) 437 | .on('change', '.wpjsfsp-field-select select', function () { 438 | $(this).parents('.wpjsfsp-field').trigger('wpjsfspFieldChanged'); 439 | }) 440 | .on('click', '.wpjsfsp-field-checkbox input', function () { 441 | $(this).parents('.wpjsfsp-field').trigger('wpjsfspFieldChanged'); 442 | }) 443 | .on('click', '.wpjsfsp-field-radio input', function (event) { 444 | var $this = $(this), 445 | $selected = $this.parents('li'), 446 | $wrapper = $this.parents('.wpjsfsp-field'); 447 | 448 | $wrapper.trigger('wpjsfspFieldChanged'); 449 | 450 | $wrapper.find('li.wpjsfsp-selected').removeClass('wpjsfsp-selected'); 451 | 452 | $selected.addClass('wpjsfsp-selected'); 453 | }); 454 | 455 | }(jQuery)); -------------------------------------------------------------------------------- /assets/js/src/admin/plugins/js-wp-editor.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @copyright Copyright (c) 2017, Code Atlantic 3 | * @author Daniel Iser 4 | */ 5 | var wpActiveEditor = true; 6 | (function ($) { 7 | "use strict"; 8 | 9 | var wp_editor = function (options) { 10 | 11 | var default_options, 12 | id_regexp = new RegExp('wpjsfsp_id', 'g'); 13 | 14 | if (typeof tinyMCEPreInit === 'undefined' || typeof QTags === 'undefined' || typeof wpjsfsp_wpeditor_vars === 'undefined') { 15 | console.warn('js_wp_editor( $settings ); must be loaded'); 16 | return this; 17 | } 18 | 19 | default_options = { 20 | 'mode': 'html', 21 | 'mceInit': { 22 | "theme": "modern", 23 | "skin": "lightgray", 24 | "language": "en", 25 | "formats": { 26 | "alignleft": [ 27 | { 28 | "selector": "p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li", 29 | "styles": {"textAlign": "left"}, 30 | "deep": false, 31 | "remove": "none" 32 | }, 33 | { 34 | "selector": "img,table,dl.wp-caption", 35 | "classes": ["alignleft"], 36 | "deep": false, 37 | "remove": "none" 38 | } 39 | ], 40 | "aligncenter": [ 41 | { 42 | "selector": "p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li", 43 | "styles": {"textAlign": "center"}, 44 | "deep": false, 45 | "remove": "none" 46 | }, 47 | { 48 | "selector": "img,table,dl.wp-caption", 49 | "classes": ["aligncenter"], 50 | "deep": false, 51 | "remove": "none" 52 | } 53 | ], 54 | "alignright": [ 55 | { 56 | "selector": "p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li", 57 | "styles": {"textAlign": "right"}, 58 | "deep": false, 59 | "remove": "none" 60 | }, 61 | { 62 | "selector": "img,table,dl.wp-caption", 63 | "classes": ["alignright"], 64 | "deep": false, 65 | "remove": "none" 66 | } 67 | ], 68 | "strikethrough": {"inline": "del", "deep": true, "split": true} 69 | }, 70 | "relative_urls": false, 71 | "remove_script_host": false, 72 | "convert_urls": false, 73 | "browser_spellcheck": true, 74 | "fix_list_elements": true, 75 | "entities": "38,amp,60,lt,62,gt", 76 | "entity_encoding": "raw", 77 | "keep_styles": false, 78 | "paste_webkit_styles": "font-weight font-style color", 79 | "preview_styles": "font-family font-size font-weight font-style text-decoration text-transform", 80 | "wpeditimage_disable_captions": false, 81 | "wpeditimage_html5_captions": false, 82 | "plugins": "charmap,hr,media,paste,tabfocus,textcolor,fullscreen,wordpress,wpeditimage,wpgallery,wplink,wpdialogs,wpview,image", 83 | "content_css": wpjsfsp_wpeditor_vars.includes_url + "css/dashicons.css?ver=3.9," + wpjsfsp_wpeditor_vars.includes_url + "js/mediaelement/mediaelementplayer.min.css?ver=3.9," + wpjsfsp_wpeditor_vars.includes_url + "js/mediaelement/wp-mediaelement.css?ver=3.9," + wpjsfsp_wpeditor_vars.includes_url + "js/tinymce/skins/wordpress/wp-content.css?ver=3.9", 84 | "selector": "#wpjsfsp_id", 85 | "resize": "vertical", 86 | "menubar": false, 87 | "wpautop": true, 88 | "indent": false, 89 | "toolbar1": "bold,italic,strikethrough,bullist,numlist,blockquote,hr,alignleft,aligncenter,alignright,link,unlink,wp_more,spellchecker,fullscreen,wp_adv", 90 | "toolbar2": "formatselect,underline,alignjustify,forecolor,pastetext,removeformat,charmap,outdent,indent,undo,redo,wp_help", 91 | "toolbar3": "", 92 | "toolbar4": "", 93 | "tabfocus_elements": ":prev,:next", 94 | "body_class": "wpjsfsp_id" 95 | } 96 | }; 97 | 98 | if (tinyMCEPreInit.mceInit.wpjsfsp_id) { 99 | default_options.mceInit = tinyMCEPreInit.mceInit.wpjsfsp_id; 100 | } 101 | 102 | options = $.extend(true, {}, default_options, options); 103 | 104 | return this.each(function () { 105 | var $this = $(this), 106 | current_id = $this.attr('id'), 107 | temp = {}; 108 | 109 | if (tinyMCE.editors[current_id] !== undefined) { 110 | tinyMCE.remove(tinymce.editors[current_id]); 111 | } 112 | 113 | if (!$this.is('textarea')) { 114 | console.warn('Element must be a textarea'); 115 | if ($this.closest('.wp-editor-wrap').length) { 116 | temp.editor_wrap = $this.closest('.wp-editor-wrap'); 117 | temp.field_parent = temp.editor_wrap.parent(); 118 | 119 | temp.editor_wrap.before($this.clone()); 120 | temp.editor_wrap.remove(); 121 | 122 | $this = temp.field_parent.find('textarea[id="' + current_id + '"]'); 123 | } 124 | } 125 | $this.addClass('wp-editor-area').show(); 126 | 127 | 128 | $.each(options.mceInit, function (key, value) { 129 | if ($.type(value) === 'string') { 130 | options.mceInit[key] = value.replace(id_regexp, current_id); 131 | } 132 | }); 133 | 134 | options.mode = options.mode === 'tmce' ? 'tmce' : 'html'; 135 | 136 | tinyMCEPreInit.mceInit[current_id] = options.mceInit; 137 | 138 | var wrap = $('
'), 139 | editor_tools = $('
'), 140 | editor_tabs = $('
'), 141 | switch_editor_html = $('Text'), 142 | switch_editor_tmce = $('Visual'), 143 | media_buttons = $('
'), 144 | insert_media_button = $(' Add Media'), 145 | editor_container = $('
'), 146 | content_css = /*Object.prototype.hasOwnProperty.call(tinyMCEPreInit.mceInit[current_id], 'content_css') ? tinyMCEPreInit.mceInit[current_id]['content_css'].split(',') :*/ false; 147 | 148 | insert_media_button.appendTo(media_buttons); 149 | media_buttons.appendTo(editor_tools); 150 | 151 | switch_editor_html.appendTo(editor_tabs); 152 | switch_editor_tmce.appendTo(editor_tabs); 153 | editor_tabs.appendTo(editor_tools); 154 | 155 | editor_tools.appendTo(wrap); 156 | editor_container.appendTo(wrap); 157 | 158 | editor_container.append($this.clone().addClass('wp-editor-area')); 159 | 160 | if (content_css !== false) 161 | $.each(content_css, function () { 162 | if (!$('link[href="' + this + '"]').length) 163 | $this.before(''); 164 | }); 165 | 166 | $this.before(''); 167 | 168 | $this.before(wrap); 169 | $this.remove(); 170 | 171 | new QTags(current_id); 172 | QTags._buttonsInit(); 173 | switchEditors.go(current_id, options.mode); 174 | 175 | $('.insert-media', wrap).on('click', function (event) { 176 | var elem = $(event.currentTarget), 177 | options = { 178 | frame: 'post', 179 | state: 'insert', 180 | title: wp.media.view.l10n.addMedia, 181 | multiple: true 182 | }; 183 | 184 | event.preventDefault(); 185 | 186 | elem.blur(); 187 | 188 | if (elem.hasClass('gallery')) { 189 | options.state = 'gallery'; 190 | options.title = wp.media.view.l10n.createGalleryTitle; 191 | } 192 | 193 | wp.media.editor.open(current_id, options); 194 | }); 195 | 196 | }); 197 | }; 198 | 199 | // Import this module. 200 | window.WPJSFSP = window.WPJSFSP || {}; 201 | window.WPJSFSP.wp_editor = wp_editor; 202 | 203 | $.fn.wp_editor = WPJSFSP.wp_editor; 204 | 205 | $(document) 206 | .on('wpjsfsp_init', function () { 207 | 208 | $('.wpjsfsp-field-editor textarea:not(.initialized)').each(function () { 209 | var $this = $(this).addClass('initialized'); 210 | $this.wp_editor({ 211 | mode: 'tmce' 212 | }); 213 | }); 214 | }) 215 | .on('mousedown', '.wpjsfsp-submit button', function (event) { 216 | var $form = $(event.target).parents('form').eq(0); 217 | 218 | tinyMCE.triggerSave(); 219 | 220 | $form.trigger('wpjsfsp_before_submit'); 221 | }); 222 | 223 | }(jQuery)); -------------------------------------------------------------------------------- /assets/js/src/admin/plugins/links.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @copyright Copyright (c) 2017, Code Atlantic 3 | * @author Daniel Iser 4 | */ 5 | var wpActiveEditor = true; 6 | (function ($) { 7 | "use strict"; 8 | 9 | var current_link_field; 10 | 11 | // Import this module. 12 | window.WPJSFSP = window.WPJSFSP || {}; 13 | 14 | $(document) 15 | .on('click', '.wpjsfsp-field-link button', function (event) { 16 | var $input = $(this).next().select(), 17 | id = $input.attr('id'); 18 | 19 | current_link_field = $input; 20 | 21 | wpLink.open(id, $input.val(), ""); //open the link popup 22 | 23 | WPJSFSP.selectors('#wp-link-wrap').removeClass('has-text-field'); 24 | WPJSFSP.selectors('#wp-link-target').hide(); 25 | 26 | event.preventDefault(); 27 | event.stopPropagation(); 28 | return false; 29 | }) 30 | .on('click', '#wp-link-submit, #wp-link-cancel button, #wp-link-close', function (event) { 31 | var linkAtts = wpLink.getAttrs(); 32 | 33 | // If not for our fields then ignore it. 34 | if (current_link_field === undefined || !current_link_field) { 35 | return; 36 | } 37 | 38 | // If not the close buttons then its the save button. 39 | if (event.target.id === 'wp-link-submit') { 40 | current_link_field.val(linkAtts.href); 41 | } 42 | 43 | wpLink.textarea = current_link_field; 44 | wpLink.close(); 45 | 46 | // Clear the current_link_field 47 | current_link_field = false; 48 | 49 | //trap any other events 50 | event.preventDefault ? event.preventDefault() : event.returnValue = false; 51 | event.stopPropagation(); 52 | return false; 53 | }); 54 | }(jQuery)); -------------------------------------------------------------------------------- /assets/js/src/admin/plugins/modals.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @copyright Copyright (c) 2017, Code Atlantic 3 | * @author Daniel Iser 4 | */ 5 | (function ($) { 6 | "use strict"; 7 | 8 | var $html = $('html'), 9 | $document = $(document), 10 | $top_level_elements, 11 | focusableElementsString = "a[href], area[href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), iframe, object, embed, *[tabindex], *[contenteditable]", 12 | previouslyFocused, 13 | modals = { 14 | _current: null, 15 | // Accessibility: Checks focus events to ensure they stay inside the modal. 16 | forceFocus: function (event) { 17 | if (WPJSFSP.modals._current && !WPJSFSP.modals._current.contains(event.target)) { 18 | event.stopPropagation(); 19 | WPJSFSP.modals._current.focus(); 20 | } 21 | }, 22 | trapEscapeKey: function (e) { 23 | if (e.keyCode === 27) { 24 | WPJSFSP.modals.closeAll(); 25 | e.preventDefault(); 26 | } 27 | }, 28 | trapTabKey: function (e) { 29 | // if tab or shift-tab pressed 30 | if (e.keyCode === 9) { 31 | // get list of focusable items 32 | var focusableItems = WPJSFSP.modals._current.find('*').filter(focusableElementsString).filter(':visible'), 33 | // get currently focused item 34 | focusedItem = $(':focus'), 35 | // get the number of focusable items 36 | numberOfFocusableItems = focusableItems.length, 37 | // get the index of the currently focused item 38 | focusedItemIndex = focusableItems.index(focusedItem); 39 | 40 | if (e.shiftKey) { 41 | //back tab 42 | // if focused on first item and user preses back-tab, go to the last focusable item 43 | if (focusedItemIndex === 0) { 44 | focusableItems.get(numberOfFocusableItems - 1).focus(); 45 | e.preventDefault(); 46 | } 47 | } else { 48 | //forward tab 49 | // if focused on the last item and user preses tab, go to the first focusable item 50 | if (focusedItemIndex === numberOfFocusableItems - 1) { 51 | focusableItems.get(0).focus(); 52 | e.preventDefault(); 53 | } 54 | } 55 | } 56 | }, 57 | setFocusToFirstItem: function () { 58 | // set focus to first focusable item 59 | WPJSFSP.modals._current.find('.wpjsfsp-modal-content *').filter(focusableElementsString).filter(':visible').first().focus(); 60 | }, 61 | closeAll: function (callback) { 62 | $('.wpjsfsp-modal-background') 63 | .off('keydown.wpjsfsp_modal') 64 | .hide(0, function () { 65 | $('html').css({overflow: 'visible', width: 'auto'}); 66 | 67 | if ($top_level_elements) { 68 | $top_level_elements.attr('aria-hidden', 'false'); 69 | $top_level_elements = null; 70 | } 71 | 72 | // Accessibility: Focus back on the previously focused element. 73 | if (previouslyFocused.length) { 74 | previouslyFocused.focus(); 75 | } 76 | 77 | // Accessibility: Clears the WPJSFSP.modals._current var. 78 | WPJSFSP.modals._current = null; 79 | 80 | // Accessibility: Removes the force focus check. 81 | $document.off('focus.wpjsfsp_modal'); 82 | if (undefined !== callback) { 83 | callback(); 84 | } 85 | }) 86 | .attr('aria-hidden', 'true'); 87 | 88 | }, 89 | show: function (modal, callback) { 90 | $('.wpjsfsp-modal-background') 91 | .off('keydown.wpjsfsp_modal') 92 | .hide(0) 93 | .attr('aria-hidden', 'true'); 94 | 95 | $html 96 | .data('origwidth', $html.innerWidth()) 97 | .css({overflow: 'hidden', 'width': $html.innerWidth()}); 98 | 99 | // Accessibility: Sets the previous focus element. 100 | 101 | var $focused = $(':focus'); 102 | if (!$focused.parents('.wpjsfsp-modal-wrap').length) { 103 | previouslyFocused = $focused; 104 | } 105 | 106 | // Accessibility: Sets the current modal for focus checks. 107 | WPJSFSP.modals._current = $(modal); 108 | 109 | // Accessibility: Close on esc press. 110 | WPJSFSP.modals._current 111 | .on('keydown.wpjsfsp_modal', function (e) { 112 | WPJSFSP.modals.trapEscapeKey(e); 113 | WPJSFSP.modals.trapTabKey(e); 114 | }) 115 | .show(0, function () { 116 | $top_level_elements = $('body > *').filter(':visible').not(WPJSFSP.modals._current); 117 | $top_level_elements.attr('aria-hidden', 'true'); 118 | 119 | WPJSFSP.modals._current 120 | .trigger('wpjsfsp_init') 121 | // Accessibility: Add focus check that prevents tabbing outside of modal. 122 | .on('focus.wpjsfsp_modal', WPJSFSP.modals.forceFocus); 123 | 124 | // Accessibility: Focus on the modal. 125 | WPJSFSP.modals.setFocusToFirstItem(); 126 | 127 | if (undefined !== callback) { 128 | callback(); 129 | } 130 | }) 131 | .attr('aria-hidden', 'false'); 132 | 133 | }, 134 | remove: function (modal) { 135 | $(modal).remove(); 136 | }, 137 | replace: function (modal, replacement) { 138 | WPJSFSP.modals.remove($.trim(modal)); 139 | $('body').append($.trim(replacement)); 140 | }, 141 | reload: function (modal, replacement, callback) { 142 | WPJSFSP.modals.replace(modal, replacement); 143 | WPJSFSP.modals.show(modal, callback); 144 | $(modal).trigger('wpjsfsp_init'); 145 | } 146 | }; 147 | 148 | // Import this module. 149 | window.WPJSFSP = window.WPJSFSP || {}; 150 | window.WPJSFSP.modals = modals; 151 | 152 | $(document).on('click', '.wpjsfsp-modal-background, .wpjsfsp-modal-wrap .cancel, .wpjsfsp-modal-wrap .wpjsfsp-modal-close', function (e) { 153 | var $target = $(e.target); 154 | if (/*$target.hasClass('wpjsfsp-modal-background') || */$target.hasClass('cancel') || $target.hasClass('wpjsfsp-modal-close') || $target.hasClass('submitdelete')) { 155 | WPJSFSP.modals.closeAll(); 156 | e.preventDefault(); 157 | e.stopPropagation(); 158 | } 159 | }); 160 | 161 | }(jQuery)); -------------------------------------------------------------------------------- /assets/js/src/admin/plugins/models.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @copyright Copyright (c) 2017, Code Atlantic 3 | * @author Daniel Iser 4 | */ 5 | (function ($) { 6 | "use strict"; 7 | 8 | var models = { 9 | field: function (args) { 10 | return $.extend(true, {}, { 11 | type: 'text', 12 | id: '', 13 | id_prefix: '', 14 | name: '', 15 | label: null, 16 | placeholder: '', 17 | desc: null, 18 | dynamic_desc: null, 19 | size: 'regular', 20 | classes: [], 21 | dependencies: "", 22 | value: null, 23 | select2: false, 24 | multiple: false, 25 | as_array: false, 26 | options: [], 27 | object_type: null, 28 | object_key: null, 29 | std: null, 30 | min: 0, 31 | max: 50, 32 | step: 1, 33 | unit: 'px', 34 | units: {}, 35 | required: false, 36 | desc_position: 'bottom', 37 | meta: {} 38 | }, args); 39 | } 40 | }; 41 | 42 | // Import this module. 43 | window.WPJSFSP = window.WPJSFSP || {}; 44 | window.WPJSFSP.models = models; 45 | }(jQuery)); -------------------------------------------------------------------------------- /assets/js/src/admin/plugins/rangesliders.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @copyright Copyright (c) 2017, Code Atlantic 3 | * @author Daniel Iser 4 | */ 5 | (function ($) { 6 | 'use strict'; 7 | 8 | var rangesliders = { 9 | cloneables: { 10 | slider: $(''), 11 | plus: $(''), 12 | minus: $('') 13 | }, 14 | init: function () { 15 | $('.wpjsfsp-field-rangeslider:not(.wpjsfsp-rangeslider-initialized)').each(function () { 16 | var $this = $(this).addClass('wpjsfsp-rangeslider-initialized'), 17 | $input = $this.find('input.wpjsfsp-range-manual'), 18 | $slider = rangesliders.cloneables.slider.clone(), 19 | $plus = rangesliders.cloneables.plus.clone(), 20 | $minus = rangesliders.cloneables.minus.clone(), 21 | settings = { 22 | force: $input.data('force-minmax'), 23 | min: parseInt($input.attr('min'), 10) || 0, 24 | max: parseInt($input.attr('max'), 10) || 100, 25 | step: parseInt($input.attr('step'), 10) || 1, 26 | value: parseInt($input.attr('value'), 10) || 0 27 | }; 28 | 29 | if (settings.force && settings.value > settings.max) { 30 | settings.value = settings.max; 31 | $input.val(settings.value); 32 | } 33 | 34 | $slider.prop({ 35 | min: settings.min || 0, 36 | max: ( settings.force || (settings.max && settings.max > settings.value) ) ? settings.max : settings.value * 37 | 1.5, 38 | step: settings.step || settings.value * 1.5 / 100, 39 | value: settings.value 40 | }).on('change input', function () { 41 | $input.trigger('input'); 42 | }); 43 | 44 | $input.next().after($minus, $plus); 45 | $input.before($slider); 46 | 47 | }); 48 | } 49 | }; 50 | 51 | // Import this module. 52 | window.WPJSFSP = window.WPJSFSP || {}; 53 | window.WPJSFSP.rangesliders = rangesliders; 54 | 55 | $(document) 56 | .on('wpjsfsp_init', WPJSFSP.rangesliders.init) 57 | /** 58 | * Updates the input field when the slider is used. 59 | */ 60 | .on('input', '.wpjsfsp-field-rangeslider.wpjsfsp-rangeslider-initialized .wpjsfsp-range-slider', function () { 61 | var $slider = $(this); 62 | $slider.siblings('.wpjsfsp-range-manual').val($slider.val()); 63 | }) 64 | /** 65 | * Update sliders value, min, & max when manual entry is detected. 66 | */ 67 | .on('change', '.wpjsfsp-range-manual', function () { 68 | var $input = $(this), 69 | max = parseInt($input.prop('max'), 0), 70 | min = parseInt($input.prop('min'), 0), 71 | step = parseInt($input.prop('step'), 0), 72 | force = $input.data('force-minmax'), 73 | value = parseInt($input.val(), 0), 74 | $slider = $input.prev(); 75 | 76 | if (isNaN(value)) { 77 | value = $slider.val(); 78 | } 79 | 80 | if (force && value > max) { 81 | value = max; 82 | } else if (force && value < min) { 83 | value = min; 84 | } 85 | 86 | $input.val(value).trigger('input'); 87 | 88 | $slider.prop({ 89 | 'max': force || (max && max > value) ? max : value * 1.5, 90 | 'step': step || value * 1.5 / 100, 91 | 'value': value 92 | }); 93 | }) 94 | .on('click', '.wpjsfsp-range-plus', function (event) { 95 | var $input = $(this).siblings('.wpjsfsp-range-manual'), 96 | max = parseInt($input.prop('max'), 0), 97 | step = parseInt($input.prop('step'), 0), 98 | force = $input.data('force-minmax'), 99 | value = parseInt($input.val(), 0), 100 | $slider = $input.prev(); 101 | 102 | event.preventDefault(); 103 | 104 | value += step; 105 | 106 | if (isNaN(value)) { 107 | value = $slider.val(); 108 | } 109 | 110 | if (force && value > max) { 111 | value = max; 112 | } 113 | 114 | $input.val(value).trigger('input'); 115 | $slider.val(value); 116 | }) 117 | .on('click', '.wpjsfsp-range-minus', function (event) { 118 | var $input = $(this).siblings('.wpjsfsp-range-manual'), 119 | min = parseInt($input.prop('min'), 0), 120 | step = parseInt($input.prop('step'), 0), 121 | force = $input.data('force-minmax'), 122 | value = parseInt($input.val(), 0), 123 | $slider = $input.prev(); 124 | 125 | event.preventDefault(); 126 | 127 | value -= step; 128 | 129 | if (isNaN(value)) { 130 | value = $slider.val(); 131 | } 132 | 133 | if (force && value < min) { 134 | value = min; 135 | } 136 | 137 | $input.val(value).trigger('input'); 138 | $slider.val(value); 139 | }); 140 | 141 | }(jQuery, document)); -------------------------------------------------------------------------------- /assets/js/src/admin/plugins/select2.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @copyright Copyright (c) 2017, Code Atlantic 3 | * @author Daniel Iser 4 | */ 5 | (function ($) { 6 | "use strict"; 7 | 8 | var select2 = { 9 | init: function () { 10 | $('.wpjsfsp-field-select2 select').filter(':not(.select2-initialized)').each(function () { 11 | 12 | var $this = $(this), 13 | current = $this.data('current') || $this.find('option[selected="selected"]').attr('value'), 14 | object_type = $this.data('objecttype'), 15 | object_key = $this.data('objectkey'), 16 | options = { 17 | width: '100%', 18 | multiple: false, 19 | dropdownParent: $this.parent() 20 | }; 21 | 22 | if ($this.attr('multiple')) { 23 | options.multiple = true; 24 | } 25 | 26 | if (object_type && object_key) { 27 | options = $.extend(options, { 28 | ajax: { 29 | url: ajaxurl, 30 | dataType: 'json', 31 | delay: 250, 32 | data: function (params) { 33 | return { 34 | s: params.term, // search term 35 | page: params.page, 36 | action: "wpjsfsp_object_search", 37 | object_type: object_type, 38 | object_key: object_key 39 | }; 40 | }, 41 | processResults: function (data, params) { 42 | // parse the results into the format expected by Select2 43 | // since we are using custom formatting functions we do not need to 44 | // alter the remote JSON data, except to indicate that infinite 45 | // scrolling can be used 46 | params.page = params.page || 1; 47 | 48 | return { 49 | results: data.items, 50 | pagination: { 51 | more: (params.page * 10) < data.total_count 52 | } 53 | }; 54 | }, 55 | cache: true 56 | }, 57 | cache: true, 58 | escapeMarkup: function (markup) { 59 | return markup; 60 | }, // let our custom formatter work 61 | maximumInputLength: 20, 62 | closeOnSelect: !options.multiple, 63 | templateResult: WPJSFSP.select2.formatObject, 64 | templateSelection: WPJSFSP.select2.formatObjectSelection 65 | }); 66 | } 67 | 68 | 69 | $this 70 | .addClass('select2-initialized') 71 | .wpjsfselect2(options); 72 | 73 | if (current !== null && current !== undefined) { 74 | 75 | if (options.multiple && 'object' !== typeof current && current !== "") { 76 | current = [current]; 77 | } else if (!options.multiple && current === '') { 78 | current = null; 79 | } 80 | } else { 81 | current = null; 82 | } 83 | 84 | if (object_type && object_key && current !== null && (typeof current === 'number' || current.length)) { 85 | $.ajax({ 86 | url: ajaxurl, 87 | data: { 88 | action: "wpjsfsp_object_search", 89 | object_type: object_type, 90 | object_key: object_key, 91 | include: current && current.length ? ((typeof current === 'string' || typeof current === 'number') ? current : [current]) : null 92 | }, 93 | dataType: "json", 94 | success: function (data) { 95 | $.each(data.items, function (key, item) { 96 | // Add any option that doesn't already exist 97 | if (!$this.find('option[value="' + item.id + '"]').length) { 98 | $this.prepend(''); 99 | } 100 | }); 101 | // Update the options 102 | $this.val(current).trigger('change'); 103 | } 104 | }); 105 | } else if (current && ((options.multiple && current.length) || (!options.multiple && current !== ""))) { 106 | $this.val(current).trigger('change'); 107 | } else if (current === null) { 108 | $this.val(current).trigger('change'); 109 | } 110 | }); 111 | }, 112 | formatObject: function (object) { 113 | return object.text; 114 | }, 115 | formatObjectSelection: function (object) { 116 | return object.text || object.text; 117 | } 118 | }; 119 | 120 | // Import this module. 121 | window.WPJSFSP = window.WPJSFSP || {}; 122 | window.WPJSFSP.select2 = select2; 123 | 124 | $(document) 125 | .on('wpjsfsp_init', function () { 126 | WPJSFSP.select2.init(); 127 | }); 128 | }(jQuery)); -------------------------------------------------------------------------------- /assets/js/src/admin/plugins/selectors.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @copyright Copyright (c) 2017, Code Atlantic 3 | * @author Daniel Iser 4 | */ 5 | (function ($) { 6 | "use strict"; 7 | 8 | function Selector_Cache() { 9 | var elementCache = {}; 10 | 11 | var get_from_cache = function (selector, $ctxt, reset) { 12 | 13 | if ('boolean' === typeof $ctxt) { 14 | reset = $ctxt; 15 | $ctxt = false; 16 | } 17 | var cacheKey = $ctxt ? $ctxt.selector + ' ' + selector : selector; 18 | 19 | if (undefined === elementCache[cacheKey] || reset) { 20 | elementCache[cacheKey] = $ctxt ? $ctxt.find(selector) : jQuery(selector); 21 | } 22 | 23 | return elementCache[cacheKey]; 24 | }; 25 | 26 | get_from_cache.elementCache = elementCache; 27 | return get_from_cache; 28 | } 29 | 30 | var selectors = new Selector_Cache(); 31 | 32 | // Import this module. 33 | window.WPJSFSP = window.WPJSFSP || {}; 34 | window.WPJSFSP.selectors = selectors; 35 | }(jQuery)); -------------------------------------------------------------------------------- /assets/js/src/admin/plugins/serialize-object.js: -------------------------------------------------------------------------------- 1 | /** 2 | * jQuery serializeObject 3 | * @copyright 2014, macek 4 | * @link https://github.com/macek/jquery-serialize-object 5 | * @license BSD 6 | * @version 2.5.0 7 | */ 8 | (function (root, factory) { 9 | 10 | // AMD 11 | if (typeof define === "function" && define.amd) { 12 | define(["exports", "jquery"], function (exports, $) { 13 | return factory(exports, $); 14 | }); 15 | } 16 | 17 | // CommonJS 18 | else if (typeof exports !== "undefined") { 19 | var $ = require("jquery"); 20 | factory(exports, $); 21 | } 22 | 23 | // Browser 24 | else { 25 | factory(root, (root.jQuery || root.Zepto || root.ender || root.$)); 26 | } 27 | 28 | }(this, function (exports, $) { 29 | 30 | var patterns = { 31 | validate: /^[a-z_][a-z0-9_]*(?:\[(?:\d*|[a-z0-9_]+)\])*$/i, 32 | key: /[a-z0-9_]+|(?=\[\])/gi, 33 | push: /^$/, 34 | fixed: /^\d+$/, 35 | named: /^[a-z0-9_]+$/i 36 | }; 37 | 38 | function FormSerializer(helper, $form) { 39 | 40 | // private variables 41 | var data = {}, 42 | pushes = {}; 43 | 44 | // private API 45 | function build(base, key, value) { 46 | base[key] = value; 47 | return base; 48 | } 49 | 50 | function makeObject(root, value) { 51 | 52 | var keys = root.match(patterns.key), k; 53 | 54 | try { 55 | value = JSON.parse(value); 56 | } catch (Error) { 57 | } 58 | 59 | // nest, nest, ..., nest 60 | while ((k = keys.pop()) !== undefined) { 61 | // foo[] 62 | if (patterns.push.test(k)) { 63 | var idx = incrementPush(root.replace(/\[\]$/, '')); 64 | value = build([], idx, value); 65 | } 66 | 67 | // foo[n] 68 | else if (patterns.fixed.test(k)) { 69 | value = build([], k, value); 70 | } 71 | 72 | // foo; foo[bar] 73 | else if (patterns.named.test(k)) { 74 | value = build({}, k, value); 75 | } 76 | } 77 | 78 | return value; 79 | } 80 | 81 | function incrementPush(key) { 82 | if (pushes[key] === undefined) { 83 | pushes[key] = 0; 84 | } 85 | return pushes[key]++; 86 | } 87 | 88 | function encode(pair) { 89 | switch ($('[name="' + pair.name + '"]', $form).attr("type")) { 90 | case "checkbox": 91 | return pair.value === "on" ? true : pair.value; 92 | default: 93 | return pair.value; 94 | } 95 | } 96 | 97 | function addPair(pair) { 98 | if (!patterns.validate.test(pair.name)) return this; 99 | var obj = makeObject(pair.name, encode(pair)); 100 | 101 | data = helper.extend(true, data, obj); 102 | return this; 103 | } 104 | 105 | function addPairs(pairs) { 106 | if (!helper.isArray(pairs)) { 107 | throw new Error("formSerializer.addPairs expects an Array"); 108 | } 109 | for (var i = 0, len = pairs.length; i < len; i++) { 110 | this.addPair(pairs[i]); 111 | } 112 | return this; 113 | } 114 | 115 | function serialize() { 116 | return data; 117 | } 118 | 119 | function serializeJSON() { 120 | return JSON.stringify(serialize()); 121 | } 122 | 123 | // public API 124 | this.addPair = addPair; 125 | this.addPairs = addPairs; 126 | this.serialize = serialize; 127 | this.serializeJSON = serializeJSON; 128 | } 129 | 130 | FormSerializer.patterns = patterns; 131 | 132 | FormSerializer.serializeObject = function serializeObject() { 133 | var serialized; 134 | 135 | if (this.is('form')) { 136 | serialized = this.serializeArray(); 137 | } else { 138 | serialized = this.find(':input').serializeArray(); 139 | } 140 | 141 | return new FormSerializer($, this) 142 | .addPairs(serialized) 143 | .serialize(); 144 | }; 145 | 146 | FormSerializer.serializeJSON = function serializeJSON() { 147 | var serialized; 148 | 149 | if (this.is('form')) { 150 | serialized = this.serializeArray(); 151 | } else { 152 | serialized = this.find(':input').serializeArray(); 153 | } 154 | 155 | return new FormSerializer($, this) 156 | .addPairs(serialized) 157 | .serializeJSON(); 158 | }; 159 | 160 | if (typeof $.fn !== "undefined") { 161 | $.fn.serializeObject = FormSerializer.serializeObject; 162 | $.fn.serializeJSON = FormSerializer.serializeJSON; 163 | } 164 | 165 | exports.FormSerializer = FormSerializer; 166 | 167 | return FormSerializer; 168 | })); -------------------------------------------------------------------------------- /assets/js/src/admin/plugins/settings.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @copyright Copyright (c) 2017, Code Atlantic 3 | * @author Daniel Iser 4 | */ 5 | (function ($) { 6 | "use strict"; 7 | window.wpjsfsp_settings_editor = window.wpjsfsp_settings_editor || { 8 | form_args: {}, 9 | current_values: {} 10 | }; 11 | 12 | $(document) 13 | .ready(function () { 14 | var $container = $('#wpjsfsp-settings-container'), 15 | args = wpjsfsp_settings_editor.form_args || {}, 16 | values = wpjsfsp_settings_editor.current_values || {}; 17 | 18 | if ($container.length) { 19 | WPJSFSP.forms.render(args, values, $container); 20 | } 21 | }); 22 | }(jQuery)); -------------------------------------------------------------------------------- /assets/js/src/admin/plugins/tabs.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @copyright Copyright (c) 2017, Code Atlantic 3 | * @author Daniel Iser 4 | */ 5 | (function ($) { 6 | "use strict"; 7 | var tabs = { 8 | init: function () { 9 | $('.wpjsfsp-tabs-container').filter(':not(.wpjsfsp-tabs-initialized)').each(function () { 10 | var $this = $(this).addClass('wpjsfsp-tabs-initialized'), 11 | $tabList = $this.find('> ul.tabs'), 12 | $firstTab = $tabList.find('> li:first'), 13 | forceMinHeight = $this.data('min-height'); 14 | 15 | if ($this.hasClass('vertical-tabs')) { 16 | var minHeight = forceMinHeight && forceMinHeight > 0 ? forceMinHeight : $tabList.eq(0).outerHeight(true); 17 | 18 | $this.css({ 19 | minHeight: minHeight + 'px' 20 | }); 21 | 22 | if ($this.parent().innerHeight < minHeight) { 23 | $this.parent().css({ 24 | minHeight: minHeight + 'px' 25 | }); 26 | } 27 | } 28 | 29 | // Trigger first tab. 30 | $firstTab.trigger('click'); 31 | }); 32 | } 33 | }; 34 | 35 | // Import this module. 36 | window.WPJSFSP = window.WPJSFSP || {}; 37 | window.WPJSFSP.tabs = tabs; 38 | 39 | $(document) 40 | .on('wpjsfsp_init', function () { 41 | WPJSFSP.tabs.init(); 42 | }) 43 | .on('click', '.wpjsfsp-tabs-initialized li.tab', function (e) { 44 | var $this = $(this), 45 | $container = $this.parents('.wpjsfsp-tabs-container:first'), 46 | $tabs = $container.find('> ul.tabs > li.tab'), 47 | $tab_contents = $container.find('> div.tab-content'), 48 | link = $this.find('a').attr('href'); 49 | 50 | $tabs.removeClass('active'); 51 | $tab_contents.removeClass('active'); 52 | 53 | $this.addClass('active'); 54 | $container.find('> div.tab-content' + link).addClass('active'); 55 | 56 | e.preventDefault(); 57 | }); 58 | }(jQuery)); -------------------------------------------------------------------------------- /assets/js/src/admin/plugins/templates.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @copyright Copyright (c) 2017, Code Atlantic 3 | * @author Daniel Iser 4 | */ 5 | (function ($) { 6 | "use strict"; 7 | var templates = { 8 | render: function (template, data) { 9 | var _template = wp.template(template); 10 | 11 | data = data || {}; 12 | 13 | if (data.classes !== undefined && Array.isArray(data.classes)) { 14 | data.classes = data.classes.join(' '); 15 | } 16 | 17 | // Prepare the meta data for templates. 18 | data = WPJSFSP.templates.prepareMeta(data); 19 | 20 | return _template(data); 21 | }, 22 | renderInline: function (content, data) { 23 | var options = { 24 | evaluate: /<#([\s\S]+?)#>/g, 25 | interpolate: /\{\{\{([\s\S]+?)\}\}\}/g, 26 | escape: /\{\{([^\}]+?)\}\}(?!\})/g, 27 | variable: 'data' 28 | }, 29 | template = _.template(content, null, options); 30 | 31 | return template(data); 32 | }, 33 | shortcode: function (args) { 34 | var data = $.extend(true, {}, { 35 | tag: '', 36 | meta: {}, 37 | has_content: false, 38 | content: '' 39 | }, args), 40 | template = data.has_content ? 'wpjsfsp-shortcode-w-content' : 'wpjsfsp-shortcode'; 41 | 42 | return WPJSFSP.templates.render(template, data); 43 | }, 44 | modal: function (args) { 45 | var data = $.extend(true, {}, { 46 | id: '', 47 | title: '', 48 | description: '', 49 | classes: '', 50 | save_button: window.WPJSFSP.I10n.save, 51 | cancel_button: window.WPJSFSP.I10n.cancel, 52 | content: '' 53 | }, args); 54 | 55 | return WPJSFSP.templates.render('wpjsfsp-modal', data); 56 | }, 57 | tabs: function (data) { 58 | data = $.extend(true, {}, { 59 | id: '', 60 | vertical: false, 61 | form: false, 62 | classes: [], 63 | tabs: {}, 64 | meta: {} 65 | }, data); 66 | 67 | if (typeof data.classes === 'string') { 68 | data.classes = [data.classes]; 69 | } 70 | 71 | if (data.form) { 72 | data.classes.push('wpjsfsp-tabbed-form'); 73 | } 74 | 75 | data.meta['data-tab-count'] = Object.keys(data.tabs).length; 76 | 77 | data.classes.push(data.vertical ? 'vertical-tabs' : 'horizontal-tabs'); 78 | 79 | data.classes = data.classes.join(' '); 80 | 81 | return WPJSFSP.templates.render('wpjsfsp-tabs', data); 82 | }, 83 | section: function (args) { 84 | var data = $.extend(true, {}, { 85 | classes: [], 86 | fields: [] 87 | }, args); 88 | 89 | 90 | return WPJSFSP.templates.render('wpjsfsp-field-section', data); 91 | }, 92 | fieldArgs: function (args) { 93 | var options = [], 94 | data = $.extend(true, {}, WPJSFSP.models.field(args)); 95 | 96 | if (!data.value && args.std !== undefined) { 97 | data.value = args.std; 98 | } 99 | 100 | if ('string' === typeof data.classes) { 101 | data.classes = data.classes.split(' '); 102 | } 103 | 104 | if (args.class !== undefined) { 105 | data.classes.push(args.class); 106 | } 107 | 108 | if (args.dependencies !== undefined && typeof args.dependencies === 'object') { 109 | data.dependencies = JSON.stringify(args.dependencies); 110 | } 111 | 112 | if (data.required) { 113 | data.meta.required = true; 114 | data.classes.push('wpjsfsp-required'); 115 | } 116 | 117 | if (typeof data.dynamic_desc === 'string' && data.dynamic_desc.length) { 118 | data.classes.push('wpjsfsp-field-dynamic-desc'); 119 | data.desc = WPJSFSP.templates.renderInline(data.dynamic_desc, data); 120 | } 121 | 122 | switch (args.type) { 123 | case 'select': 124 | case 'objectselect': 125 | case 'postselect': 126 | case 'taxonomyselect': 127 | if (data.options !== undefined) { 128 | _.each(data.options, function (label, value) { 129 | var selected = false, 130 | optgroup, 131 | optgroup_options; 132 | 133 | // Check if the label is an object. If so this is a optgroup and the label is sub options array. 134 | // NOTE: The value in the case its an optgroup is the optgroup label. 135 | if (typeof label !== 'object') { 136 | 137 | if (data.value !== null) { 138 | if (data.multiple && ((typeof data.value === 'object' && Object.keys(data.value).length && data.value[value] !== undefined) || (Array.isArray(data.value) && data.value.indexOf(value) !== -1))) { 139 | selected = 'selected'; 140 | } else if (!data.multiple && data.value == value) { 141 | selected = 'selected'; 142 | } 143 | } 144 | 145 | options.push( 146 | WPJSFSP.templates.prepareMeta({ 147 | label: label, 148 | value: value, 149 | meta: { 150 | selected: selected 151 | } 152 | }) 153 | ); 154 | 155 | } else { 156 | // Process Option Groups 157 | 158 | // Swap label & value due to group labels being used as keys. 159 | optgroup = value; 160 | optgroup_options = []; 161 | 162 | _.each(label, function (label, value) { 163 | var selected = false; 164 | 165 | if (data.value !== null) { 166 | if (data.multiple && ((typeof data.value === 'object' && Object.keys(data.value).length && data.value[value] !== undefined) || (Array.isArray(data.value) && data.value.indexOf(value) !== -1))) { 167 | selected = 'selected'; 168 | } else if (!data.multiple && data.value == value) { 169 | selected = 'selected'; 170 | } 171 | } 172 | optgroup_options.push( 173 | WPJSFSP.templates.prepareMeta({ 174 | label: label, 175 | value: value, 176 | meta: { 177 | selected: selected 178 | } 179 | }) 180 | ); 181 | 182 | }); 183 | 184 | options.push({ 185 | label: optgroup, 186 | options: optgroup_options 187 | }); 188 | 189 | } 190 | 191 | }); 192 | 193 | data.options = options; 194 | 195 | } 196 | 197 | if (data.multiple) { 198 | 199 | data.meta.multiple = true; 200 | 201 | if (data.as_array) { 202 | data.name += '[]'; 203 | } 204 | 205 | if (!data.value || !data.value.length) { 206 | data.value = []; 207 | } 208 | 209 | if (typeof data.value === 'string') { 210 | data.value = [data.value]; 211 | } 212 | 213 | } 214 | 215 | if (args.type !== 'select') { 216 | data.select2 = true; 217 | data.classes.push('wpjsfsp-field-objectselect'); 218 | data.classes.push(args.type === 'postselect' ? 'wpjsfsp-field-postselect' : 'wpjsfsp-field-taxonomyselect'); 219 | data.meta['data-objecttype'] = args.type === 'postselect' ? 'post_type' : 'taxonomy'; 220 | data.meta['data-objectkey'] = args.type === 'postselect' ? args.post_type : args.taxonomy; 221 | data.meta['data-current'] = typeof data.value === 'object' || Array.isArray(data.value) ? JSON.stringify(data.value) : data.value; 222 | } 223 | 224 | if (data.select2) { 225 | data.classes.push('wpjsfsp-field-select2'); 226 | 227 | if (data.placeholder) { 228 | data.meta['data-placeholder'] = data.placeholder; 229 | } 230 | } 231 | 232 | break; 233 | case 'radio': 234 | if (data.options !== undefined) { 235 | _.each(data.options, function (label, value) { 236 | 237 | options.push( 238 | WPJSFSP.templates.prepareMeta({ 239 | label: label, 240 | value: value, 241 | meta: { 242 | checked: data.value === value 243 | } 244 | }) 245 | ); 246 | 247 | }); 248 | 249 | data.options = options; 250 | } 251 | break; 252 | case 'multicheck': 253 | if (data.options !== undefined) { 254 | 255 | if (!data.value) { 256 | data.value = []; 257 | } 258 | 259 | if (data.as_array) { 260 | data.name += '[]'; 261 | } 262 | 263 | _.each(data.options, function (label, value) { 264 | 265 | options.push( 266 | WPJSFSP.templates.prepareMeta({ 267 | label: label, 268 | value: value, 269 | meta: { 270 | checked: (typeof data.value === 'object' && data.value[value] !== undefined) || (typeof data.value === 'array' && data.value.indexOf(value) >= 0) 271 | } 272 | }) 273 | ); 274 | 275 | }); 276 | 277 | data.options = options; 278 | } 279 | break; 280 | case 'checkbox': 281 | if (parseInt(data.value, 10) === 1) { 282 | data.meta.checked = true; 283 | } 284 | break; 285 | case 'rangeslider': 286 | // data.meta.readonly = true; 287 | data.meta.step = data.step; 288 | data.meta.min = data.min; 289 | data.meta.max = data.max; 290 | break; 291 | case 'textarea': 292 | data.meta.cols = data.cols; 293 | data.meta.rows = data.rows; 294 | break; 295 | case 'measure': 296 | if (typeof data.value === 'string' && data.value !== '') { 297 | data.number = parseInt(data.value); 298 | data.unitValue = data.value.replace(data.number, ""); 299 | data.value = data.number; 300 | } else { 301 | data.unitValue = null; 302 | } 303 | 304 | if (data.units !== undefined) { 305 | _.each(data.units, function (label, value) { 306 | var selected = false; 307 | 308 | if (data.unitValue == value) { 309 | selected = 'selected'; 310 | } 311 | 312 | options.push( 313 | WPJSFSP.templates.prepareMeta({ 314 | label: label, 315 | value: value, 316 | meta: { 317 | selected: selected 318 | } 319 | }) 320 | ); 321 | 322 | }); 323 | 324 | data.units = options; 325 | } 326 | break; 327 | case 'license_key': 328 | 329 | data.value = $.extend({ 330 | key: '', 331 | license: {}, 332 | messages: [], 333 | status: 'empty', 334 | expires: false, 335 | classes: false 336 | }, data.value); 337 | 338 | data.classes.push('wpjsfsp-license-' + data.value.status + '-notice'); 339 | 340 | if (data.value.classes) { 341 | data.classes.push(data.value.classes); 342 | } 343 | break; 344 | } 345 | 346 | return data; 347 | }, 348 | field: function (args) { 349 | var fieldTemplate, 350 | data = WPJSFSP.templates.fieldArgs(args); 351 | 352 | fieldTemplate = 'wpjsfsp-field-' + data.type; 353 | 354 | if (!$('#tmpl-' + fieldTemplate).length) { 355 | if (data.type === 'objectselfect' || data.type === 'postselect' || data.type === 'taxonomyselect') { 356 | fieldTemplate = 'wpjsfsp-field-select'; 357 | } 358 | if (!$('#tmpl-' + fieldTemplate).length) { 359 | return ''; 360 | } 361 | } 362 | 363 | data.field = WPJSFSP.templates.render(fieldTemplate, data); 364 | 365 | return WPJSFSP.templates.render('wpjsfsp-field-wrapper', data); 366 | }, 367 | prepareMeta: function (data) { 368 | // Convert meta JSON to attribute string. 369 | var _meta = [], 370 | key; 371 | 372 | for (key in data.meta) { 373 | if (data.meta.hasOwnProperty(key)) { 374 | // Boolean attributes can only require attribute key, not value. 375 | if ('boolean' === typeof data.meta[key]) { 376 | // Only set truthy boolean attributes. 377 | if (data.meta[key]) { 378 | _meta.push(_.escape(key)); 379 | } 380 | } else { 381 | _meta.push(_.escape(key) + '="' + _.escape(data.meta[key]) + '"'); 382 | } 383 | } 384 | } 385 | 386 | data.meta = _meta.join(' '); 387 | return data; 388 | } 389 | }; 390 | 391 | // Import this module. 392 | window.WPJSFSP = window.WPJSFSP || {}; 393 | window.WPJSFSP.templates = templates; 394 | }(jQuery)); -------------------------------------------------------------------------------- /assets/js/src/admin/plugins/utils.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @copyright Copyright (c) 2017, Code Atlantic 3 | * @author Daniel Iser 4 | */ 5 | (function ($) { 6 | "use strict"; 7 | 8 | String.prototype.capitalize = function () { 9 | return this.charAt(0).toUpperCase() + this.slice(1); 10 | }; 11 | 12 | var root = this, 13 | inputTypes = 'color,date,datetime,datetime-local,email,hidden,month,number,password,range,search,tel,text,time,url,week'.split(','), 14 | inputNodes = 'select,textarea'.split(','), 15 | rName = /\[([^\]]*)\]/g; 16 | 17 | // ugly hack for IE7-8 18 | function isInArray(array, needle) { 19 | return $.inArray(needle, array) !== -1; 20 | } 21 | 22 | function storeValue(container, parsedName, value) { 23 | 24 | var part = parsedName[0]; 25 | 26 | if (parsedName.length > 1) { 27 | if (!container[part]) { 28 | // If the next part is eq to '' it means we are processing complex name (i.e. `some[]`) 29 | // for this case we need to use Array instead of an Object for the index increment purpose 30 | container[part] = parsedName[1] ? {} : []; 31 | } 32 | storeValue(container[part], parsedName.slice(1), value); 33 | } else { 34 | 35 | // Increment Array index for `some[]` case 36 | if (!part) { 37 | part = container.length; 38 | } 39 | 40 | container[part] = value; 41 | } 42 | } 43 | 44 | var utils = { 45 | convert_meta_to_object: function (data) { 46 | var converted_data = {}, 47 | element, 48 | property, 49 | key; 50 | 51 | for (key in data) { 52 | if (data.hasOwnProperty(key)) { 53 | element = key.split(/_(.+)?/)[0]; 54 | property = key.split(/_(.+)?/)[1]; 55 | if (converted_data[element] === undefined) { 56 | converted_data[element] = {}; 57 | } 58 | converted_data[element][property] = data[key]; 59 | } 60 | } 61 | return converted_data; 62 | }, 63 | object_to_array: function (object) { 64 | var array = [], 65 | i; 66 | 67 | // Convert facets to array (JSON.stringify breaks arrays). 68 | if (typeof object === 'object') { 69 | for (i in object) { 70 | array.push(object[i]); 71 | } 72 | object = array; 73 | } 74 | 75 | return object; 76 | }, 77 | checked: function (val1, val2, print) { 78 | "use strict"; 79 | 80 | var checked = false; 81 | if (typeof val1 === 'object' && typeof val2 === 'string' && jQuery.inArray(val2, val1) !== -1) { 82 | checked = true; 83 | } else if (typeof val2 === 'object' && typeof val1 === 'string' && jQuery.inArray(val1, val2) !== -1) { 84 | checked = true; 85 | } else if (val1 === val2) { 86 | checked = true; 87 | } else if (val1 == val2) { 88 | checked = true; 89 | } 90 | 91 | if (print !== undefined && print) { 92 | return checked ? ' checked="checked"' : ''; 93 | } 94 | return checked; 95 | }, 96 | selected: function (val1, val2, print) { 97 | "use strict"; 98 | 99 | var selected = false; 100 | if (typeof val1 === 'object' && typeof val2 === 'string' && jQuery.inArray(val2, val1) !== -1) { 101 | selected = true; 102 | } else if (typeof val2 === 'object' && typeof val1 === 'string' && jQuery.inArray(val1, val2) !== -1) { 103 | selected = true; 104 | } else if (val1 === val2) { 105 | selected = true; 106 | } 107 | 108 | if (print !== undefined && print) { 109 | return selected ? ' selected="selected"' : ''; 110 | } 111 | return selected; 112 | }, 113 | convert_hex: function (hex, opacity) { 114 | if (undefined === hex) { 115 | return ''; 116 | } 117 | if (undefined === opacity) { 118 | opacity = 100; 119 | } 120 | 121 | hex = hex.replace('#', ''); 122 | var r = parseInt(hex.substring(0, 2), 16), 123 | g = parseInt(hex.substring(2, 4), 16), 124 | b = parseInt(hex.substring(4, 6), 16), 125 | result = 'rgba(' + r + ',' + g + ',' + b + ',' + opacity / 100 + ')'; 126 | return result; 127 | }, 128 | debounce: function (callback, threshold) { 129 | var timeout; 130 | return function () { 131 | var context = this, params = arguments; 132 | window.clearTimeout(timeout); 133 | timeout = window.setTimeout(function () { 134 | callback.apply(context, params); 135 | }, threshold); 136 | }; 137 | }, 138 | throttle: function (callback, threshold) { 139 | var suppress = false, 140 | clear = function () { 141 | suppress = false; 142 | }; 143 | return function () { 144 | if (!suppress) { 145 | callback(); 146 | window.setTimeout(clear, threshold); 147 | suppress = true; 148 | } 149 | }; 150 | }, 151 | serializeForm: function (options) { 152 | $.extend({}, options); 153 | 154 | var values = {}, 155 | settings = $.extend(true, { 156 | include: [], 157 | exclude: [], 158 | includeByClass: '' 159 | }, options); 160 | 161 | this.find(':input').each(function () { 162 | 163 | var parsedName; 164 | 165 | // Apply simple checks and filters 166 | if (!this.name || this.disabled || 167 | isInArray(settings.exclude, this.name) || 168 | (settings.include.length && !isInArray(settings.include, this.name)) || 169 | this.className.indexOf(settings.includeByClass) === -1) { 170 | return; 171 | } 172 | 173 | // Parse complex names 174 | // JS RegExp doesn't support "positive look behind" :( that's why so weird parsing is used 175 | parsedName = this.name.replace(rName, '[$1').split('['); 176 | if (!parsedName[0]) { 177 | return; 178 | } 179 | 180 | if (this.checked || 181 | isInArray(inputTypes, this.type) || 182 | isInArray(inputNodes, this.nodeName.toLowerCase())) { 183 | 184 | // Simulate control with a complex name (i.e. `some[]`) 185 | // as it handled in the same way as Checkboxes should 186 | if (this.type === 'checkbox') { 187 | parsedName.push(''); 188 | } 189 | 190 | // jQuery.val() is used to simplify of getting values 191 | // from the custom controls (which follow jQuery .val() API) and Multiple Select 192 | storeValue(values, parsedName, $(this).val()); 193 | } 194 | }); 195 | 196 | return values; 197 | } 198 | 199 | }; 200 | 201 | // Import this module. 202 | window.WPJSFSP = window.WPJSFSP || {}; 203 | window.WPJSFSP.utils = utils; 204 | 205 | $.fn.wpjsfspSerializeForm = utils.serializeForm; 206 | }(jQuery)); -------------------------------------------------------------------------------- /assets/sass/admin.scss: -------------------------------------------------------------------------------- 1 | /*! 2 | * @copyright Copyright (c) 2017, Code Atlantic 3 | * @author Daniel Iser 4 | */ 5 | 6 | $plugin_prefix: 'wpjsfsp'; 7 | $custom_select2_selector: 'wpjsfselect2'; 8 | $tab-color: #E4E4E4; 9 | 10 | // Shared modules. 11 | @import 'modules/general'; 12 | @import 'modules/fields'; 13 | @import 'modules/select2'; 14 | @import 'modules/tabs'; 15 | @import 'modules/modal'; 16 | 17 | // Custom pages. 18 | @import 'partials/admin/settings'; 19 | -------------------------------------------------------------------------------- /assets/sass/modules/_fields.scss: -------------------------------------------------------------------------------- 1 | /*! 2 | * @copyright Copyright (c) 2017, Code Atlantic 3 | * @author Daniel Iser 4 | */ 5 | 6 | $plugin_prefix: 'plugin' !default; 7 | $custom_select2_selector: 'select2' !default; 8 | 9 | .#{$plugin_prefix}-field { 10 | position: relative; 11 | 12 | margin-bottom: 1em; 13 | 14 | > label { 15 | display: block; 16 | font-weight: bold; 17 | } 18 | 19 | .#{$plugin_prefix}-doclink { 20 | font-size: 16px; 21 | line-height: 20px; 22 | } 23 | 24 | } 25 | 26 | /** 27 | * Sections 28 | */ 29 | .#{$plugin_prefix}-field-section { 30 | 31 | } 32 | 33 | /** 34 | * Heading & separator fields 35 | */ 36 | .#{$plugin_prefix}-field-heading, 37 | .#{$plugin_prefix}-field-separator { 38 | h3 { 39 | //font-size: 1.2em; 40 | // margin-top: 0; 41 | // margin-bottom: 0; 42 | } 43 | 44 | .#{$plugin_prefix}-desc { 45 | display: none; 46 | } 47 | 48 | h3 + hr { 49 | // margin-top: 1em; // Reset 50 | // margin-bottom: 2em; 51 | } 52 | 53 | hr + h3 { 54 | // margin-top: 1em; // Reset to wp default. 55 | // margin-bottom: 1em; // Reset to wp default. 56 | } 57 | } 58 | 59 | .#{$plugin_prefix}-field-hidden { 60 | display: none; 61 | } 62 | 63 | .#{$plugin_prefix}-field-editor { 64 | #insert-media-button { 65 | display: none; 66 | } 67 | } 68 | 69 | .#{$plugin_prefix}-field-link { 70 | 71 | max-width: 400; 72 | 73 | input { 74 | margin-right: 24px; 75 | display: block; 76 | width: 100%; 77 | } 78 | 79 | button.dashicons { 80 | position: absolute; 81 | right: 0; 82 | width: 1.5em; 83 | height: 1.5em; 84 | line-height: 1; 85 | padding: 0; 86 | font-size: 16px; 87 | vertical-align: sub; 88 | margin-top: 1px; 89 | box-shadow: 0 0 0 #cccccc; 90 | } 91 | } 92 | 93 | /** 94 | * Select fields 95 | */ 96 | .#{$plugin_prefix}-field-select { 97 | option.bold { 98 | font-weight: bold; 99 | font-size: 1.125em; 100 | } 101 | } 102 | 103 | /** 104 | * Checkbox fields 105 | */ 106 | .#{$plugin_prefix}-field-checkbox { 107 | position: relative; 108 | 109 | label { 110 | margin-left: 1.5em; 111 | 112 | &.#{$plugin_prefix}-desc { 113 | display: inline; 114 | font-weight: inherit; 115 | font-size: inherit; 116 | margin: 0 0 1em; 117 | } 118 | } 119 | 120 | input[type="checkbox"] { 121 | position: absolute; 122 | top: .25em; 123 | } 124 | } 125 | 126 | /** 127 | * Multicheck & Radio fields 128 | */ 129 | .#{$plugin_prefix}-field-multicheck, 130 | .#{$plugin_prefix}-field-radio { 131 | 132 | input, label { 133 | line-height: 1em; 134 | margin-bottom: 10px; 135 | } 136 | 137 | input[type="radio"] { 138 | display: inline-block; 139 | margin-right: .25em; 140 | } 141 | 142 | input + label { 143 | font-weight: normal; 144 | display: inline-block; 145 | } 146 | 147 | label:first-child { 148 | font-weight: bold; 149 | margin: 0 0 10px; 150 | } 151 | 152 | > p.#{$plugin_prefix}-desc { 153 | margin: 0 0 .5em; 154 | } 155 | 156 | } 157 | 158 | /** 159 | * Range & range slider fields 160 | */ 161 | .#{$plugin_prefix}-field-range, 162 | .#{$plugin_prefix}-field-rangeslider { 163 | input[type="range"] { 164 | vertical-align: middle; 165 | } 166 | 167 | .#{$plugin_prefix}-range-manual { 168 | padding-right: 25px; 169 | text-align: right; 170 | width: 80px; 171 | } 172 | 173 | .#{$plugin_prefix}-range-value-unit { 174 | position: relative; 175 | display: inline-block; 176 | margin-left: -30px; 177 | margin-right: 10px; 178 | width: 20px; 179 | text-align: left; 180 | top: .125em; 181 | } 182 | } 183 | 184 | /** 185 | * Image fields 186 | */ 187 | .#{$plugin_prefix}-image-field { 188 | .#{$plugin_prefix}-image-field .#{$plugin_prefix}-image-select, 189 | &.#{$plugin_prefix}-image-empty .#{$plugin_prefix}-image-preview { 190 | display: none; 191 | } 192 | 193 | &.#{$plugin_prefix}-image-empty .#{$plugin_prefix}-image-select { 194 | display: block; 195 | } 196 | 197 | .#{$plugin_prefix}-image-preview-img { 198 | float: left; 199 | line-height: 0; 200 | margin: 5px 0; 201 | } 202 | 203 | .#{$plugin_prefix}-image-preview-img img { 204 | max-width: 60px; 205 | } 206 | 207 | .#{$plugin_prefix}-image-preview select { 208 | margin: 8px 0 8px 10px; 209 | width: 200px; 210 | } 211 | 212 | .#{$plugin_prefix}-image-edit { 213 | margin: 0 0 0 11px; 214 | } 215 | 216 | .#{$plugin_prefix}-image-replace, 217 | .#{$plugin_prefix}-image-remove { 218 | margin: 0 0 0 8px; 219 | } 220 | } 221 | 222 | /** 223 | * Conditions field 224 | */ 225 | .#{$plugin_prefix}-field-conditions { 226 | 227 | .facet-builder { 228 | 229 | .#{$plugin_prefix}-doclink { 230 | display: none; 231 | } 232 | 233 | 234 | p { 235 | margin: 0 0 1em; 236 | } 237 | a { 238 | text-decoration: none; 239 | } 240 | 241 | .facet-groups { 242 | 243 | display: none; 244 | 245 | .facet-group-wrap { 246 | 247 | .facet-group { 248 | box-shadow: 0 1px 0 #ccc; 249 | color: #555; 250 | border: 1px solid #ccc; 251 | background: #f7f7f7; 252 | } 253 | 254 | &:last-child .and, 255 | .add-or { 256 | em, 257 | a, 258 | button { 259 | color: #0073aa; 260 | cursor: pointer; 261 | 262 | &::before { 263 | content: "+ "; 264 | } 265 | 266 | } 267 | 268 | } 269 | 270 | } 271 | 272 | } 273 | 274 | .facet-list { 275 | } 276 | 277 | .facet { 278 | position: relative; 279 | padding: 12px 30px 6px 10px; 280 | border-bottom: 1px solid #e1e1e1; 281 | border-top: 1px solid #fff; 282 | 283 | &:first-child { 284 | border-top: 0; 285 | 286 | .or { 287 | display: none; 288 | } 289 | } 290 | 291 | &::before, 292 | &::after { 293 | display: table; 294 | content: ""; 295 | line-height: 0; 296 | } 297 | 298 | &::after { 299 | clear: both; 300 | } 301 | 302 | } 303 | 304 | .#{$plugin_prefix}-field { 305 | margin-bottom: 0.5em; 306 | } 307 | 308 | .facet-col { 309 | float: left; 310 | margin-right: 20px; 311 | padding-bottom: 6px; 312 | position: relative; 313 | min-width: 175px; 314 | 315 | select, 316 | input { 317 | margin: 0; 318 | max-width: 100%; 319 | } 320 | } 321 | 322 | .facet-target { 323 | 324 | position: relative; 325 | //max-width: 240px; 326 | 327 | * { 328 | box-sizing: border-box; 329 | } 330 | 331 | 332 | select, 333 | .#{$custom_select2_selector}-container .#{$custom_select2_selector}-selection { 334 | padding-left: 28px; 335 | 336 | // Rendered Option 337 | .#{$custom_select2_selector}-selection__rendered { 338 | padding-left: 3px; 339 | } 340 | 341 | } 342 | 343 | .#{$plugin_prefix}-not-operand { 344 | cursor: pointer; 345 | position: absolute; 346 | left: 2px; 347 | top: 2px; 348 | z-index: 10; 349 | //width: 23px; 350 | line-height: 24px; 351 | height: 25px; 352 | 353 | //padding: 0; 354 | background: #f7f7f7; 355 | border: 1px solid transparent; 356 | border-radius: 2px 0 0 2px; 357 | border-right: 1px solid #ddd; 358 | text-align: center; 359 | 360 | span { 361 | font-size: 1.25em; 362 | } 363 | 364 | &::before { 365 | color: #555; 366 | font-size: 16px; 367 | line-height: 24px; 368 | } 369 | 370 | input[type="checkbox"] { 371 | display: none; 372 | } 373 | 374 | &:focus { 375 | outline: none; 376 | border: 1px solid #5b9dd9; 377 | box-shadow: 0 0 2px rgba(30, 140, 190, 0.8); 378 | } 379 | 380 | } 381 | 382 | &.not-operand-checked { 383 | 384 | .#{$plugin_prefix}-not-operand { 385 | span, 386 | &::before { 387 | color: #a00; 388 | 389 | } 390 | } 391 | 392 | select, 393 | .#{$custom_select2_selector}-container .#{$custom_select2_selector}-selection { 394 | padding-left: 58px; 395 | } 396 | 397 | } 398 | 399 | .#{$custom_select2_selector}-container-active { 400 | .#{$custom_select2_selector}-choices, 401 | .#{$custom_select2_selector}-single { 402 | border-color: #5b9dd9; 403 | box-shadow: 0 0 2px rgba(30, 140, 190, 0.8); 404 | } 405 | } 406 | 407 | } 408 | 409 | .facet-actions { 410 | position: absolute; 411 | right: 6px; 412 | top: 18px; 413 | 414 | button { 415 | border: 0; 416 | padding: 0; 417 | background: none; 418 | margin-left: 5px; 419 | } 420 | } 421 | 422 | .dashicons-plus-alt, 423 | .dashicons-dismiss { 424 | color: #999; 425 | } 426 | 427 | /* + AND + OR link stylings */ 428 | .or { 429 | color: #484848; 430 | font-weight: 500; 431 | margin-left: -21px; 432 | left: 50%; 433 | position: absolute; 434 | top: -6px; 435 | font-style: normal; 436 | line-height: 10px; 437 | text-transform: uppercase; 438 | } 439 | 440 | .add-or { 441 | border-top: 1px solid #fff; 442 | text-align: center; 443 | 444 | > .add { 445 | left: -6.5px; 446 | position: relative; 447 | top: -9px; 448 | } 449 | } 450 | 451 | .and { 452 | border-bottom: 1px dashed #e1e1e1; 453 | margin: .5em 0 1.7em; 454 | text-align: center; 455 | } 456 | 457 | .or, 458 | .add-or > .add { 459 | background: #f7f7f7; 460 | font-size: 1.1em; 461 | padding: 0 10px; 462 | } 463 | 464 | .and, .add-or { 465 | em, 466 | a, 467 | button, 468 | label { 469 | background: transparent; 470 | font-size: 1.1em; 471 | font-style: normal; 472 | margin: 0 10px; 473 | padding: 0 10px; 474 | position: relative; 475 | top: 9px; 476 | text-transform: uppercase; 477 | box-shadow: none; 478 | color: #484848; 479 | cursor: default; 480 | border: 0; 481 | 482 | } 483 | 484 | em { 485 | color: #484848; 486 | } 487 | } 488 | 489 | } 490 | 491 | .no-facet-groups { 492 | display: block; 493 | .facet-target { 494 | max-width: 100%; 495 | } 496 | } 497 | 498 | /* Conditionals */ 499 | .has-conditions { 500 | 501 | .facet-groups { 502 | display: block; 503 | } 504 | 505 | .no-facet-groups { 506 | display: none; 507 | } 508 | 509 | } 510 | 511 | .#{$plugin_prefix}-field-select2 { 512 | select { 513 | width: 100% !important; 514 | } 515 | } 516 | 517 | } 518 | 519 | /** 520 | * License fields. 521 | */ 522 | .#{$plugin_prefix}-field-license_key { 523 | background: #fafafa; 524 | padding: 14px; 525 | border-top: 2px solid #999; 526 | border-bottom: 2px solid #999; 527 | margin: 0 -14px 14px; 528 | 529 | p { 530 | font-size: 13px; 531 | margin-top: 0; 532 | } 533 | 534 | a { 535 | color: #444; 536 | } 537 | 538 | a:hover { 539 | text-decoration: none; 540 | } 541 | 542 | span.wpjsfsp-license-status { 543 | margin-left: 5px; 544 | margin-right: 5px; 545 | } 546 | 547 | .#{$plugin_prefix}-license-messages { 548 | p:last-child { 549 | margin-bottom: 0; 550 | } 551 | } 552 | 553 | &.#{$plugin_prefix}-license-expires-soon-notice { 554 | //background-color: #00a0d2; 555 | //color: #fff; 556 | //border-color: #00a0d2; 557 | border-color: #dc3232; 558 | } 559 | 560 | &.#{$plugin_prefix}-license-valid-notice { 561 | //background-color: #60c560; 562 | border-color: #46b450; 563 | //color: #fff; 564 | .wpjsfsp-license-status { 565 | color: #46b450; 566 | } 567 | } 568 | 569 | &.#{$plugin_prefix}-license-inactive-notice { 570 | //background-color: #0073aa; 571 | border-color: #0073aa; 572 | //color: #fff; 573 | } 574 | 575 | &.#{$plugin_prefix}-license-expiration-date-notice { 576 | 577 | } 578 | 579 | &.#{$plugin_prefix}-license-expired-notice { 580 | background-color: #e24e4e; 581 | color: #fff; 582 | border-color: #dc3232; 583 | } 584 | 585 | &.#{$plugin_prefix}-license-error-notice, 586 | &.#{$plugin_prefix}-license-missing-notice, 587 | &.#{$plugin_prefix}-license-invalid-notice, 588 | &.#{$plugin_prefix}-license-site_inactive-notice, 589 | &.#{$plugin_prefix}-license-item_name_mismatch-notice { 590 | background-color: #ffebcd; 591 | border-color: #dc3232; 592 | } 593 | 594 | &.#{$plugin_prefix}-license-expired-notice { 595 | a { 596 | color: #fff; 597 | 598 | &:hover { 599 | text-decoration: none; 600 | } 601 | } 602 | } 603 | 604 | } 605 | 606 | [data-#{$plugin_prefix}-dependencies] { 607 | display: none; 608 | } -------------------------------------------------------------------------------- /assets/sass/modules/_general.scss: -------------------------------------------------------------------------------- 1 | /*! 2 | * @copyright Copyright (c) 2017, Code Atlantic 3 | * @author Daniel Iser 4 | */ 5 | 6 | button.no-button { 7 | border: 0; 8 | padding: 0; 9 | background: none; 10 | cursor: pointer; 11 | 12 | &.link-button { 13 | color: #0073aa; 14 | &:hover { 15 | color: #00a0d2; 16 | } 17 | } 18 | 19 | &.delete-button { 20 | color: #a00; 21 | &:hover { 22 | color: #f00; 23 | } 24 | } 25 | } 26 | 27 | -------------------------------------------------------------------------------- /assets/sass/modules/_modal.scss: -------------------------------------------------------------------------------- 1 | $plugin_prefix: 'plugin' !default; 2 | 3 | .#{$plugin_prefix}-modal-background { 4 | 5 | &, &:before, &:after, 6 | & *, & *:before, & *:after { 7 | -webkit-box-sizing: border-box; /* Safari/Chrome, other WebKit */ 8 | -moz-box-sizing: border-box; /* Firefox, other Gecko */ 9 | box-sizing: border-box; 10 | } 11 | 12 | 13 | display: none; 14 | position: fixed; 15 | top: 0; 16 | left: 0; 17 | right: 0; 18 | bottom: 0; 19 | height: 100%; 20 | width: 100%; 21 | background: rgba(0,0,0,0.70); 22 | z-index: 100100; 23 | overflow-y: scroll; 24 | 25 | .#{$plugin_prefix}-modal-wrap { 26 | position: absolute; 27 | top: 60px; 28 | margin-bottom: 60px; 29 | left: 50%; 30 | width: 550px; 31 | margin-left: -300px; 32 | background-color: #fff; 33 | box-shadow: 0 3px 6px rgba(0,0,0,.3); 34 | z-index: 100105; 35 | transition: height .2s, margin-top .2s; 36 | 37 | @media screen and ( max-width: 520px ) { 38 | width: auto; 39 | margin-left: 0; 40 | top: 10px; 41 | right: 10px; 42 | bottom: 10px; 43 | left: 10px; 44 | } 45 | } 46 | 47 | .#{$plugin_prefix}-modal-header { 48 | position: absolute; 49 | top: 0; 50 | right: 0; 51 | left: 0; 52 | height: 36px; 53 | padding: 0 36px 0 16px; 54 | font-size: 18px; 55 | font-weight: 600; 56 | line-height: 36px; 57 | background: #fcfcfc; 58 | border-bottom: 1px solid #dfdfdf; 59 | 60 | .#{$plugin_prefix}-modal-close { 61 | position: absolute; 62 | top: 0; 63 | right: 0; 64 | width: 36px; 65 | height: 36px; 66 | padding: 0; 67 | color: #666; 68 | text-align: center; 69 | background: 0 0; 70 | border: none; 71 | cursor: pointer; 72 | 73 | &::before { 74 | font: 400 20px/36px dashicons; 75 | vertical-align: top; 76 | speak: none; 77 | -webkit-font-smoothing: antialiased; 78 | -moz-osx-font-smoothing: grayscale; 79 | width: 36px; 80 | height: 36px; 81 | content: '\f158'; 82 | } 83 | } 84 | 85 | } 86 | 87 | .#{$plugin_prefix}-modal-content { 88 | padding: 52px 16px 60px; 89 | 90 | div.error { 91 | margin: 0 0 10px; 92 | } 93 | p { 94 | margin-top: 0; 95 | } 96 | textarea { 97 | width: 100%; 98 | } 99 | 100 | @media screen and (max-width: 782px) { 101 | padding: 50px 16px 60px; 102 | } 103 | } 104 | 105 | .#{$plugin_prefix}-modal-footer { 106 | position: absolute; 107 | bottom: 0; 108 | left: 0; 109 | right: 0; 110 | padding: 8px 16px; 111 | background: #fcfcfc; 112 | border-top: 1px solid #dfdfdf; 113 | 114 | .cancel { 115 | line-height: 25px; 116 | float: left; 117 | 118 | .no-button { 119 | border: 0; 120 | padding: 0; 121 | background: none; 122 | cursor: pointer; 123 | 124 | &.link-button { 125 | color: #0073aa; 126 | text-decoration: underline; 127 | } 128 | 129 | } 130 | 131 | .submitdelete { 132 | text-decoration: none; 133 | padding: 1px 2px; 134 | } 135 | 136 | @media screen and (max-width: 782px) { 137 | line-height: 32px; 138 | } 139 | } 140 | 141 | .#{$plugin_prefix}-submit { 142 | line-height: 23px; 143 | float: right; 144 | 145 | button { 146 | float: right; 147 | margin-bottom: 0; 148 | 149 | } 150 | 151 | .spinner { 152 | float: left; 153 | vertical-align: middle; 154 | } 155 | 156 | } 157 | } 158 | 159 | &.tabbed-content { 160 | 161 | .#{$plugin_prefix}-modal-content { 162 | padding: 36px 0 44px; 163 | } 164 | } 165 | 166 | } -------------------------------------------------------------------------------- /assets/sass/modules/_select2.scss: -------------------------------------------------------------------------------- 1 | /*! 2 | * @copyright Copyright (c) 2017, Code Atlantic 3 | * @author Daniel Iser 4 | */ 5 | 6 | $plugin_prefix: 'plugin' !default; 7 | $custom_select2_selector: 'select2' !default; 8 | 9 | /* jQuery select2 Styles. 10 | The bulk of this is to style jquery select2 to better resemble the default WP dashboard inputs. 11 | */ 12 | 13 | .#{$plugin_prefix}-field-select2 { 14 | 15 | //region Select2 Core Styles 16 | 17 | // These are here for the namespaced #{$custom_select2_selector} and so that we can properly address issues when other plugins do things wrong. 18 | .#{$custom_select2_selector}-container { 19 | box-sizing: border-box; 20 | 21 | display: inline-block; 22 | margin: 0; 23 | position: relative; 24 | vertical-align: middle; 25 | 26 | @import "../vendor/select2/single"; 27 | @import "../vendor/select2/multiple"; 28 | } 29 | 30 | @import "../vendor/select2/dropdown"; 31 | 32 | .#{$custom_select2_selector}-close-mask { 33 | border: 0; 34 | margin: 0; 35 | padding: 0; 36 | display: block; 37 | position: fixed; 38 | left: 0; 39 | top: 0; 40 | min-height: 100%; 41 | min-width: 100%; 42 | height: auto; 43 | width: auto; 44 | opacity: 0; 45 | z-index: 99; 46 | 47 | // styles required for IE to work 48 | 49 | background-color: #fff; 50 | filter: alpha(opacity=0); 51 | } 52 | 53 | .#{$custom_select2_selector}-hidden-accessible { 54 | border: 0 !important; 55 | clip: rect(0 0 0 0) !important; 56 | height: 1px !important; 57 | margin: -1px !important; 58 | overflow: hidden !important; 59 | padding: 0 !important; 60 | position: absolute !important; 61 | width: 1px !important; 62 | } 63 | 64 | @import "../vendor/select2/theme/default/layout"; 65 | @import "../vendor/select2/theme/classic/layout"; 66 | //endregion Select2 Core Styles 67 | 68 | > .#{$custom_select2_selector}-container--below.#{$custom_select2_selector}-container--open + .#{$custom_select2_selector}-container--open, 69 | > .#{$custom_select2_selector}-container--below.#{$custom_select2_selector}-container--open + .#{$plugin_prefix}-desc + .#{$custom_select2_selector}-container--open { 70 | position: absolute !important; 71 | } 72 | 73 | position: relative; 74 | 75 | // All Select2 Containers - Wraps Both Selectbox & Dropdown Elements 76 | .#{$custom_select2_selector}-container { 77 | 78 | // Selectbox 79 | .#{$custom_select2_selector}-selection { 80 | margin: 1px; 81 | font-size: 14px; 82 | border-radius: 0; 83 | box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.07); 84 | border-color: #ddd; 85 | transition: 0.05s border-color ease-in-out; 86 | 87 | } 88 | &.#{$custom_select2_selector}-container--focus { 89 | 90 | .#{$custom_select2_selector}-selection { 91 | outline: none; 92 | border-color: #5b9dd9; 93 | box-shadow: 0 0 2px rgba(30, 140, 190, 0.8); 94 | } 95 | } 96 | // Single Select 97 | .#{$custom_select2_selector}-selection--single { 98 | 99 | // Rendered Option 100 | .#{$custom_select2_selector}-selection__rendered { 101 | //padding-left: 0; 102 | } 103 | 104 | } 105 | 106 | // Multiple Select 107 | .#{$custom_select2_selector}-selection--multiple { 108 | overflow-y: auto; 109 | max-height: 150px; 110 | min-height: 28px; 111 | line-height: 16px; 112 | font-size: 12px; 113 | 114 | .#{$custom_select2_selector}-selection__clear { 115 | margin-right: 3px; 116 | } 117 | 118 | .#{$custom_select2_selector}-selection__rendered { 119 | 120 | } 121 | 122 | .#{$custom_select2_selector}-search--inline { 123 | margin: 0; 124 | // Search Field 125 | .#{$custom_select2_selector}-search__field { 126 | border-color: #ddd; 127 | padding: 3px 5px 0; 128 | min-width: 5em; 129 | width: 100% !important; 130 | } 131 | } 132 | 133 | .#{$custom_select2_selector}-selection__choice { 134 | margin-top: 4px; 135 | margin-bottom: 0; 136 | } 137 | 138 | } 139 | 140 | // Dropdown 141 | .#{$custom_select2_selector}-dropdown { 142 | margin: 0 1px; 143 | border-color: #ddd; 144 | box-shadow: 0 1px 2px rgba(0, 0, 0, 0.07); 145 | // Compensate for the margin applied to the Selectbox. 146 | max-width: calc(100% - 4px); 147 | position: relative; 148 | 149 | // Search Field 150 | .#{$custom_select2_selector}-search__field { 151 | border-color: #ddd; 152 | padding: 3px 5px; 153 | min-width: 5em; 154 | } 155 | 156 | // Results 157 | .#{$custom_select2_selector}-results { 158 | 159 | // Each result set. Can be nested. 160 | .#{$custom_select2_selector}-results__option { 161 | padding: 3px 6px; 162 | margin: 0; 163 | 164 | &[aria-selected=true] { 165 | } 166 | 167 | } 168 | .#{$custom_select2_selector}-results__option[role=group] { 169 | padding: 3px 0 0; 170 | 171 | .#{$custom_select2_selector}-results__group { 172 | padding: 0 6px; 173 | } 174 | } 175 | 176 | .#{$custom_select2_selector}-results__options--nested { 177 | padding: 3px 6px 0; 178 | } 179 | 180 | // Hover 181 | .#{$custom_select2_selector}-results__option--highlighted { 182 | background: #3e86d0; 183 | } 184 | 185 | } 186 | 187 | } 188 | 189 | } 190 | 191 | .#{$custom_select2_selector}-container + .#{$custom_select2_selector}-container--open { 192 | top: inherit !important; 193 | } 194 | 195 | } -------------------------------------------------------------------------------- /assets/sass/modules/_tabs.scss: -------------------------------------------------------------------------------- 1 | $tab-color: #E4E4E4 !default; 2 | $plugin_prefix: 'plugin' !default; 3 | 4 | .#{$plugin_prefix}-tabs-container { 5 | box-sizing: border-box; 6 | 7 | > * { 8 | box-sizing: border-box; 9 | } 10 | 11 | position: relative; 12 | 13 | > ul.tabs { 14 | margin: 0; 15 | 16 | .tab { 17 | font-size: 1.2em; 18 | 19 | a { 20 | padding: 8px 16px; 21 | border: 0; 22 | display: block; 23 | text-decoration: none; 24 | &:focus { 25 | box-shadow: none; 26 | } 27 | } 28 | 29 | } 30 | } 31 | 32 | > .tab-content { 33 | 34 | display: none; 35 | padding: 16px; 36 | 37 | &.active { 38 | display: block; 39 | } 40 | 41 | .form-table { 42 | display: block; 43 | 44 | &:first-child { 45 | margin-top: 0; 46 | } 47 | } 48 | } 49 | 50 | &.horizontal-tabs { 51 | display: block; 52 | 53 | > ul.tabs { 54 | > li.tab { 55 | 56 | display: inline-block; 57 | padding: 0; 58 | margin: 0; 59 | 60 | a { 61 | padding: .5em 1em; 62 | 63 | } 64 | 65 | } 66 | 67 | } 68 | 69 | > .tab-content { 70 | padding-top: 16px; 71 | } 72 | } 73 | 74 | &.vertical-tabs { 75 | min-height: 100px; 76 | //padding-left: 150px; 77 | //width: calc(100% - 150px); 78 | padding-left: 140px; 79 | width: 100%; 80 | 81 | > ul.tabs { 82 | width: 140px; 83 | min-height: 100%; 84 | display: block; 85 | position: absolute; 86 | left: 0; 87 | top: 0; 88 | margin: 0; 89 | //background: #23282D; 90 | border-top: 0; 91 | border-right: 1px solid #DFDFDF; 92 | 93 | > .tab { 94 | margin: 0; 95 | display: block; 96 | border-bottom: 1px solid #eee; 97 | 98 | a { 99 | background: #FCFCFC; 100 | color: #000; 101 | display: block; 102 | } 103 | 104 | &:hover a, a:focus { 105 | background-color: #0073AA; 106 | } 107 | 108 | &.active { 109 | 110 | a { 111 | background-color: #32373C; 112 | color: #fff; 113 | } 114 | } 115 | 116 | &:first-child { 117 | margin-top: 8px; 118 | } 119 | 120 | } 121 | } 122 | 123 | > .tab-content { 124 | } 125 | 126 | } 127 | 128 | &.link-tabs { 129 | 130 | > ul.tabs { 131 | display: block; 132 | 133 | > li.tab { 134 | display: inline-block; 135 | 136 | a { 137 | display: inline; 138 | padding: 0 0.25em; 139 | color: #0073aa; 140 | } 141 | 142 | &.active a, 143 | a:active { 144 | color: #000; 145 | } 146 | 147 | &.active a, 148 | &:hover a, 149 | a:active { 150 | text-decoration: underline; 151 | } 152 | 153 | &::after { 154 | display: inline-block; 155 | content: "|"; 156 | margin: 0 0.25em; 157 | } 158 | 159 | &:last-child::after { 160 | content: ""; 161 | } 162 | 163 | } 164 | } 165 | 166 | } 167 | 168 | &.sub-tabs { 169 | > .tab-content { 170 | padding: 16px 0 0; 171 | } 172 | } 173 | 174 | 175 | &[data-tab-count="0"], 176 | &[data-tab-count="1"] { 177 | &.horizontal-tabs { 178 | > ul.tabs { 179 | display: none; 180 | } 181 | } 182 | 183 | &.sub-tabs { 184 | > .tab-content { 185 | padding-top: 0; 186 | } 187 | } 188 | } 189 | 190 | 191 | } -------------------------------------------------------------------------------- /assets/sass/partials/admin/_settings.scss: -------------------------------------------------------------------------------- 1 | /*! 2 | * @copyright Copyright (c) 2017, Code Atlantic 3 | * @author Daniel Iser 4 | */ 5 | 6 | #wpjsfsp-settings { 7 | 8 | // Custom styles here. 9 | 10 | } -------------------------------------------------------------------------------- /assets/sass/vendor/select2/_dropdown.scss: -------------------------------------------------------------------------------- 1 | .wpjsfselect2-dropdown { 2 | background-color: white; 3 | 4 | border: 1px solid #aaa; 5 | border-radius: 4px; 6 | 7 | box-sizing: border-box; 8 | 9 | display: block; 10 | 11 | position: absolute; 12 | left: -100000px; 13 | 14 | width: 100%; 15 | 16 | z-index: 1051; 17 | } 18 | 19 | .wpjsfselect2-results { 20 | display: block; 21 | } 22 | 23 | .wpjsfselect2-results__options { 24 | list-style: none; 25 | margin: 0; 26 | padding: 0; 27 | } 28 | 29 | .wpjsfselect2-results__option { 30 | padding: 6px; 31 | 32 | user-select: none; 33 | -webkit-user-select: none; 34 | 35 | &[aria-selected] { 36 | cursor: pointer; 37 | } 38 | } 39 | 40 | .wpjsfselect2-container--open .wpjsfselect2-dropdown { 41 | left: 0; 42 | } 43 | 44 | .wpjsfselect2-container--open .wpjsfselect2-dropdown--above { 45 | border-bottom: none; 46 | border-bottom-left-radius: 0; 47 | border-bottom-right-radius: 0; 48 | } 49 | 50 | .wpjsfselect2-container--open .wpjsfselect2-dropdown--below { 51 | border-top: none; 52 | border-top-left-radius: 0; 53 | border-top-right-radius: 0; 54 | } 55 | 56 | .wpjsfselect2-search--dropdown { 57 | display: block; 58 | padding: 4px; 59 | 60 | .wpjsfselect2-search__field { 61 | padding: 4px; 62 | width: 100%; 63 | box-sizing: border-box; 64 | 65 | &::-webkit-search-cancel-button { 66 | -webkit-appearance: none; 67 | } 68 | } 69 | 70 | &.wpjsfselect2-search--hide { 71 | display: none; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /assets/sass/vendor/select2/_multiple.scss: -------------------------------------------------------------------------------- 1 | .wpjsfselect2-selection--multiple { 2 | box-sizing: border-box; 3 | 4 | cursor: pointer; 5 | display: block; 6 | 7 | min-height: 32px; 8 | 9 | user-select: none; 10 | -webkit-user-select: none; 11 | 12 | .wpjsfselect2-selection__rendered { 13 | display: inline-block; 14 | overflow: hidden; 15 | padding-left: 8px; 16 | text-overflow: ellipsis; 17 | white-space: nowrap; 18 | } 19 | } 20 | 21 | .wpjsfselect2-search--inline { 22 | float: left; 23 | 24 | .wpjsfselect2-search__field { 25 | box-sizing: border-box; 26 | border: none; 27 | font-size: 100%; 28 | margin-top: 5px; 29 | padding: 0; 30 | 31 | &::-webkit-search-cancel-button { 32 | -webkit-appearance: none; 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /assets/sass/vendor/select2/_single.scss: -------------------------------------------------------------------------------- 1 | .wpjsfselect2-selection--single { 2 | box-sizing: border-box; 3 | 4 | cursor: pointer; 5 | display: block; 6 | 7 | height: 28px; 8 | 9 | user-select: none; 10 | -webkit-user-select: none; 11 | 12 | .wpjsfselect2-selection__rendered { 13 | display: block; 14 | padding-left: 8px; 15 | padding-right: 20px; 16 | 17 | overflow: hidden; 18 | text-overflow: ellipsis; 19 | white-space: nowrap; 20 | } 21 | 22 | .wpjsfselect2-selection__clear { 23 | position: relative; 24 | } 25 | } 26 | 27 | &[dir="rtl"] { 28 | .wpjsfselect2-selection--single { 29 | .wpjsfselect2-selection__rendered { 30 | padding-right: 8px; 31 | padding-left: 20px; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /assets/sass/vendor/select2/core.scss: -------------------------------------------------------------------------------- 1 | .wpjsfselect2-container { 2 | box-sizing: border-box; 3 | 4 | display: inline-block; 5 | margin: 0; 6 | position: relative; 7 | vertical-align: middle; 8 | 9 | @import "single"; 10 | @import "multiple"; 11 | } 12 | 13 | @import "dropdown"; 14 | 15 | .wpjsfselect2-close-mask { 16 | border: 0; 17 | margin: 0; 18 | padding: 0; 19 | display: block; 20 | position: fixed; 21 | left: 0; 22 | top: 0; 23 | min-height: 100%; 24 | min-width: 100%; 25 | height: auto; 26 | width: auto; 27 | opacity: 0; 28 | z-index: 99; 29 | 30 | // styles required for IE to work 31 | 32 | background-color: #fff; 33 | filter: alpha(opacity=0); 34 | } 35 | 36 | .wpjsfselect2-hidden-accessible { 37 | border: 0 !important; 38 | clip: rect(0 0 0 0) !important; 39 | height: 1px !important; 40 | margin: -1px !important; 41 | overflow: hidden !important; 42 | padding: 0 !important; 43 | position: absolute !important; 44 | width: 1px !important; 45 | } 46 | 47 | @import "theme/default/layout"; 48 | @import "theme/classic/layout"; 49 | -------------------------------------------------------------------------------- /assets/sass/vendor/select2/mixins/_gradients.scss: -------------------------------------------------------------------------------- 1 | // https://github.com/twbs/bootstrap-sass/blob/3.3-stable/assets/stylesheets/bootstrap/mixins/_gradients.scss#L17-L27 2 | 3 | // Vertical gradient, from top to bottom 4 | // 5 | // Creates two color stops, start and end, by specifying a color and position for each color stop. 6 | // Color stops are not available in IE9 and below. 7 | @mixin gradient-vertical($start-color: #555, $end-color: #333, $start-percent: 0%, $end-percent: 100%) { 8 | background-image: -webkit-linear-gradient(top, $start-color $start-percent, $end-color $end-percent); // Safari 5.1-6, Chrome 10+ 9 | background-image: -o-linear-gradient(top, $start-color $start-percent, $end-color $end-percent); // Opera 12 10 | background-image: linear-gradient(to bottom, $start-color $start-percent, $end-color $end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+ 11 | background-repeat: repeat-x; 12 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#{ie-hex-str($start-color)}', endColorstr='#{ie-hex-str($end-color)}', GradientType=0); // IE9 and down 13 | } 14 | -------------------------------------------------------------------------------- /assets/sass/vendor/select2/theme/classic/_defaults.scss: -------------------------------------------------------------------------------- 1 | $remove-color: #888 !default; 2 | $remove-hover-color: #555 !default; 3 | $remove-width: 20px !default; 4 | 5 | $selection-color: #444 !default; 6 | 7 | $border-color: #aaa !default; 8 | $border-radius: 4px !default; 9 | 10 | $focus-border-color: #5897fb !default; 11 | 12 | $container-height: 28px !default; 13 | 14 | $selection-bg-top-color: white !default; 15 | $selection-bg-bottom-color: #eeeeee !default; 16 | 17 | $container-placeholder-color: #999 !default; 18 | 19 | $container-focus-border-color: blue !default; 20 | 21 | $selection-opened-bg-top-color: $selection-bg-bottom-color !default; 22 | $selection-opened-bg-bottom-color: $selection-bg-top-color !default; 23 | 24 | $dropdown-z-index: 1 !default; 25 | 26 | $dropdown-bg-color: $selection-bg-top-color !default; 27 | 28 | $results-max-height: 200px !default; 29 | $results-nested-padding: 20px !default; 30 | 31 | $results-choice-bg-hover-color: #3875d7 !default; 32 | $results-choice-fg-hover-color: white !default; 33 | 34 | $results-choice-fg-unselectable-color: grey !default; 35 | -------------------------------------------------------------------------------- /assets/sass/vendor/select2/theme/classic/_multiple.scss: -------------------------------------------------------------------------------- 1 | .wpjsfselect2-selection--multiple { 2 | background-color: white; 3 | 4 | border: 1px solid $border-color; 5 | border-radius: $border-radius; 6 | 7 | cursor: text; 8 | 9 | outline: 0; 10 | 11 | &:focus { 12 | border: 1px solid $focus-border-color; 13 | } 14 | 15 | .wpjsfselect2-selection__rendered { 16 | list-style: none; 17 | margin: 0; 18 | padding: 0 5px; 19 | } 20 | 21 | .wpjsfselect2-selection__clear { 22 | display: none; 23 | } 24 | 25 | .wpjsfselect2-selection__choice { 26 | background-color: #e4e4e4; 27 | 28 | border: 1px solid $border-color; 29 | border-radius: $border-radius; 30 | 31 | cursor: default; 32 | 33 | float: left; 34 | 35 | margin-right: 5px; 36 | margin-top: 5px; 37 | padding: 0 5px; 38 | } 39 | 40 | .wpjsfselect2-selection__choice__remove { 41 | color: $remove-color; 42 | cursor: pointer; 43 | 44 | display: inline-block; 45 | font-weight: bold; 46 | 47 | margin-right: 2px; 48 | 49 | &:hover { 50 | color: $remove-hover-color; 51 | } 52 | } 53 | } 54 | 55 | &[dir="rtl"] { 56 | .wpjsfselect2-selection--multiple { 57 | .wpjsfselect2-selection__choice { 58 | float: right; 59 | } 60 | 61 | .wpjsfselect2-selection__choice { 62 | margin-left: 5px; 63 | margin-right: auto; 64 | } 65 | 66 | .wpjsfselect2-selection__choice__remove { 67 | margin-left: 2px; 68 | margin-right: auto; 69 | } 70 | } 71 | } 72 | 73 | &.wpjsfselect2-container--open { 74 | .wpjsfselect2-selection--multiple { 75 | border: 1px solid $focus-border-color; 76 | } 77 | 78 | &.wpjsfselect2-container--above { 79 | .wpjsfselect2-selection--multiple { 80 | border-top: none; 81 | border-top-left-radius: 0; 82 | border-top-right-radius: 0; 83 | } 84 | } 85 | 86 | &.wpjsfselect2-container--below { 87 | .wpjsfselect2-selection--multiple { 88 | border-bottom: none; 89 | border-bottom-left-radius: 0; 90 | border-bottom-right-radius: 0; 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /assets/sass/vendor/select2/theme/classic/_single.scss: -------------------------------------------------------------------------------- 1 | .wpjsfselect2-selection--single { 2 | background-color: mix($selection-bg-top-color, $selection-bg-bottom-color); 3 | 4 | border: 1px solid $border-color; 5 | border-radius: $border-radius; 6 | 7 | outline: 0; 8 | 9 | @include gradient-vertical($selection-bg-top-color, $selection-bg-bottom-color, 50%, 100%); 10 | 11 | &:focus { 12 | border: 1px solid $focus-border-color; 13 | } 14 | 15 | .wpjsfselect2-selection__rendered { 16 | color: #444; 17 | line-height: 28px; 18 | } 19 | 20 | .wpjsfselect2-selection__clear { 21 | cursor: pointer; 22 | float: right; 23 | font-weight: bold; 24 | margin-right: 10px; 25 | } 26 | 27 | .wpjsfselect2-selection__placeholder { 28 | color: #999; 29 | } 30 | 31 | .wpjsfselect2-selection__arrow { 32 | background-color: #ddd; 33 | 34 | border: none; 35 | border-left: 1px solid $border-color; 36 | border-top-right-radius: $border-radius; 37 | border-bottom-right-radius: $border-radius; 38 | 39 | height: 26px; 40 | 41 | position: absolute; 42 | 43 | top: 1px; 44 | right: 1px; 45 | 46 | width: 20px; 47 | 48 | @include gradient-vertical(#eeeeee, #cccccc, 50%, 100%); 49 | 50 | b { 51 | border-color: #888 transparent transparent transparent; 52 | border-style: solid; 53 | border-width: 5px 4px 0 4px; 54 | 55 | height: 0; 56 | left: 50%; 57 | 58 | margin-left: -4px; 59 | margin-top: -2px; 60 | 61 | position: absolute; 62 | 63 | top: 50%; 64 | width: 0; 65 | } 66 | } 67 | } 68 | 69 | &[dir="rtl"] { 70 | .wpjsfselect2-selection--single { 71 | .wpjsfselect2-selection__clear { 72 | float: left; 73 | } 74 | 75 | .wpjsfselect2-selection__arrow { 76 | border: none; 77 | border-right: 1px solid $border-color; 78 | 79 | border-radius: 0; 80 | border-top-left-radius: $border-radius; 81 | border-bottom-left-radius: $border-radius; 82 | 83 | left: 1px; 84 | right: auto; 85 | } 86 | } 87 | } 88 | 89 | &.wpjsfselect2-container--open { 90 | .wpjsfselect2-selection--single { 91 | border: 1px solid $focus-border-color; 92 | 93 | .wpjsfselect2-selection__arrow { 94 | background: transparent; 95 | 96 | border: none; 97 | 98 | b { 99 | border-color: transparent transparent #888 transparent; 100 | border-width: 0 4px 5px 4px; 101 | } 102 | } 103 | } 104 | 105 | &.wpjsfselect2-container--above { 106 | .wpjsfselect2-selection--single { 107 | border-top: none; 108 | border-top-left-radius: 0; 109 | border-top-right-radius: 0; 110 | 111 | @include gradient-vertical($selection-opened-bg-bottom-color, $selection-opened-bg-top-color, 0%, 50%); 112 | } 113 | } 114 | 115 | &.wpjsfselect2-container--below { 116 | .wpjsfselect2-selection--single { 117 | border-bottom: none; 118 | border-bottom-left-radius: 0; 119 | border-bottom-right-radius: 0; 120 | 121 | @include gradient-vertical($selection-opened-bg-top-color, $selection-opened-bg-bottom-color, 50%, 100%); 122 | } 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /assets/sass/vendor/select2/theme/classic/layout.scss: -------------------------------------------------------------------------------- 1 | @import "defaults"; 2 | @import "../../mixins/gradients"; 3 | 4 | .wpjsfselect2-container--classic { 5 | @import "single"; 6 | @import "multiple"; 7 | 8 | .wpjsfselect2-search--dropdown { 9 | .wpjsfselect2-search__field { 10 | border: 1px solid $border-color; 11 | outline: 0; 12 | } 13 | } 14 | 15 | .wpjsfselect2-search--inline { 16 | .wpjsfselect2-search__field { 17 | outline: 0; 18 | box-shadow: none; 19 | } 20 | } 21 | 22 | .wpjsfselect2-dropdown { 23 | background-color: $dropdown-bg-color; 24 | border: 1px solid transparent; 25 | } 26 | 27 | .wpjsfselect2-dropdown--above { 28 | border-bottom: none; 29 | } 30 | 31 | .wpjsfselect2-dropdown--below { 32 | border-top: none; 33 | } 34 | 35 | .wpjsfselect2-results > .wpjsfselect2-results__options { 36 | max-height: $results-max-height; 37 | overflow-y: auto; 38 | } 39 | 40 | .wpjsfselect2-results__option { 41 | &[role=group] { 42 | padding: 0; 43 | } 44 | 45 | &[aria-disabled=true] { 46 | color: $results-choice-fg-unselectable-color; 47 | } 48 | } 49 | 50 | .wpjsfselect2-results__option--highlighted[aria-selected] { 51 | background-color: $results-choice-bg-hover-color; 52 | color: $results-choice-fg-hover-color; 53 | } 54 | 55 | .wpjsfselect2-results__group { 56 | cursor: default; 57 | display: block; 58 | padding: 6px; 59 | } 60 | 61 | &.wpjsfselect2-container--open .wpjsfselect2-dropdown { 62 | border-color: $focus-border-color; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /assets/sass/vendor/select2/theme/default/_multiple.scss: -------------------------------------------------------------------------------- 1 | .wpjsfselect2-selection--multiple { 2 | background-color: white; 3 | border: 1px solid #aaa; 4 | border-radius: 4px; 5 | cursor: text; 6 | 7 | .wpjsfselect2-selection__rendered { 8 | box-sizing: border-box; 9 | list-style: none; 10 | margin: 0; 11 | padding: 0 5px; 12 | width: 100%; 13 | 14 | li { 15 | list-style: none; 16 | } 17 | } 18 | 19 | .wpjsfselect2-selection__placeholder { 20 | color: #999; 21 | 22 | margin-top: 5px; 23 | 24 | float: left; 25 | } 26 | 27 | .wpjsfselect2-selection__clear { 28 | cursor: pointer; 29 | float: right; 30 | font-weight: bold; 31 | margin-top: 5px; 32 | margin-right: 10px; 33 | } 34 | 35 | .wpjsfselect2-selection__choice { 36 | background-color: #e4e4e4; 37 | 38 | border: 1px solid #aaa; 39 | border-radius: 4px; 40 | cursor: default; 41 | 42 | float: left; 43 | 44 | margin-right: 5px; 45 | margin-top: 5px; 46 | padding: 0 5px; 47 | } 48 | 49 | .wpjsfselect2-selection__choice__remove { 50 | color: #999; 51 | cursor: pointer; 52 | 53 | display: inline-block; 54 | font-weight: bold; 55 | 56 | margin-right: 2px; 57 | 58 | &:hover { 59 | color: #333; 60 | } 61 | } 62 | } 63 | 64 | &[dir="rtl"] { 65 | .wpjsfselect2-selection--multiple { 66 | .wpjsfselect2-selection__choice, .wpjsfselect2-selection__placeholder, .wpjsfselect2-search--inline { 67 | float: right; 68 | } 69 | 70 | .wpjsfselect2-selection__choice { 71 | margin-left: 5px; 72 | margin-right: auto; 73 | } 74 | 75 | .wpjsfselect2-selection__choice__remove { 76 | margin-left: 2px; 77 | margin-right: auto; 78 | } 79 | } 80 | } 81 | 82 | &.wpjsfselect2-container--focus { 83 | .wpjsfselect2-selection--multiple { 84 | border: solid black 1px; 85 | outline: 0; 86 | } 87 | } 88 | 89 | &.wpjsfselect2-container--disabled { 90 | .wpjsfselect2-selection--multiple { 91 | background-color: #eee; 92 | cursor: default; 93 | } 94 | 95 | .wpjsfselect2-selection__choice__remove { 96 | display: none; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /assets/sass/vendor/select2/theme/default/_single.scss: -------------------------------------------------------------------------------- 1 | .wpjsfselect2-selection--single { 2 | background-color: #fff; 3 | border: 1px solid #aaa; 4 | border-radius: 4px; 5 | 6 | .wpjsfselect2-selection__rendered { 7 | color: #444; 8 | line-height: 28px; 9 | } 10 | 11 | .wpjsfselect2-selection__clear { 12 | cursor: pointer; 13 | float: right; 14 | font-weight: bold; 15 | } 16 | 17 | .wpjsfselect2-selection__placeholder { 18 | color: #999; 19 | } 20 | 21 | .wpjsfselect2-selection__arrow { 22 | height: 26px; 23 | 24 | position: absolute; 25 | 26 | top: 1px; 27 | right: 1px; 28 | 29 | width: 20px; 30 | 31 | b { 32 | border-color: #888 transparent transparent transparent; 33 | border-style: solid; 34 | border-width: 5px 4px 0 4px; 35 | 36 | height: 0; 37 | left: 50%; 38 | 39 | margin-left: -4px; 40 | margin-top: -2px; 41 | 42 | position: absolute; 43 | 44 | top: 50%; 45 | width: 0; 46 | } 47 | } 48 | } 49 | 50 | &[dir="rtl"] { 51 | .wpjsfselect2-selection--single { 52 | .wpjsfselect2-selection__clear { 53 | float: left; 54 | } 55 | 56 | .wpjsfselect2-selection__arrow { 57 | left: 1px; 58 | right: auto; 59 | } 60 | } 61 | } 62 | 63 | &.wpjsfselect2-container--disabled { 64 | .wpjsfselect2-selection--single { 65 | background-color: #eee; 66 | cursor: default; 67 | 68 | .wpjsfselect2-selection__clear { 69 | display: none; 70 | } 71 | } 72 | } 73 | 74 | &.wpjsfselect2-container--open { 75 | .wpjsfselect2-selection--single { 76 | .wpjsfselect2-selection__arrow { 77 | b { 78 | border-color: transparent transparent #888 transparent; 79 | border-width: 0 4px 5px 4px; 80 | } 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /assets/sass/vendor/select2/theme/default/layout.scss: -------------------------------------------------------------------------------- 1 | .wpjsfselect2-container--default { 2 | @import "single"; 3 | @import "multiple"; 4 | 5 | &.wpjsfselect2-container--open.wpjsfselect2-container--above { 6 | .wpjsfselect2-selection--single, .wpjsfselect2-selection--multiple { 7 | border-top-left-radius: 0; 8 | border-top-right-radius: 0; 9 | } 10 | } 11 | 12 | &.wpjsfselect2-container--open.wpjsfselect2-container--below { 13 | .wpjsfselect2-selection--single, .wpjsfselect2-selection--multiple { 14 | border-bottom-left-radius: 0; 15 | border-bottom-right-radius: 0; 16 | } 17 | } 18 | 19 | .wpjsfselect2-search--dropdown { 20 | .wpjsfselect2-search__field { 21 | border: 1px solid #aaa; 22 | } 23 | } 24 | 25 | .wpjsfselect2-search--inline { 26 | .wpjsfselect2-search__field { 27 | background: transparent; 28 | border: none; 29 | outline: 0; 30 | box-shadow: none; 31 | -webkit-appearance: textfield; 32 | } 33 | } 34 | 35 | .wpjsfselect2-results > .wpjsfselect2-results__options { 36 | max-height: 200px; 37 | overflow-y: auto; 38 | } 39 | 40 | .wpjsfselect2-results__option { 41 | &[role=group] { 42 | padding: 0; 43 | } 44 | 45 | &[aria-disabled=true] { 46 | color: #999; 47 | } 48 | 49 | &[aria-selected=true] { 50 | background-color: #ddd; 51 | } 52 | 53 | .wpjsfselect2-results__option { 54 | padding-left: 1em; 55 | 56 | .wpjsfselect2-results__group { 57 | padding-left: 0; 58 | } 59 | 60 | .wpjsfselect2-results__option { 61 | margin-left: -1em; 62 | padding-left: 2em; 63 | 64 | .wpjsfselect2-results__option { 65 | margin-left: -2em; 66 | padding-left: 3em; 67 | 68 | .wpjsfselect2-results__option { 69 | margin-left: -3em; 70 | padding-left: 4em; 71 | 72 | .wpjsfselect2-results__option { 73 | margin-left: -4em; 74 | padding-left: 5em; 75 | 76 | .wpjsfselect2-results__option { 77 | margin-left: -5em; 78 | padding-left: 6em; 79 | } 80 | } 81 | } 82 | } 83 | } 84 | } 85 | } 86 | 87 | .wpjsfselect2-results__option--highlighted[aria-selected] { 88 | background-color: #5897fb; 89 | color: white; 90 | } 91 | 92 | .wpjsfselect2-results__group { 93 | cursor: default; 94 | display: block; 95 | padding: 6px; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /classes/Admin.php: -------------------------------------------------------------------------------- 1 | array(), 49 | 'total_count' => 0, 50 | ); 51 | switch ( $_REQUEST['object_type'] ) { 52 | case 'post_type': 53 | $post_type = ! empty( $_REQUEST['object_key'] ) ? $_REQUEST['object_key'] : 'post'; 54 | $args = array( 55 | 's' => ! empty( $_REQUEST['s'] ) ? $_REQUEST['s'] : null, 56 | 'post__in' => ! empty( $_REQUEST['include'] ) ? array_map( 'intval', (array) $_REQUEST['include'] ) : null, 57 | 'page' => ! empty( $_REQUEST['page'] ) ? absint( $_REQUEST['page'] ) : null, 58 | 'posts_per_page' => 10, 59 | ); 60 | $query = Helpers::post_type_selectlist( $post_type, $args, true ); 61 | foreach ( $query['items'] as $name => $id ) { 62 | $results['items'][] = array( 63 | 'id' => $id, 64 | 'text' => $name, 65 | ); 66 | } 67 | $results['total_count'] = $query['total_count']; 68 | break; 69 | case 'taxonomy': 70 | $taxonomy = ! empty( $_REQUEST['object_key'] ) ? $_REQUEST['object_key'] : 'category'; 71 | $args = array( 72 | 'search' => ! empty( $_REQUEST['s'] ) ? $_REQUEST['s'] : '', 73 | 'include' => ! empty( $_REQUEST['include'] ) ? $_REQUEST['include'] : null, 74 | 'page' => ! empty( $_REQUEST['page'] ) ? absint( $_REQUEST['page'] ) : null, 75 | 'number' => 10, 76 | ); 77 | $query = Helpers::taxonomy_selectlist( $taxonomy, $args, true ); 78 | foreach ( $query['items'] as $name => $id ) { 79 | $results['items'][] = array( 80 | 'id' => $id, 81 | 'text' => $name, 82 | ); 83 | } 84 | $results['total_count'] = $query['total_count']; 85 | break; 86 | } 87 | echo json_encode( $results ); 88 | die(); 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /classes/Admin/Assets.php: -------------------------------------------------------------------------------- 1 | wp_create_nonce( 'wpjsfsp-admin-nonce' ), 49 | 'I10n' => array( 50 | 'conditions' => array( 51 | 'not_operand' => array( 52 | 'is' => __( 'Is', 'wp-js-form-sample-plugin' ), 53 | 'not' => __( 'Not', 'wp-js-form-sample-plugin' ), 54 | ), 55 | ), 56 | 'save' => __( 'Save', 'wp-js-form-sample-plugin' ), 57 | 'cancel' => __( 'Cancel', 'wp-js-form-sample-plugin' ), 58 | 'add' => __( 'Add', 'wp-js-form-sample-plugin' ), 59 | 'update' => __( 'Update', 'wp-js-form-sample-plugin' ), 60 | ), 61 | ) ) ); 62 | } 63 | 64 | 65 | } 66 | 67 | /** 68 | * JavaScript Wordpress editor 69 | * Author: Ante Primorac 70 | * Author URI: http://anteprimorac.from.hr 71 | * Version: 1.1 72 | * License: 73 | * Copyright (c) 2013 Ante Primorac 74 | * Permission is hereby granted, free of charge, to any person obtaining a copy 75 | * of this software and associated documentation files (the "Software"), to deal 76 | * in the Software without restriction, including without limitation the rights 77 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 78 | * copies of the Software, and to permit persons to whom the Software is 79 | * furnished to do so, subject to the following conditions: 80 | * 81 | * The above copyright notice and this permission notice shall be included in 82 | * all copies or substantial portions of the Software. 83 | * 84 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 85 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 86 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 87 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 88 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 89 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 90 | * THE SOFTWARE. 91 | * Usage: 92 | * server side(WP): 93 | * js_wp_editor( $settings ); 94 | * client side(jQuery): 95 | * $('textarea').wp_editor( options ); 96 | */ 97 | public static function js_wp_editor() { 98 | if ( ! class_exists( '\_WP_Editors' ) ) { 99 | require( ABSPATH . WPINC . '/class-wp-editor.php' ); 100 | } 101 | 102 | $set = \_WP_Editors::parse_settings( 'wpjsfsp_id', array() ); 103 | 104 | if ( ! current_user_can( 'upload_files' ) ) { 105 | $set['media_buttons'] = false; 106 | } 107 | 108 | if ( $set['media_buttons'] ) { 109 | wp_enqueue_style( 'buttons' ); 110 | wp_enqueue_script( 'thickbox' ); 111 | wp_enqueue_style( 'thickbox' ); 112 | wp_enqueue_script( 'media-upload' ); 113 | wp_enqueue_script( 'wp-embed' ); 114 | 115 | $post = get_post( 1 ); 116 | if ( ! $post && ! empty( $GLOBALS['post_ID'] ) ) { 117 | $post = $GLOBALS['post_ID']; 118 | } 119 | wp_enqueue_media( array( 120 | 'post' => $post, 121 | ) ); 122 | } 123 | 124 | \_WP_Editors::editor_settings( 'wpjsfsp_id', $set ); 125 | 126 | wp_localize_script( 'wpjsfsp-admin', 'wpjsfsp_wpeditor_vars', array( 127 | 'url' => get_home_url(), 128 | 'includes_url' => includes_url(), 129 | ) ); 130 | } 131 | 132 | } -------------------------------------------------------------------------------- /classes/Admin/Footer_Templates.php: -------------------------------------------------------------------------------- 1 | 29 | 32 | 33 | 36 | 37 | 58 | 59 | 69 | 70 | 73 | 74 | 84 | 85 | 88 | 89 | 92 | 97 | 100 | 101 | 104 | 105 | 108 | 109 | 112 | 113 | 116 | 117 | 120 | 125 | 128 | 129 | 132 | 133 | 137 | 138 | 141 | 142 | 149 | 150 | 172 | 173 | 179 | 180 | 186 | 191 | 198 | 199 | 219 | 220 | 223 | 224 | 227 | 228 | 237 | 242 | 283 | 284 | 301 | 302 | 305 | 306 | 309 | 314 | 317 | 318 | 353 | 354 | 374 | 375 | 399 | preload_posts = isset( $_GET['page'] ) && $_GET['page'] == 'wpjsfsp-settings'; 49 | } 50 | 51 | return self::$instance; 52 | } 53 | 54 | /** 55 | * @param array $conditions 56 | */ 57 | public function add_conditions( $conditions = array() ) { 58 | foreach ( $conditions as $key => $condition ) { 59 | if ( empty( $condition['id'] ) && ! is_numeric( $key ) ) { 60 | $condition['id'] = $key; 61 | } 62 | 63 | $this->add_condition( $condition ); 64 | } 65 | } 66 | 67 | /** 68 | * @param array $condition 69 | */ 70 | public function add_condition( $condition = array() ) { 71 | if ( ! empty( $condition['id'] ) && ! isset ( $this->conditions[ $condition['id'] ] ) ) { 72 | $condition = wp_parse_args( $condition, array( 73 | 'id' => '', 74 | 'callback' => null, 75 | 'group' => '', 76 | 'name' => '', 77 | 'priority' => 10, 78 | 'fields' => array(), 79 | 'advanced' => false, 80 | ) ); 81 | 82 | $this->conditions[ $condition['id'] ] = $condition; 83 | } 84 | 85 | return; 86 | } 87 | 88 | /** 89 | * @return array 90 | */ 91 | public function get_conditions() { 92 | if ( ! isset( $this->conditions ) ) { 93 | $this->register_conditions(); 94 | } 95 | 96 | 97 | return $this->conditions; 98 | } 99 | 100 | /** 101 | * @return array|mixed 102 | */ 103 | public function condition_sort_order() { 104 | if ( ! $this->condition_sort_order ) { 105 | 106 | $order = apply_filters( 'wpjsfsp_condition_group_sort_order', array( 107 | __( 'General', 'wp-js-form-sample-plugin' ) => 1, 108 | __( 'Pages', 'wp-js-form-sample-plugin' ) => 5, 109 | __( 'Posts', 'wp-js-form-sample-plugin' ) => 5, 110 | __( 'Categories', 'wp-js-form-sample-plugin' ) => 14, 111 | __( 'Tags', 'wp-js-form-sample-plugin' ) => 14, 112 | __( 'Format', 'wp-js-form-sample-plugin' ) => 16, 113 | ) ); 114 | 115 | $post_types = get_post_types( array( 'public' => true, '_builtin' => false ), 'objects' ); 116 | foreach ( $post_types as $name => $post_type ) { 117 | $order[ $post_type->labels->name ] = 10; 118 | } 119 | 120 | $taxonomies = get_taxonomies( array( 'public' => true, '_builtin' => false ), 'objects' ); 121 | foreach ( $taxonomies as $tax_name => $taxonomy ) { 122 | $order[ $taxonomy->labels->name ] = 15; 123 | } 124 | 125 | $this->condition_sort_order = apply_filters( 'wpjsfsp_condition_sort_order', $order ); 126 | 127 | } 128 | 129 | return $this->condition_sort_order; 130 | } 131 | 132 | /** 133 | * @param $a 134 | * @param $b 135 | * 136 | * @return int 137 | */ 138 | public function sort_condition_groups( $a, $b ) { 139 | 140 | $order = $this->condition_sort_order(); 141 | 142 | $ai = isset( $order[ $a ] ) ? intval( $order[ $a ] ) : 10; 143 | $bi = isset( $order[ $b ] ) ? intval( $order[ $b ] ) : 10; 144 | 145 | if ( $ai == $bi ) { 146 | return 0; 147 | } 148 | 149 | // Compare their positions in line. 150 | return $ai > $bi ? 1 : - 1; 151 | } 152 | 153 | /** 154 | * @return array 155 | */ 156 | public function get_conditions_by_group() { 157 | 158 | static $groups; 159 | 160 | if ( ! isset( $groups ) ) { 161 | 162 | $groups = array(); 163 | 164 | foreach ( $this->get_conditions() as $condition ) { 165 | $groups[ $condition['group'] ][ $condition['id'] ] = $condition; 166 | } 167 | 168 | uksort( $groups, array( $this, 'sort_condition_groups' ) ); 169 | 170 | } 171 | 172 | return $groups; 173 | } 174 | 175 | /** 176 | * @return array 177 | */ 178 | public function dropdown_list() { 179 | $groups = array(); 180 | 181 | $conditions_by_group = $this->get_conditions_by_group(); 182 | 183 | foreach ( $conditions_by_group as $group => $_conditions ) { 184 | 185 | $conditions = array(); 186 | 187 | foreach ( $_conditions as $id => $condition ) { 188 | $conditions[ $id ] = $condition['name']; 189 | } 190 | 191 | $groups[ $group ] = $conditions; 192 | } 193 | 194 | return $groups; 195 | } 196 | 197 | /** 198 | * @param null $condition 199 | * 200 | * @return mixed|null 201 | */ 202 | public function get_condition( $condition = null ) { 203 | $conditions = $this->get_conditions(); 204 | 205 | return isset( $conditions[ $condition ] ) ? $conditions[ $condition ] : null; 206 | } 207 | 208 | /** 209 | * @return array 210 | */ 211 | public function generate_post_type_conditions() { 212 | $conditions = array(); 213 | $post_types = get_post_types( array( 'public' => true ), 'objects' ); 214 | 215 | foreach ( $post_types as $name => $post_type ) { 216 | 217 | if ( $post_type->has_archive ) { 218 | $conditions[ $name . '_index' ] = array( 219 | 'group' => $post_type->labels->name, 220 | 'name' => sprintf( _x( '%s Archive', 'condition: post type plural label ie. Posts: All', 'wp-js-form-sample-plugin' ), $post_type->labels->name ), 221 | 'callback' => array( '\\WPJSFSP\Condition_Callbacks', 'post_type' ), 222 | 'priority' => 5, 223 | ); 224 | } 225 | 226 | $conditions[ $name . '_all' ] = array( 227 | 'group' => $post_type->labels->name, 228 | 'name' => sprintf( _x( 'A %s', 'condition: post type singular label ie. Posts: All', 'wp-js-form-sample-plugin' ), $post_type->labels->singular_name ), 229 | 'callback' => array( '\\WPJSFSP\Condition_Callbacks', 'post_type' ), 230 | ); 231 | 232 | $conditions[ $name . '_selected' ] = array( 233 | 'group' => $post_type->labels->name, 234 | 'name' => sprintf( _x( 'A Selected %s', 'condition: post type singular label ie. Posts: Selected', 'wp-js-form-sample-plugin' ), $post_type->labels->singular_name ), 235 | 'fields' => array( 236 | 'selected' => array( 237 | 'placeholder' => sprintf( _x( 'Select %s.', 'condition: post type singular label ie. Select Posts', 'wp-js-form-sample-plugin' ), strtolower( $post_type->labels->singular_name ) ), 238 | 'type' => 'postselect', 239 | 'post_type' => $name, 240 | 'multiple' => true, 241 | 'as_array' => true, 242 | 'options' => $this->preload_posts ? Helpers::post_type_selectlist( $name ) : array(), 243 | ), 244 | ), 245 | 'callback' => array( '\\WPJSFSP\Condition_Callbacks', 'post_type' ), 246 | ); 247 | 248 | $conditions[ $name . '_ID' ] = array( 249 | 'group' => $post_type->labels->name, 250 | 'name' => sprintf( _x( 'A %s with ID', 'condition: post type singular label ie. Posts: ID', 'wp-js-form-sample-plugin' ), $post_type->labels->singular_name ), 251 | 'fields' => array( 252 | 'selected' => array( 253 | 'placeholder' => sprintf( _x( '%s IDs: 128, 129', 'condition: post type singular label ie. Posts IDs', 'wp-js-form-sample-plugin' ), strtolower( $post_type->labels->singular_name ) ), 254 | 'type' => 'text', 255 | ), 256 | ), 257 | 'callback' => array( '\\WPJSFSP\Condition_Callbacks', 'post_type' ), 258 | ); 259 | 260 | $templates = wp_get_theme()->get_page_templates(); 261 | 262 | if ( $name == 'page' && ! empty( $templates ) ) { 263 | $conditions[ $name . '_template' ] = array( 264 | 'group' => $post_type->labels->name, 265 | 'name' => sprintf( _x( 'A %s: With Template', 'condition: post type plural label ie. Pages: With Template', 'wp-js-form-sample-plugin' ), $post_type->labels->name ), 266 | 'fields' => array( 267 | 'selected' => array( 268 | 'type' => 'select', 269 | 'select2' => true, 270 | 'multiple' => true, 271 | 'as_array' => true, 272 | 'options' => array_flip( array_merge( array( 'default' => __( 'Default', 'wp-js-form-sample-plugin' ) ), $templates ) ), 273 | ), 274 | ), 275 | 'callback' => array( '\\WPJSFSP\Condition_Callbacks', 'post_type' ), 276 | ); 277 | } 278 | 279 | $conditions = array_merge( $conditions, $this->generate_post_type_tax_conditions( $name ) ); 280 | 281 | } 282 | 283 | return $conditions; 284 | } 285 | 286 | /** 287 | * @param $name 288 | * 289 | * @return array 290 | */ 291 | public function generate_post_type_tax_conditions( $name ) { 292 | $post_type = get_post_type_object( $name ); 293 | $taxonomies = get_object_taxonomies( $name, 'object' ); 294 | $conditions = array(); 295 | foreach ( $taxonomies as $tax_name => $taxonomy ) { 296 | 297 | $conditions[ $name . '_w_' . $tax_name ] = array( 298 | 'group' => $post_type->labels->name, 299 | 'name' => sprintf( _x( 'A %1$s with %2$s', 'condition: post type plural and taxonomy singular label ie. Posts: With Category', 'wp-js-form-sample-plugin' ), $post_type->labels->singular_name, $taxonomy->labels->singular_name ), 300 | 'fields' => array( 301 | 'selected' => array( 302 | 'placeholder' => sprintf( _x( 'Select %s.', 'condition: post type plural label ie. Select categories', 'wp-js-form-sample-plugin' ), strtolower( $taxonomy->labels->name ) ), 303 | 'type' => 'taxonomyselect', 304 | 'taxonomy' => $tax_name, 305 | 'multiple' => true, 306 | 'as_array' => true, 307 | 'options' => $this->preload_posts ? Helpers::taxonomy_selectlist( $tax_name ) : array(), 308 | ), 309 | ), 310 | 'callback' => array( '\\WPJSFSP\Condition_Callbacks', 'post_type_tax' ), 311 | ); 312 | } 313 | 314 | return $conditions; 315 | } 316 | 317 | /** 318 | * Generates conditions for all public taxonomies. 319 | * 320 | * @return array 321 | */ 322 | public function generate_taxonomy_conditions() { 323 | $conditions = array(); 324 | $taxonomies = get_taxonomies( array( 'public' => true ), 'objects' ); 325 | 326 | foreach ( $taxonomies as $tax_name => $taxonomy ) { 327 | 328 | $conditions[ 'tax_' . $tax_name . '_all' ] = array( 329 | 'group' => $taxonomy->labels->name, 330 | 'name' => sprintf( _x( 'A %s', 'condition: taxonomy plural label ie. Categories: All', 'wp-js-form-sample-plugin' ), $taxonomy->labels->name ), 331 | 'callback' => array( '\\WPJSFSP\Condition_Callbacks', 'taxonomy' ), 332 | ); 333 | 334 | $conditions[ 'tax_' . $tax_name . '_selected' ] = array( 335 | 'group' => $taxonomy->labels->name, 336 | 'name' => sprintf( _x( '%s: Selected', 'condition: taxonomy plural label ie. Categories: Selected', 'wp-js-form-sample-plugin' ), $taxonomy->labels->name ), 337 | 'fields' => array( 338 | 'selected' => array( 339 | 'placeholder' => sprintf( _x( 'Select %s.', 'condition: taxonomy plural label ie. Select Categories', 'wp-js-form-sample-plugin' ), strtolower( $taxonomy->labels->name ) ), 340 | 'type' => 'taxonomyselect', 341 | 'taxonomy' => $tax_name, 342 | 'multiple' => true, 343 | 'as_array' => true, 344 | 'options' => $this->preload_posts ? Helpers::taxonomy_selectlist( $tax_name ) : array(), 345 | ), 346 | ), 347 | 'callback' => array( '\\WPJSFSP\Condition_Callbacks', 'taxonomy' ), 348 | ); 349 | 350 | $conditions[ 'tax_' . $tax_name . '_ID' ] = array( 351 | 'group' => $taxonomy->labels->name, 352 | 'name' => sprintf( _x( 'A %s with IDs', 'condition: taxonomy plural label ie. Categories: Selected', 'wp-js-form-sample-plugin' ), $taxonomy->labels->name ), 353 | 'fields' => array( 354 | 'selected' => array( 355 | 'placeholder' => sprintf( _x( '%s IDs: 128, 129', 'condition: taxonomy plural label ie. Category IDs', 'wp-js-form-sample-plugin' ), strtolower( $taxonomy->labels->singular_name ) ), 356 | 'type' => 'text', 357 | ), 358 | ), 359 | 'callback' => array( '\\WPJSFSP\Condition_Callbacks', 'taxonomy' ), 360 | ); 361 | 362 | } 363 | 364 | return $conditions; 365 | } 366 | 367 | /** 368 | * Registers all known conditions when called. 369 | */ 370 | public function register_conditions() { 371 | 372 | $conditions['is_front_page'] = array( 373 | 'group' => __( 'General', 'wp-js-form-sample-plugin' ), 374 | 'name' => __( 'The Home Page', 'wp-js-form-sample-plugin' ), 375 | 'callback' => 'is_front_page', 376 | 'priority' => 2, 377 | ); 378 | $conditions['is_home'] = array( 379 | 'group' => __( 'Posts', 'wp-js-form-sample-plugin' ), 380 | 'name' => __( 'The Blog Index', 'wp-js-form-sample-plugin' ), 381 | 'callback' => 'is_home', 382 | 'priority' => 1, 383 | ); 384 | $conditions['is_search'] = array( 385 | 'group' => __( 'General', 'wp-js-form-sample-plugin' ), 386 | 'name' => __( 'A Search Result Page', 'wp-js-form-sample-plugin' ), 387 | 'callback' => 'is_search', 388 | ); 389 | $conditions['is_404'] = array( 390 | 'group' => __( 'General', 'wp-js-form-sample-plugin' ), 391 | 'name' => __( 'A 404 Error Page', 'wp-js-form-sample-plugin' ), 392 | 'callback' => 'is_404', 393 | ); 394 | 395 | $conditions = array_merge( $this->generate_post_type_conditions(), $this->generate_taxonomy_conditions() ); 396 | 397 | $conditions = apply_filters( 'wpjsfsp_registered_conditions', $conditions ); 398 | 399 | $this->add_conditions( $conditions ); 400 | } 401 | 402 | } 403 | -------------------------------------------------------------------------------- /classes/Helpers.php: -------------------------------------------------------------------------------- 1 | $value ) { 40 | $array[ $key ] = is_object( $value ) || is_array( $value ) ? self::object_to_array( $value ) : $value; 41 | } 42 | 43 | return $array; 44 | } 45 | 46 | public static function post_type_selectlist( $post_type, $args = array(), $include_total = false ) { 47 | 48 | $args = wp_parse_args( $args, array( 49 | 'posts_per_page' => 10, 50 | 'post_type' => $post_type, 51 | 'post__in' => null, 52 | 'post__not_in' => null, 53 | 'post_status' => null, 54 | 'page' => 1, 55 | // Performance Optimization. 56 | 'no_found_rows' => ! $include_total ? true : false, 57 | 'update_post_term_cache' => false, 58 | 'update_post_meta_cache' => false, 59 | ) ); 60 | 61 | if ( $post_type == 'attachment' ) { 62 | $args['post_status'] = 'inherit'; 63 | } 64 | 65 | // Query Caching. 66 | static $queries = array(); 67 | 68 | $key = md5( serialize( $args ) ); 69 | 70 | if ( ! isset( $queries[ $key ] ) ) { 71 | $query = new \WP_Query( $args ); 72 | 73 | $posts = array(); 74 | foreach ( $query->posts as $post ) { 75 | $posts[ $post->post_title ] = $post->ID; 76 | } 77 | 78 | $results = array( 79 | 'items' => $posts, 80 | 'total_count' => $query->found_posts, 81 | ); 82 | 83 | $queries[ $key ] = $results; 84 | } else { 85 | $results = $queries[ $key ]; 86 | } 87 | 88 | return ! $include_total ? $results['items'] : $results; 89 | } 90 | 91 | public static function taxonomy_selectlist( $taxonomies = array(), $args = array(), $include_total = false ) { 92 | if ( empty ( $taxonomies ) ) { 93 | $taxonomies = array( 'category' ); 94 | } 95 | 96 | $args = wp_parse_args( $args, array( 97 | 'hide_empty' => false, 98 | 'number' => 10, 99 | 'search' => '', 100 | 'include' => null, 101 | 'offset' => 0, 102 | 'page' => null, 103 | ) ); 104 | 105 | if ( $args['page'] ) { 106 | $args['offset'] = ( $args['page'] - 1 ) * $args['number']; 107 | } 108 | 109 | // Query Caching. 110 | static $queries = array(); 111 | 112 | $key = md5( serialize( $args ) ); 113 | 114 | if ( ! isset( $queries[ $key ] ) ) { 115 | $terms = array(); 116 | 117 | foreach ( get_terms( $taxonomies, $args ) as $term ) { 118 | $terms[ $term->name ] = $term->term_id; 119 | } 120 | 121 | $total_args = $args; 122 | unset( $total_args['number'] ); 123 | unset( $total_args['offset'] ); 124 | 125 | $results = array( 126 | 'items' => $terms, 127 | 'total_count' => $include_total ? wp_count_terms( $taxonomies, $total_args ) : null, 128 | ); 129 | 130 | $queries[ $key ] = $results; 131 | } else { 132 | $results = $queries[ $key ]; 133 | } 134 | 135 | return ! $include_total ? $results['items'] : $results; 136 | } 137 | 138 | public static function is_customize_preview() { 139 | global $wp_customize; 140 | 141 | return ( $wp_customize instanceof \WP_Customize_Manager ) && $wp_customize->is_preview(); 142 | } 143 | 144 | } 145 | -------------------------------------------------------------------------------- /classes/Options.php: -------------------------------------------------------------------------------- 1 | ' 8 | }) 9 | }, 10 | pkg = require('./package.json'); 11 | 12 | //region JavaScript 13 | gulp.task('js:admin', function() { 14 | return gulp.src(['assets/js/src/admin/vendor/*.js', 'assets/js/src/admin/plugins/**/*.js', 'assets/js/src/admin/general.js']) 15 | .pipe($fn.plumber(plumberErrorHandler)) 16 | .pipe($fn.jshint()) 17 | .pipe($fn.jshint.reporter('default')) 18 | .pipe($fn.order([ 19 | "vendor/**/*.js", 20 | "plugins/**/*.js", 21 | 'general.js' 22 | ], { base: 'assets/js/src/admin/' })) 23 | .pipe($fn.concat('admin.js')) 24 | // Prefix with the plugin name- 25 | .pipe($fn.rename({ prefix: pkg.name + '-' })) 26 | .pipe(gulp.dest('assets/js')) 27 | .pipe($fn.uglify()) 28 | .pipe($fn.rename({extname: '.min.js'})) 29 | .pipe(gulp.dest('assets/js')) 30 | .pipe($fn.livereload()); 31 | }); 32 | 33 | gulp.task('js:site', function() { 34 | return gulp.src(['assets/js/src/site/plugins/**/*.js', 'assets/js/src/site/general.js']) 35 | .pipe($fn.plumber(plumberErrorHandler)) 36 | .pipe($fn.jshint()) 37 | .pipe($fn.jshint.reporter('default')) 38 | .pipe($fn.order([ 39 | "plugins/compatibility.js", 40 | "plugins/pum.js", 41 | "plugins/**/*.js", 42 | 'general.js' 43 | ], { base: 'assets/js/src/site/' })) 44 | .pipe($fn.concat('site.js')) 45 | // Prefix with the plugin name- 46 | .pipe($fn.rename({ prefix: pkg.name + '-' })) 47 | .pipe(gulp.dest('assets/js')) 48 | .pipe($fn.uglify()) 49 | .pipe($fn.rename({extname: '.min.js'})) 50 | .pipe(gulp.dest('assets/js')) 51 | .pipe($fn.livereload()); 52 | }); 53 | 54 | gulp.task('js:other', function() { 55 | return gulp.src('assets/js/src/*.js') 56 | .pipe($fn.plumber(plumberErrorHandler)) 57 | .pipe($fn.jshint()) 58 | .pipe($fn.jshint.reporter('default')) 59 | .pipe(gulp.dest('assets/js')) 60 | .pipe($fn.uglify()) 61 | .pipe($fn.rename({extname: '.min.js'})) 62 | .pipe(gulp.dest('assets/js')) 63 | .pipe($fn.livereload()); 64 | }); 65 | 66 | gulp.task('js', ['js:admin', 'js:site', 'js:other']); 67 | //endregion JavaScript 68 | 69 | //region Language Files 70 | gulp.task('langpack', function () { 71 | return gulp.src(['**/*.php', '!build/**/*.*']) 72 | .pipe($fn.plumber(plumberErrorHandler)) 73 | .pipe($fn.sort()) 74 | .pipe($fn.wpPot( { 75 | domain: pkg.name, 76 | bugReport: 'danieliser@wizardinternetsolutions.com', 77 | team: 'Daniel Iser ' 78 | } )) 79 | .pipe(gulp.dest('languages')); 80 | }); 81 | //endregion Language Files 82 | 83 | //region SASS & CSS 84 | gulp.task('css', function() { 85 | return gulp.src(['assets/sass/admin.scss', 'assets/sass/site.scss']) 86 | .pipe($fn.plumber(plumberErrorHandler)) 87 | // Prefix with the plugin name- 88 | .pipe($fn.rename({ prefix: pkg.name + '-' })) 89 | .pipe($fn.sourcemaps.init()) 90 | .pipe($fn.sass({ 91 | errLogToConsole: true, 92 | outputStyle: 'expanded', 93 | precision: 10 94 | })) 95 | .pipe($fn.sourcemaps.write()) 96 | .pipe($fn.sourcemaps.init({ 97 | loadMaps: true 98 | })) 99 | .pipe($fn.autoprefixer('last 2 version', '> 1%', 'safari 5', 'ie 8', 'ie 9', 'opera 12.1', 'ios 6', 'android 4')) 100 | .pipe($fn.sourcemaps.write('.')) 101 | .pipe($fn.plumber.stop()) 102 | .pipe(gulp.dest('assets/css')) 103 | .pipe($fn.filter('**/*.css')) // Filtering stream to only css files 104 | .pipe($fn.combineMq()) // Combines Media Queries 105 | .pipe($fn.livereload()) 106 | .pipe($fn.rename({ suffix: '.min' })) 107 | .pipe($fn.csso({ 108 | //sourceMap: true, 109 | })) 110 | .pipe(gulp.dest('assets/css')) 111 | .pipe($fn.livereload()) 112 | .pipe(gulp.dest('assets/css')); 113 | }); 114 | gulp.task('css:other', function() { 115 | return gulp.src(['assets/sass/*.scss', '!assets/sass/admin.scss', '!assets/sass/site.scss']) 116 | .pipe($fn.plumber(plumberErrorHandler)) 117 | .pipe($fn.sourcemaps.init()) 118 | .pipe($fn.sass({ 119 | errLogToConsole: true, 120 | outputStyle: 'expanded', 121 | precision: 10 122 | })) 123 | .pipe($fn.sourcemaps.write()) 124 | .pipe($fn.sourcemaps.init({ 125 | loadMaps: true 126 | })) 127 | .pipe($fn.autoprefixer('last 2 version', '> 1%', 'safari 5', 'ie 8', 'ie 9', 'opera 12.1', 'ios 6', 'android 4')) 128 | .pipe($fn.sourcemaps.write('.')) 129 | .pipe($fn.plumber.stop()) 130 | // Prefix with the plugin name- 131 | .pipe(gulp.dest('assets/css')) 132 | .pipe($fn.filter('**/*.css')) // Filtering stream to only css files 133 | .pipe($fn.combineMq()) // Combines Media Queries 134 | .pipe($fn.livereload()) 135 | .pipe($fn.rename({ suffix: '.min' })) 136 | .pipe($fn.csso({ 137 | //sourceMap: true, 138 | })) 139 | .pipe(gulp.dest('assets/css')) 140 | .pipe($fn.livereload()) 141 | .pipe(gulp.dest('assets/css')); 142 | }); 143 | //endregion SASS & CSS 144 | 145 | //region Cleaners 146 | gulp.task('clean-js:site', function () { 147 | return gulp.src('assets/js/site*.js', {read: false}) 148 | .pipe($fn.plumber(plumberErrorHandler)) 149 | .pipe($fn.clean()); 150 | }); 151 | gulp.task('clean-js:admin', function () { 152 | return gulp.src('assets/js/admin*.js', {read: false}) 153 | .pipe($fn.plumber(plumberErrorHandler)) 154 | .pipe($fn.clean()); 155 | }); 156 | gulp.task('clean-js:other', function () { 157 | return gulp.src(['assets/js/*.js', '!assets/js/site*.js', '!assets/js/admin*.js'], {read: false}) 158 | .pipe($fn.plumber(plumberErrorHandler)) 159 | .pipe($fn.clean()); 160 | }); 161 | gulp.task('clean-css', function () { 162 | return gulp.src(['assets/css/*.css', 'assets/css/*.css.map'], {read: false}) 163 | .pipe($fn.plumber(plumberErrorHandler)) 164 | .pipe($fn.clean()); 165 | }); 166 | gulp.task('clean-langpack', function () { 167 | return gulp.src(['languages/*.pot'], {read: false}) 168 | .pipe($fn.plumber(plumberErrorHandler)) 169 | .pipe($fn.clean()); 170 | }); 171 | gulp.task('clean-build', function () { 172 | return gulp.src('build/*', {read: false}) 173 | .pipe($fn.plumber(plumberErrorHandler)) 174 | .pipe($fn.clean()); 175 | }); 176 | gulp.task('clean-package', function () { 177 | return gulp.src('release/'+pkg.name+'_v'+pkg.version+'.zip', {read: false}) 178 | .pipe($fn.plumber(plumberErrorHandler)) 179 | .pipe($fn.clean({force: true})); 180 | }); 181 | 182 | // Cleaning Routines 183 | gulp.task('clean-js', function (done) { 184 | runSequence( 185 | ['clean-js:site', 'clean-js:admin', 'clean-js:other'], 186 | done 187 | ); 188 | }); 189 | gulp.task('clean-all', function (done) { 190 | runSequence( 191 | ['clean-js', 'clean-css', 'clean-langpack'], 192 | ['clean-build', 'clean-package'], 193 | done 194 | ); 195 | }); 196 | //endregion Cleaners 197 | 198 | //region Watch & Build 199 | gulp.task('watch', function () { 200 | $fn.livereload.listen(); 201 | gulp.watch('assets/sass/**/*.scss', ['css', 'css:other']); 202 | gulp.watch('assets/js/src/admin/**/*.js', ['js:admin']); 203 | gulp.watch('assets/js/src/site/**/*.js', ['js:site']); 204 | gulp.watch(['assets/js/src/**/*.js', '!assets/js/src/site/**/*.js', '!assets/js/src/admin/**/*.js'], ['js:other']); 205 | gulp.watch('**/*.php', ['langpack']); 206 | }); 207 | 208 | // Cleans & Rebuilds Assets Prior to Builds 209 | gulp.task('prebuild', function (done) { 210 | runSequence( 211 | 'clean-all', 212 | ['css', 'css:other', 'js', 'langpack'], 213 | done 214 | ); 215 | }); 216 | 217 | // Copies a clean set of build files into the build folder 218 | gulp.task('build', ['prebuild'], function () { 219 | return gulp.src(['./**/*.*', '!./build/**', '!./release/**', '!./node_modules/**', '!./gulpfile.js', '!./package.json', '!./assets/js/src/**']) 220 | .pipe($fn.plumber(plumberErrorHandler)) 221 | .pipe(gulp.dest('build/'+pkg.name)); 222 | }); 223 | 224 | // Generates a release package with the current version from package.json 225 | gulp.task('package', ['clean-package'], function () { 226 | return gulp.src('build/**/*.*') 227 | .pipe($fn.plumber(plumberErrorHandler)) 228 | .pipe($fn.zip(pkg.name+'_v'+pkg.version+'.zip')) 229 | .pipe(gulp.dest('release')); 230 | }); 231 | 232 | // Runs all build routines and generates a release. 233 | gulp.task('release', function (done) { 234 | runSequence( 235 | 'build', 236 | 'package', 237 | done 238 | ); 239 | }); 240 | 241 | // Runs a release and cleans up afterwards. 242 | gulp.task('release:clean', ['release'], function (done) { 243 | runSequence( 244 | 'clean-build', 245 | done 246 | ); 247 | }); 248 | //endregion Watch & Build 249 | 250 | gulp.task('default', function (done) { 251 | runSequence( 252 | 'prebuild', 253 | 'watch', 254 | done 255 | ); 256 | }); -------------------------------------------------------------------------------- /includes/compat.php: -------------------------------------------------------------------------------- 1 | \n" 12 | "X-Poedit-Basepath: ..\n" 13 | "X-Poedit-SourceCharset: UTF-8\n" 14 | "X-Poedit-KeywordsList: __;_e;_n:1,2;_x:1,2c;_ex:1,2c;_nx:4c,1,2;esc_attr__;esc_attr_e;esc_attr_x:1,2c;esc_html__;esc_html_e;esc_html_x:1,2c;_n_noop:1,2;_nx_noop:3c,1,2;__ngettext_noop:1,2\n" 15 | "X-Poedit-SearchPath-0: .\n" 16 | "X-Poedit-SearchPathExcluded-0: *.js\n" 17 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 18 | 19 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wpjsfsp", 3 | "version": "1.0.0", 4 | "description": "", 5 | "scripts": { 6 | "test": "echo \"Error: no test specified\" && exit 1" 7 | }, 8 | "repository": { 9 | "type": "git", 10 | "url": "git+https://github.com/danieliser/WP-JS-Form-Sample-Plugin.git" 11 | }, 12 | "author": "Daniel Iser", 13 | "license": "GPL-3.0", 14 | "bugs": { 15 | "url": "https://github.com/danieliser/WP-JS-Form-Sample-Plugin/issues" 16 | }, 17 | "homepage": "https://github.com/danieliser/WP-JS-Form-Sample-Plugin#readme", 18 | "devDependencies": { 19 | "gulp": "^3.9.1", 20 | "gulp-autoprefixer": "^3.1.0", 21 | "gulp-clean": "^0.3.2", 22 | "gulp-combine-mq": "^0.4.0", 23 | "gulp-concat": "^2.6.0", 24 | "gulp-csso": "^1.1.0", 25 | "gulp-filter": "^4.0.0", 26 | "gulp-jshint": "^2.0.0", 27 | "gulp-livereload": "^3.8.1", 28 | "gulp-load-plugins": "^1.2.0", 29 | "gulp-notify": "^2.2.0", 30 | "gulp-order": "^1.1.1", 31 | "gulp-plumber": "^1.1.0", 32 | "gulp-rename": "^1.2.2", 33 | "gulp-sass": "^2.2.0", 34 | "gulp-sort": "^2.0.0", 35 | "gulp-sourcemaps": "^1.6.0", 36 | "gulp-todo": "^5.1.0", 37 | "gulp-uglify": "^1.5.3", 38 | "gulp-watch": "^4.3.5", 39 | "gulp-wp-pot": "^1.1.1", 40 | "gulp-zip": "^3.2.0", 41 | "jshint": "^2.9.1", 42 | "run-sequence": "^1.1.5" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /readme.txt: -------------------------------------------------------------------------------- 1 | === WPJSFSP === 2 | Contributors: danieliser 3 | Author URI: https://twitter.com/daniel_iser 4 | Plugin URI: https://github.com/danieliser/WP-JS-Form-Sample-Plugin 5 | Donate link: 6 | Tags: 7 | Requires at least: 3.4.0 8 | Tested up to: 4.8.3 9 | Stable tag: 1.0.0 10 | Minimum PHP: 5.3 11 | License: GNU Version 3 or Any Later Version 12 | 13 | == Description == 14 | 15 | Increase user engagement and grow your business with automated marketing messages 16 | 17 | == Installation == 18 | 19 | = Minimum Requirements = 20 | 21 | * WordPress 3.6 or greater 22 | * PHP version 5.3 or greater 23 | 24 | == Changelog == 25 | = v1.0.0 = 26 | * Initial Release -------------------------------------------------------------------------------- /screenshots/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danieliser/WP-JS-Form-Sample-Plugin/ce1a1d62979b884110ac2b47edac0b6e4f0d5e70/screenshots/1.png -------------------------------------------------------------------------------- /screenshots/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danieliser/WP-JS-Form-Sample-Plugin/ce1a1d62979b884110ac2b47edac0b6e4f0d5e70/screenshots/2.png -------------------------------------------------------------------------------- /screenshots/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danieliser/WP-JS-Form-Sample-Plugin/ce1a1d62979b884110ac2b47edac0b6e4f0d5e70/screenshots/3.png -------------------------------------------------------------------------------- /screenshots/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danieliser/WP-JS-Form-Sample-Plugin/ce1a1d62979b884110ac2b47edac0b6e4f0d5e70/screenshots/4.png -------------------------------------------------------------------------------- /screenshots/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danieliser/WP-JS-Form-Sample-Plugin/ce1a1d62979b884110ac2b47edac0b6e4f0d5e70/screenshots/5.png -------------------------------------------------------------------------------- /screenshots/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danieliser/WP-JS-Form-Sample-Plugin/ce1a1d62979b884110ac2b47edac0b6e4f0d5e70/screenshots/6.png -------------------------------------------------------------------------------- /screenshots/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danieliser/WP-JS-Form-Sample-Plugin/ce1a1d62979b884110ac2b47edac0b6e4f0d5e70/screenshots/7.png -------------------------------------------------------------------------------- /screenshots/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danieliser/WP-JS-Form-Sample-Plugin/ce1a1d62979b884110ac2b47edac0b6e4f0d5e70/screenshots/8.png -------------------------------------------------------------------------------- /screenshots/9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danieliser/WP-JS-Form-Sample-Plugin/ce1a1d62979b884110ac2b47edac0b6e4f0d5e70/screenshots/9.png -------------------------------------------------------------------------------- /wp-js-form-sample-plugin.php: -------------------------------------------------------------------------------- 1 | setup_constants(); 132 | self::$instance->load_textdomain(); 133 | self::$instance->includes(); 134 | self::$instance->init(); 135 | } 136 | 137 | return self::$instance; 138 | } 139 | 140 | /** 141 | * Setup plugin constants 142 | */ 143 | private function setup_constants() { 144 | self::$DIR = plugin_dir_path( __FILE__ ); 145 | self::$URL = plugins_url( '/', __FILE__ ); 146 | self::$FILE = __FILE__; 147 | } 148 | 149 | /** 150 | * Include necessary files 151 | */ 152 | private function includes() { 153 | require_once self::$DIR . '/includes/options.php'; 154 | } 155 | 156 | /** 157 | * Initialize everything 158 | */ 159 | private function init() { 160 | \WPJSFSP\Options::init(); 161 | \WPJSFSP\Admin::init(); 162 | \WPJSFSP\Conditions::instance(); 163 | } 164 | 165 | /** 166 | * Internationalization 167 | */ 168 | private function load_textdomain() { 169 | load_plugin_textdomain( 'wp-js-form-sample-plugin' ); 170 | } 171 | 172 | /** 173 | * Get the template path. 174 | * @return string 175 | */ 176 | public function template_path() { 177 | return apply_filters( 'wpjsfsp_template_path', self::$TEMPLATE_PATH ); 178 | } 179 | } 180 | 181 | /** 182 | * The main function responsible for returning the one true WPJSFSP 183 | * Instance to functions everywhere. 184 | * 185 | * Use this function like you would a global variable, except without needing 186 | * to declare the global. 187 | * 188 | * Example: 189 | * 190 | * @since 1.0.0 191 | * @return object The one true WPJSFSP Instance 192 | */ 193 | function wpjsfsp() { 194 | return WPJSFSP::instance(); 195 | } 196 | 197 | // Get Recipe Manager Running 198 | add_action( 'plugins_loaded', 'wpjsfsp', 9 ); 199 | 200 | /** 201 | * Plugin Activation hook function to check for Minimum PHP and WordPress versions 202 | * 203 | * Cannot use static:: in case php 5.2 is used. 204 | */ 205 | function wpjsfsp_activation_check() { 206 | global $wp_version; 207 | 208 | if ( version_compare( PHP_VERSION, WPJSFSP::$MIN_PHP_VER, '<' ) ) { 209 | $flag = 'PHP'; 210 | } elseif ( version_compare( $wp_version, WPJSFSP::$MIN_WP_VER, '<' ) ) { 211 | $flag = 'WordPress'; 212 | } else { 213 | return; 214 | } 215 | 216 | $version = 'PHP' == $flag ? WPJSFSP::$MIN_PHP_VER : WPJSFSP::$MIN_WP_VER; 217 | 218 | // Deactivate automatically due to insufficient PHP or WP Version. 219 | deactivate_plugins( basename( __FILE__ ) ); 220 | 221 | $notice = sprintf( __( 'The %4$s %1$s %5$s plugin requires %2$s version %3$s or greater.', 'wp-js-form-sample-plugin' ), WPJSFSP::$NAME, $flag, $version, "", "" ); 222 | 223 | wp_die( "

$notice

", __( 'Plugin Activation Error', 'wp-js-form-sample-plugin' ), array( 224 | 'response' => 200, 225 | 'back_link' => true, 226 | ) ); 227 | } 228 | 229 | // Ensure plugin & environment compatibility. 230 | register_activation_hook( __FILE__, 'wpjsfsp_activation_check' ); 231 | --------------------------------------------------------------------------------