From e07920e95649f676382bf6bb726e4296fd747a82 Mon Sep 17 00:00:00 2001 From: Titouan Mathis Date: Thu, 22 Jul 2021 14:55:46 +0200 Subject: [PATCH] Update to v5.9.9 --- acf.php | 4 +- assets/build/js/acf-field-group.js | 4557 ++-- assets/build/js/acf-input.js | 20002 +++++++++------- assets/build/js/acf.js | 8504 +++---- assets/build/js/acf.min.js | 2 +- assets/build/js/pro/acf-pro-blocks.js | 1192 +- assets/build/js/pro/acf-pro-blocks.min.js | 2 +- assets/build/js/pro/acf-pro-field-group.js | 873 +- assets/build/js/pro/acf-pro-input.js | 3344 +-- assets/inc/select2/3/select2-spinner.gif | Bin assets/inc/select2/3/select2.css | 0 assets/inc/select2/3/select2.js | 0 assets/inc/select2/3/select2.min.js | 0 assets/inc/select2/3/select2.png | Bin assets/inc/select2/3/select2x2.png | Bin assets/inc/select2/4/select2.css | 0 assets/inc/select2/4/select2.full.js | 0 assets/inc/select2/4/select2.full.min.js | 0 assets/inc/select2/4/select2.js | 0 assets/inc/select2/4/select2.min.css | 0 assets/inc/select2/4/select2.min.js | 0 .../timepicker/jquery-ui-timepicker-addon.css | 0 .../timepicker/jquery-ui-timepicker-addon.js | 0 .../jquery-ui-timepicker-addon.min.css | 0 .../jquery-ui-timepicker-addon.min.js | 0 .../admin/views/html-admin-navigation.php | 2 +- includes/api/api-helpers.php | 6 +- includes/api/api-template.php | 2 +- includes/fields.php | 0 includes/fields/class-acf-field.php | 0 includes/local-meta.php | 0 lang/acf-ar.mo | Bin lang/acf-cs_CZ.mo | Bin lang/acf-de_CH.mo | Bin lang/acf-de_DE.mo | Bin lang/acf-de_DE_formal.mo | Bin lang/acf-fr_FR.mo | Bin lang/acf-id_ID.mo | Bin lang/acf-it_IT.mo | Bin lang/acf-ja.mo | Bin lang/acf-pt_BR.mo | Bin lang/acf-sk_SK.mo | Bin pro/admin/views/html-settings-updates.php | 2 +- pro/blocks.php | 0 pro/locations/class-acf-location-block.php | 0 pro/updates.php | 2 +- readme.txt | 10 +- 47 files changed, 20781 insertions(+), 17723 deletions(-) mode change 100755 => 100644 assets/inc/select2/3/select2-spinner.gif mode change 100755 => 100644 assets/inc/select2/3/select2.css mode change 100755 => 100644 assets/inc/select2/3/select2.js mode change 100755 => 100644 assets/inc/select2/3/select2.min.js mode change 100755 => 100644 assets/inc/select2/3/select2.png mode change 100755 => 100644 assets/inc/select2/3/select2x2.png mode change 100755 => 100644 assets/inc/select2/4/select2.css mode change 100755 => 100644 assets/inc/select2/4/select2.full.js mode change 100755 => 100644 assets/inc/select2/4/select2.full.min.js mode change 100755 => 100644 assets/inc/select2/4/select2.js mode change 100755 => 100644 assets/inc/select2/4/select2.min.css mode change 100755 => 100644 assets/inc/select2/4/select2.min.js mode change 100755 => 100644 assets/inc/timepicker/jquery-ui-timepicker-addon.css mode change 100755 => 100644 assets/inc/timepicker/jquery-ui-timepicker-addon.js mode change 100755 => 100644 assets/inc/timepicker/jquery-ui-timepicker-addon.min.css mode change 100755 => 100644 assets/inc/timepicker/jquery-ui-timepicker-addon.min.js mode change 100755 => 100644 includes/fields.php mode change 100755 => 100644 includes/fields/class-acf-field.php mode change 100755 => 100644 includes/local-meta.php mode change 100755 => 100644 lang/acf-ar.mo mode change 100755 => 100644 lang/acf-cs_CZ.mo mode change 100755 => 100644 lang/acf-de_CH.mo mode change 100755 => 100644 lang/acf-de_DE.mo mode change 100755 => 100644 lang/acf-de_DE_formal.mo mode change 100755 => 100644 lang/acf-fr_FR.mo mode change 100755 => 100644 lang/acf-id_ID.mo mode change 100755 => 100644 lang/acf-it_IT.mo mode change 100755 => 100644 lang/acf-ja.mo mode change 100755 => 100644 lang/acf-pt_BR.mo mode change 100755 => 100644 lang/acf-sk_SK.mo mode change 100755 => 100644 pro/blocks.php mode change 100755 => 100644 pro/locations/class-acf-location-block.php diff --git a/acf.php b/acf.php index f1b0884..2a71eb4 100644 --- a/acf.php +++ b/acf.php @@ -3,7 +3,7 @@ Plugin Name: Advanced Custom Fields PRO Plugin URI: https://www.advancedcustomfields.com Description: Customize WordPress with powerful, professional and intuitive fields. -Version: 5.9.8 +Version: 5.9.9 Author: Delicious Brains Author URI: https://www.advancedcustomfields.com Text Domain: acf @@ -17,7 +17,7 @@ if( ! class_exists('ACF') ) : class ACF { /** @var string The plugin version number. */ - var $version = '5.9.8'; + var $version = '5.9.9'; /** @var array The plugin settings array. */ var $settings = array(); diff --git a/assets/build/js/acf-field-group.js b/assets/build/js/acf-field-group.js index a29bfdc..50c0de6 100644 --- a/assets/build/js/acf-field-group.js +++ b/assets/build/js/acf-field-group.js @@ -1,2050 +1,2525 @@ -(function ($, undefined) { - /** - * fieldGroupManager - * - * Generic field group functionality - * - * @date 15/12/17 - * @since 5.7.0 - * - * @param void - * @return void - */ - var fieldGroupManager = new acf.Model({ - id: 'fieldGroupManager', - events: { - 'submit #post': 'onSubmit', - 'click a[href="#"]': 'onClick', - 'click .submitdelete': 'onClickTrash' - }, - filters: { - 'find_fields_args': 'filterFindFieldArgs' - }, - onSubmit: function (e, $el) { - // vars - var $title = $('#titlewrap #title'); // empty - - if (!$title.val()) { - // prevent default - e.preventDefault(); // unlock form - - acf.unlockForm($el); // alert - - alert(acf.__('Field group title is required')); // focus - - $title.trigger('focus'); - } - }, - onClick: function (e) { - e.preventDefault(); - }, - onClickTrash: function (e) { - var result = confirm(acf.__('Move to trash. Are you sure?')); - - if (!result) { - e.preventDefault(); - } - }, - filterFindFieldArgs: function (args) { - args.visible = true; - return args; - } - }); - /** - * screenOptionsManager - * - * Screen options functionality - * - * @date 15/12/17 - * @since 5.7.0 - * - * @param void - * @return void - */ - - var screenOptionsManager = new acf.Model({ - id: 'screenOptionsManager', - wait: 'prepare', - events: { - 'change': 'onChange' - }, - initialize: function () { - // vars - var $div = $('#adv-settings'); - var $append = $('#acf-append-show-on-screen'); // append - - $div.find('.metabox-prefs').append($append.html()); - $div.find('.metabox-prefs br').remove(); // clean up - - $append.remove(); // initialize - - this.$el = $('#acf-field-key-hide'); // render - - this.render(); - }, - isChecked: function () { - return this.$el.prop('checked'); - }, - onChange: function (e, $el) { - var val = this.isChecked() ? 1 : 0; - acf.updateUserSetting('show_field_keys', val); - this.render(); - }, - render: function () { - if (this.isChecked()) { - $('#acf-field-group-fields').addClass('show-field-keys'); - } else { - $('#acf-field-group-fields').removeClass('show-field-keys'); - } - } - }); - /** - * appendFieldManager - * - * Appends fields together - * - * @date 15/12/17 - * @since 5.7.0 - * - * @param void - * @return void - */ - - var appendFieldManager = new acf.Model({ - actions: { - 'new_field': 'onNewField' - }, - onNewField: function (field) { - // bail ealry if not append - if (!field.has('append')) return; // vars - - var append = field.get('append'); - var $sibling = field.$el.siblings('[data-name="' + append + '"]').first(); // bail early if no sibling - - if (!$sibling.length) return; // ul - - var $div = $sibling.children('.acf-input'); - var $ul = $div.children('ul'); // create ul - - if (!$ul.length) { - $div.wrapInner(''); - $ul = $div.children('ul'); - } // li - - - var html = field.$('.acf-input').html(); - var $li = $('
  • ' + html + '
  • '); - $ul.append($li); - $ul.attr('data-cols', $ul.children().length); // clean up - - field.remove(); - } - }); +(function($, undefined){ + + /** + * fieldGroupManager + * + * Generic field group functionality + * + * @date 15/12/17 + * @since 5.7.0 + * + * @param void + * @return void + */ + + var fieldGroupManager = new acf.Model({ + + id: 'fieldGroupManager', + + events: { + 'submit #post': 'onSubmit', + 'click a[href="#"]': 'onClick', + 'click .submitdelete': 'onClickTrash', + }, + + filters: { + 'find_fields_args': 'filterFindFieldArgs' + }, + + onSubmit: function( e, $el ){ + + // vars + var $title = $('#titlewrap #title'); + + // empty + if( !$title.val() ) { + + // prevent default + e.preventDefault(); + + // unlock form + acf.unlockForm( $el ); + + // alert + alert( acf.__('Field group title is required') ); + + // focus + $title.trigger('focus'); + } + }, + + onClick: function( e ){ + e.preventDefault(); + }, + + onClickTrash: function( e ){ + var result = confirm( acf.__('Move to trash. Are you sure?') ); + if( !result ) { + e.preventDefault(); + } + }, + + filterFindFieldArgs: function( args ){ + args.visible = true; + return args; + } + }); + + + /** + * screenOptionsManager + * + * Screen options functionality + * + * @date 15/12/17 + * @since 5.7.0 + * + * @param void + * @return void + */ + + var screenOptionsManager = new acf.Model({ + + id: 'screenOptionsManager', + wait: 'prepare', + + events: { + 'change': 'onChange' + }, + + initialize: function(){ + + // vars + var $div = $('#adv-settings'); + var $append = $('#acf-append-show-on-screen'); + + // append + $div.find('.metabox-prefs').append( $append.html() ); + $div.find('.metabox-prefs br').remove(); + + // clean up + $append.remove(); + + // initialize + this.$el = $('#acf-field-key-hide'); + + // render + this.render(); + }, + + isChecked: function(){ + return this.$el.prop('checked'); + }, + + onChange: function( e, $el ) { + var val = this.isChecked() ? 1 : 0; + acf.updateUserSetting('show_field_keys', val); + this.render(); + }, + + render: function(){ + if( this.isChecked() ) { + $('#acf-field-group-fields').addClass('show-field-keys'); + } else { + $('#acf-field-group-fields').removeClass('show-field-keys'); + } + } + + }); + + + /** + * appendFieldManager + * + * Appends fields together + * + * @date 15/12/17 + * @since 5.7.0 + * + * @param void + * @return void + */ + + var appendFieldManager = new acf.Model({ + + actions: { + 'new_field' : 'onNewField' + }, + + onNewField: function( field ){ + + // bail ealry if not append + if( !field.has('append') ) return; + + // vars + var append = field.get('append'); + var $sibling = field.$el.siblings('[data-name="' + append + '"]').first(); + + // bail early if no sibling + if( !$sibling.length ) return; + + // ul + var $div = $sibling.children('.acf-input'); + var $ul = $div.children('ul'); + + // create ul + if( !$ul.length ) { + $div.wrapInner(''); + $ul = $div.children('ul'); + } + + // li + var html = field.$('.acf-input').html(); + var $li = $('
  • ' + html + '
  • '); + $ul.append( $li ); + $ul.attr('data-cols', $ul.children().length ); + + // clean up + field.remove(); + } + }); + })(jQuery); - -(function ($, undefined) { - acf.FieldObject = acf.Model.extend({ - // class used to avoid nested event triggers - eventScope: '.acf-field-object', - // events - events: { - 'click .edit-field': 'onClickEdit', - 'click .delete-field': 'onClickDelete', - 'click .duplicate-field': 'duplicate', - 'click .move-field': 'move', - 'change .field-type': 'onChangeType', - 'change .field-required': 'onChangeRequired', - 'blur .field-label': 'onChangeLabel', - 'blur .field-name': 'onChangeName', - 'change': 'onChange', - 'changed': 'onChanged' - }, - // data - data: { - // Similar to ID, but used for HTML puposes. - // It is possbile for a new field to have an ID of 0, but an id of 'field_123' */ - id: 0, - // The field key ('field_123') - key: '', - // The field type (text, image, etc) - type: '' // The $post->ID of this field - //ID: 0, - // The field's parent - //parent: 0, - // The menu order - //menu_order: 0 - - }, - setup: function ($field) { - // set $el - this.$el = $field; // inherit $field data (id, key, type) - - this.inherit($field); // load additional props - // - this won't trigger 'changed' - - this.prop('ID'); - this.prop('parent'); - this.prop('menu_order'); - }, - $input: function (name) { - return $('#' + this.getInputId() + '-' + name); - }, - $meta: function () { - return this.$('.meta:first'); - }, - $handle: function () { - return this.$('.handle:first'); - }, - $settings: function () { - return this.$('.settings:first'); - }, - $setting: function (name) { - return this.$('.acf-field-settings:first > .acf-field-setting-' + name); - }, - getParent: function () { - return acf.getFieldObjects({ - child: this.$el, - limit: 1 - }).pop(); - }, - getParents: function () { - return acf.getFieldObjects({ - child: this.$el - }); - }, - getFields: function () { - return acf.getFieldObjects({ - parent: this.$el - }); - }, - getInputName: function () { - return 'acf_fields[' + this.get('id') + ']'; - }, - getInputId: function () { - return 'acf_fields-' + this.get('id'); - }, - newInput: function (name, value) { - // vars - var inputId = this.getInputId(); - var inputName = this.getInputName(); // append name - - if (name) { - inputId += '-' + name; - inputName += '[' + name + ']'; - } // create input (avoid HTML + JSON value issues) - - - var $input = $('').attr({ - id: inputId, - name: inputName, - value: value - }); - this.$('> .meta').append($input); // return - - return $input; - }, - getProp: function (name) { - // check data - if (this.has(name)) { - return this.get(name); - } // get input value - - - var $input = this.$input(name); - var value = $input.length ? $input.val() : null; // set data silently (cache) - - this.set(name, value, true); // return - - return value; - }, - setProp: function (name, value) { - // get input - var $input = this.$input(name); - var prevVal = $input.val(); // create if new - - if (!$input.length) { - $input = this.newInput(name, value); - } // remove - - - if (value === null) { - $input.remove(); // update - } else { - $input.val(value); - } //console.log('setProp', name, value, this); - // set data silently (cache) - - - if (!this.has(name)) { - //console.log('setting silently'); - this.set(name, value, true); // set data allowing 'change' event to fire - } else { - //console.log('setting loudly!'); - this.set(name, value); - } // return - - - return this; - }, - prop: function (name, value) { - if (value !== undefined) { - return this.setProp(name, value); - } else { - return this.getProp(name); - } - }, - props: function (props) { - Object.keys(props).map(function (key) { - this.setProp(key, props[key]); - }, this); - }, - getLabel: function () { - // get label with empty default - var label = this.prop('label'); - - if (label === '') { - label = acf.__('(no label)'); - } // return - - - return label; - }, - getName: function () { - return this.prop('name'); - }, - getType: function () { - return this.prop('type'); - }, - getTypeLabel: function () { - var type = this.prop('type'); - var types = acf.get('fieldTypes'); - return types[type] ? types[type].label : type; - }, - getKey: function () { - return this.prop('key'); - }, - initialize: function () { - this.addProFields(); - }, - addProFields: function () { - // Make sure we're only running this on free version. - if (acf.data.fieldTypes.hasOwnProperty('clone')) { - return; - } // Make sure we haven't appended these fields before. - - - var $fieldTypeSelect = $('.field-type').not('.acf-free-field-type'); // Append pro fields to "Layout" group. - - var $layoutGroup = $fieldTypeSelect.find('optgroup option[value="group"]').parent(); - $layoutGroup.append('' + '' + ''); // Add pro fields to "Content" group. - - var $contentGroup = $fieldTypeSelect.find('optgroup option[value="image"]').parent(); - $contentGroup.append(''); - $fieldTypeSelect.addClass('acf-free-field-type'); - }, - render: function () { - // vars - var $handle = this.$('.handle:first'); - var menu_order = this.prop('menu_order'); - var label = this.getLabel(); - var name = this.prop('name'); - var type = this.getTypeLabel(); - var key = this.prop('key'); - var required = this.$input('required').prop('checked'); // update menu order - - $handle.find('.acf-icon').html(parseInt(menu_order) + 1); // update required - - if (required) { - label += ' *'; - } // update label - - - $handle.find('.li-field-label strong a').html(label); // update name - - $handle.find('.li-field-name').text(name); // update type - - $handle.find('.li-field-type').text(type); // update key - - $handle.find('.li-field-key').text(key); // action for 3rd party customization - - acf.doAction('render_field_object', this); - }, - refresh: function () { - acf.doAction('refresh_field_object', this); - }, - isOpen: function () { - return this.$el.hasClass('open'); - }, - onClickEdit: function (e) { - this.isOpen() ? this.close() : this.open(); - }, - open: function () { - // vars - var $settings = this.$el.children('.settings'); // open - - $settings.slideDown(); - this.$el.addClass('open'); // action (open) - - acf.doAction('open_field_object', this); - this.trigger('openFieldObject'); // action (show) - - acf.doAction('show', $settings); - }, - close: function () { - // vars - var $settings = this.$el.children('.settings'); // close - - $settings.slideUp(); - this.$el.removeClass('open'); // action (close) - - acf.doAction('close_field_object', this); - this.trigger('closeFieldObject'); // action (hide) - - acf.doAction('hide', $settings); - }, - serialize: function () { - return acf.serialize(this.$el, this.getInputName()); - }, - save: function (type) { - // defaults - type = type || 'settings'; // meta, settings - // vars - - var save = this.getProp('save'); // bail if already saving settings - - if (save === 'settings') { - return; - } // prop - - - this.setProp('save', type); // debug - - this.$el.attr('data-save', type); // action - - acf.doAction('save_field_object', this, type); - }, - submit: function () { - // vars - var inputName = this.getInputName(); - var save = this.get('save'); // close - - if (this.isOpen()) { - this.close(); - } // allow all inputs to save - - - if (save == 'settings') {// do nothing - // allow only meta inputs to save - } else if (save == 'meta') { - this.$('> .settings [name^="' + inputName + '"]').remove(); // prevent all inputs from saving - } else { - this.$('[name^="' + inputName + '"]').remove(); - } // action - - - acf.doAction('submit_field_object', this); - }, - onChange: function (e, $el) { - // save settings - this.save(); // action for 3rd party customization - - acf.doAction('change_field_object', this); - }, - onChanged: function (e, $el, name, value) { - // ignore 'save' - if (name == 'save') { - return; - } // save meta - - - if (['menu_order', 'parent'].indexOf(name) > -1) { - this.save('meta'); // save field - } else { - this.save(); - } // render - - - if (['menu_order', 'label', 'required', 'name', 'type', 'key'].indexOf(name) > -1) { - this.render(); - } // action for 3rd party customization - - - acf.doAction('change_field_object_' + name, this, value); - }, - onChangeLabel: function (e, $el) { - // set - var label = $el.val(); - this.set('label', label); // render name - - if (this.prop('name') == '') { - var name = acf.applyFilters('generate_field_object_name', acf.strSanitize(label), this); - this.prop('name', name); - } - }, - onChangeName: function (e, $el) { - // set - var name = $el.val(); - this.set('name', name); // error - - if (name.substr(0, 6) === 'field_') { - alert(acf.__('The string "field_" may not be used at the start of a field name')); - } - }, - onChangeRequired: function (e, $el) { - // set - var required = $el.prop('checked') ? 1 : 0; - this.set('required', required); - }, - delete: function (args) { - // defaults - args = acf.parseArgs(args, { - animate: true - }); // add to remove list - - var id = this.prop('ID'); - - if (id) { - var $input = $('#_acf_delete_fields'); - var newVal = $input.val() + '|' + id; - $input.val(newVal); - } // action - - - acf.doAction('delete_field_object', this); // animate - - if (args.animate) { - this.removeAnimate(); - } else { - this.remove(); - } - }, - onClickDelete: function (e, $el) { - // Bypass confirmation when holding down "shift" key. - if (e.shiftKey) { - return this.delete(); - } // add class - - - this.$el.addClass('-hover'); // add tooltip - - var tooltip = acf.newTooltip({ - confirmRemove: true, - target: $el, - context: this, - confirm: function () { - this.delete(); - }, - cancel: function () { - this.$el.removeClass('-hover'); - } - }); - }, - removeAnimate: function () { - // vars - var field = this; - var $list = this.$el.parent(); - var $fields = acf.findFieldObjects({ - sibling: this.$el - }); // remove - - acf.remove({ - target: this.$el, - endHeight: $fields.length ? 0 : 50, - complete: function () { - field.remove(); - acf.doAction('removed_field_object', field, $list); - } - }); // action - - acf.doAction('remove_field_object', field, $list); - }, - duplicate: function () { - // vars - var newKey = acf.uniqid('field_'); // duplicate - - var $newField = acf.duplicate({ - target: this.$el, - search: this.get('id'), - replace: newKey - }); // set new key - - $newField.attr('data-key', newKey); // get instance - - var newField = acf.getFieldObject($newField); // open / close - - if (this.isOpen()) { - this.close(); - } else { - newField.open(); - } // focus label - - - var $label = newField.$setting('label input'); - setTimeout(function () { - $label.trigger('focus'); - }, 251); // update newField label / name - - var label = newField.prop('label'); - var name = newField.prop('name'); - var end = name.split('_').pop(); - - var copy = acf.__('copy'); // increase suffix "1" - - - if (acf.isNumeric(end)) { - var i = end * 1 + 1; - label = label.replace(end, i); - name = name.replace(end, i); // increase suffix "(copy1)" - } else if (end.indexOf(copy) === 0) { - var i = end.replace(copy, '') * 1; - i = i ? i + 1 : 2; // replace - - label = label.replace(end, copy + i); - name = name.replace(end, copy + i); // add default "(copy)" - } else { - label += ' (' + copy + ')'; - name += '_' + copy; - } - - newField.prop('ID', 0); - newField.prop('label', label); - newField.prop('name', name); - newField.prop('key', newKey); // action - - acf.doAction('duplicate_field_object', this, newField); - acf.doAction('append_field_object', newField); - }, - wipe: function () { - // vars - var prevId = this.get('id'); - var prevKey = this.get('key'); - var newKey = acf.uniqid('field_'); // rename - - acf.rename({ - target: this.$el, - search: prevId, - replace: newKey - }); // data - - this.set('id', newKey); - this.set('prevId', prevId); - this.set('prevKey', prevKey); // props - - this.prop('key', newKey); - this.prop('ID', 0); // attr - - this.$el.attr('data-key', newKey); - this.$el.attr('data-id', newKey); // action - - acf.doAction('wipe_field_object', this); - }, - move: function () { - // helper - var hasChanged = function (field) { - return field.get('save') == 'settings'; - }; // vars - - - var changed = hasChanged(this); // has sub fields changed - - if (!changed) { - acf.getFieldObjects({ - parent: this.$el - }).map(function (field) { - changed = hasChanged(field) || field.changed; - }); - } // bail early if changed - - - if (changed) { - alert(acf.__('This field cannot be moved until its changes have been saved')); - return; - } // step 1. - - - var id = this.prop('ID'); - var field = this; - var popup = false; - - var step1 = function () { - // popup - popup = acf.newPopup({ - title: acf.__('Move Custom Field'), - loading: true, - width: '300px' - }); // ajax - - var ajaxData = { - action: 'acf/field_group/move_field', - field_id: id - }; // get HTML - - $.ajax({ - url: acf.get('ajaxurl'), - data: acf.prepareForAjax(ajaxData), - type: 'post', - dataType: 'html', - success: step2 - }); - }; - - var step2 = function (html) { - // update popup - popup.loading(false); - popup.content(html); // submit form - - popup.on('submit', 'form', step3); - }; - - var step3 = function (e, $el) { - // prevent - e.preventDefault(); // disable - - acf.startButtonLoading(popup.$('.button')); // ajax - - var ajaxData = { - action: 'acf/field_group/move_field', - field_id: id, - field_group_id: popup.$('select').val() - }; // get HTML - - $.ajax({ - url: acf.get('ajaxurl'), - data: acf.prepareForAjax(ajaxData), - type: 'post', - dataType: 'html', - success: step4 - }); - }; - - var step4 = function (html) { - // update popup - popup.content(html); // remove element - - field.removeAnimate(); - }; // start - - - step1(); - }, - onChangeType: function (e, $el) { - // clea previous timout - if (this.changeTimeout) { - clearTimeout(this.changeTimeout); - } // set new timeout - // - prevents changing type multiple times whilst user types in newType - - - this.changeTimeout = this.setTimeout(function () { - this.changeType($el.val()); - }, 300); - }, - changeType: function (newType) { - // vars - var prevType = this.prop('type'); - var prevClass = acf.strSlugify('acf-field-object-' + prevType); - var newClass = acf.strSlugify('acf-field-object-' + newType); // update props - - this.$el.removeClass(prevClass).addClass(newClass); - this.$el.attr('data-type', newType); - this.$el.data('type', newType); // abort XHR if this field is already loading AJAX data - - if (this.has('xhr')) { - this.get('xhr').abort(); - } // store settings - - - var $tbody = this.$('> .settings > table > tbody'); - var $settings = $tbody.children('[data-setting="' + prevType + '"]'); - this.set('settings-' + prevType, $settings); - $settings.detach(); // show settings - - if (this.has('settings-' + newType)) { - var $newSettings = this.get('settings-' + newType); - this.$setting('conditional_logic').before($newSettings); - this.set('type', newType); //this.refresh(); - - return; - } // load settings - - - var $loading = $('
    '); - this.$setting('conditional_logic').before($loading); // ajax - - var ajaxData = { - action: 'acf/field_group/render_field_settings', - field: this.serialize(), - prefix: this.getInputName() - }; // ajax - - var xhr = $.ajax({ - url: acf.get('ajaxurl'), - data: acf.prepareForAjax(ajaxData), - type: 'post', - dataType: 'html', - context: this, - success: function (html) { - // bail early if no settings - if (!html) return; // append settings - - $loading.after(html); // events - - acf.doAction('append', $tbody); - }, - complete: function () { - // also triggered by xhr.abort(); - $loading.remove(); - this.set('type', newType); //this.refresh(); - } - }); // set - - this.set('xhr', xhr); - }, - updateParent: function () { - // vars - var ID = acf.get('post_id'); // check parent - - var parent = this.getParent(); - - if (parent) { - ID = parseInt(parent.prop('ID')) || parent.prop('key'); - } // update - - - this.prop('parent', ID); - } - }); +(function($, undefined){ + + acf.FieldObject = acf.Model.extend({ + + // class used to avoid nested event triggers + eventScope: '.acf-field-object', + + // events + events: { + 'click .edit-field': 'onClickEdit', + 'click .delete-field': 'onClickDelete', + 'click .duplicate-field': 'duplicate', + 'click .move-field': 'move', + + 'change .field-type': 'onChangeType', + 'change .field-required': 'onChangeRequired', + 'blur .field-label': 'onChangeLabel', + 'blur .field-name': 'onChangeName', + + 'change': 'onChange', + 'changed': 'onChanged', + }, + + // data + data: { + + // Similar to ID, but used for HTML puposes. + // It is possbile for a new field to have an ID of 0, but an id of 'field_123' */ + id: 0, + + // The field key ('field_123') + key: '', + + // The field type (text, image, etc) + type: '', + + // The $post->ID of this field + //ID: 0, + + // The field's parent + //parent: 0, + + // The menu order + //menu_order: 0 + }, + + setup: function( $field ){ + + // set $el + this.$el = $field; + + // inherit $field data (id, key, type) + this.inherit( $field ); + + // load additional props + // - this won't trigger 'changed' + this.prop('ID'); + this.prop('parent'); + this.prop('menu_order'); + }, + + $input: function( name ){ + return $('#' + this.getInputId() + '-' + name); + }, + + $meta: function(){ + return this.$('.meta:first'); + }, + + $handle: function(){ + return this.$('.handle:first'); + }, + + $settings: function(){ + return this.$('.settings:first'); + }, + + $setting: function( name ){ + return this.$('.acf-field-settings:first > .acf-field-setting-' + name); + }, + + getParent: function(){ + return acf.getFieldObjects({ child: this.$el, limit: 1 }).pop(); + }, + + getParents: function(){ + return acf.getFieldObjects({ child: this.$el }); + }, + + getFields: function(){ + return acf.getFieldObjects({ parent: this.$el }); + }, + + getInputName: function(){ + return 'acf_fields[' + this.get('id') + ']'; + }, + + getInputId: function(){ + return 'acf_fields-' + this.get('id'); + }, + + newInput: function( name, value ){ + + // vars + var inputId = this.getInputId(); + var inputName = this.getInputName(); + + // append name + if( name ) { + inputId += '-'+name; + inputName += '['+name+']'; + } + + // create input (avoid HTML + JSON value issues) + var $input = $('').attr({ + id: inputId, + name: inputName, + value: value + }); + this.$('> .meta').append( $input ); + + // return + return $input; + }, + + getProp: function( name ){ + + // check data + if( this.has(name) ) { + return this.get(name); + } + + // get input value + var $input = this.$input( name ); + var value = $input.length ? $input.val() : null; + + // set data silently (cache) + this.set(name, value, true); + + // return + return value; + }, + + setProp: function( name, value ) { + + // get input + var $input = this.$input( name ); + var prevVal = $input.val(); + + // create if new + if( !$input.length ) { + $input = this.newInput( name, value ); + } + + // remove + if( value === null ) { + $input.remove(); + + // update + } else { + $input.val( value ); + } + + //console.log('setProp', name, value, this); + + // set data silently (cache) + if( !this.has(name) ) { + //console.log('setting silently'); + this.set(name, value, true); + + // set data allowing 'change' event to fire + } else { + //console.log('setting loudly!'); + this.set(name, value); + } + + // return + return this; + + }, + + prop: function( name, value ){ + if( value !== undefined ) { + return this.setProp( name, value ); + } else { + return this.getProp( name ); + } + }, + + props: function( props ){ + Object.keys( props ).map(function( key ){ + this.setProp( key, props[key] ); + }, this); + }, + + getLabel: function(){ + + // get label with empty default + var label = this.prop('label'); + if( label === '' ) { + label = acf.__('(no label)') + } + + // return + return label; + }, + + getName: function(){ + return this.prop('name'); + }, + + getType: function(){ + return this.prop('type'); + }, + + getTypeLabel: function(){ + var type = this.prop('type'); + var types = acf.get('fieldTypes'); + return ( types[type] ) ? types[type].label : type; + }, + + getKey: function(){ + return this.prop('key'); + }, + + initialize: function(){ + this.addProFields(); + }, + + addProFields: function() { + // Make sure we're only running this on free version. + if (acf.data.fieldTypes.hasOwnProperty('clone')) { + return; + } + + // Make sure we haven't appended these fields before. + var $fieldTypeSelect = $('.field-type').not('.acf-free-field-type'); + + // Append pro fields to "Layout" group. + var $layoutGroup = $fieldTypeSelect.find('optgroup option[value="group"]').parent(); + $layoutGroup.append( + '' + + '' + + '' + ); + + // Add pro fields to "Content" group. + var $contentGroup = $fieldTypeSelect.find('optgroup option[value="image"]').parent(); + $contentGroup.append( + '' + ); + + $fieldTypeSelect.addClass('acf-free-field-type'); + }, + + render: function(){ + + // vars + var $handle = this.$('.handle:first'); + var menu_order = this.prop('menu_order'); + var label = this.getLabel(); + var name = this.prop('name'); + var type = this.getTypeLabel(); + var key = this.prop('key'); + var required = this.$input('required').prop('checked'); + + // update menu order + $handle.find('.acf-icon').html( parseInt(menu_order) + 1 ); + + // update required + if( required ) { + label += ' *'; + } + + // update label + $handle.find('.li-field-label strong a').html( label ); + + // update name + $handle.find('.li-field-name').text( name ); + + // update type + $handle.find('.li-field-type').text( type ); + + // update key + $handle.find('.li-field-key').text( key ); + + // action for 3rd party customization + acf.doAction('render_field_object', this); + }, + + refresh: function(){ + acf.doAction('refresh_field_object', this); + }, + + isOpen: function() { + return this.$el.hasClass('open'); + }, + + onClickEdit: function( e ){ + this.isOpen() ? this.close() : this.open(); + }, + + open: function(){ + + // vars + var $settings = this.$el.children('.settings'); + + // open + $settings.slideDown(); + this.$el.addClass('open'); + + // action (open) + acf.doAction('open_field_object', this); + this.trigger('openFieldObject'); + + // action (show) + acf.doAction('show', $settings); + }, + + close: function(){ + + // vars + var $settings = this.$el.children('.settings'); + + // close + $settings.slideUp(); + this.$el.removeClass('open'); + + // action (close) + acf.doAction('close_field_object', this); + this.trigger('closeFieldObject'); + + // action (hide) + acf.doAction('hide', $settings); + }, + + serialize: function(){ + return acf.serialize( this.$el, this.getInputName() ); + }, + + save: function( type ){ + + // defaults + type = type || 'settings'; // meta, settings + + // vars + var save = this.getProp('save'); + + // bail if already saving settings + if( save === 'settings' ) { + return; + } + + // prop + this.setProp('save', type); + + // debug + this.$el.attr('data-save', type); + + // action + acf.doAction('save_field_object', this, type); + }, + + submit: function(){ + + // vars + var inputName = this.getInputName(); + var save = this.get('save'); + + // close + if( this.isOpen() ) { + this.close(); + } + + // allow all inputs to save + if( save == 'settings' ) { + // do nothing + + // allow only meta inputs to save + } else if( save == 'meta' ) { + this.$('> .settings [name^="' + inputName + '"]').remove(); + + // prevent all inputs from saving + } else { + this.$('[name^="' + inputName + '"]').remove(); + } + + // action + acf.doAction('submit_field_object', this); + }, + + onChange: function( e, $el ){ + + // save settings + this.save(); + + // action for 3rd party customization + acf.doAction('change_field_object', this); + }, + + onChanged: function( e, $el, name, value ){ + + // ignore 'save' + if( name == 'save' ) { + return; + } + + // save meta + if( ['menu_order', 'parent'].indexOf(name) > -1 ) { + this.save('meta'); + + // save field + } else { + this.save(); + } + + // render + if( ['menu_order', 'label', 'required', 'name', 'type', 'key'].indexOf(name) > -1 ) { + this.render(); + } + + // action for 3rd party customization + acf.doAction('change_field_object_' + name, this, value); + }, + + onChangeLabel: function( e, $el ){ + + // set + var label = $el.val(); + this.set('label', label); + + // render name + if( this.prop('name') == '' ) { + var name = acf.applyFilters('generate_field_object_name', acf.strSanitize(label), this); + this.prop('name', name); + } + }, + + onChangeName: function( e, $el){ + + // set + var name = $el.val(); + this.set('name', name); + + // error + if( name.substr(0, 6) === 'field_' ) { + alert( acf.__('The string "field_" may not be used at the start of a field name') ); + } + }, + + onChangeRequired: function( e, $el ){ + + // set + var required = $el.prop('checked') ? 1 : 0; + this.set('required', required); + }, + + delete: function( args ){ + + // defaults + args = acf.parseArgs(args, { + animate: true + }); + + // add to remove list + var id = this.prop('ID'); + + if( id ) { + var $input = $('#_acf_delete_fields'); + var newVal = $input.val() + '|' + id; + $input.val( newVal ); + } + + // action + acf.doAction('delete_field_object', this); + + // animate + if( args.animate ) { + this.removeAnimate(); + } else { + this.remove(); + } + }, + + onClickDelete: function( e, $el ){ + + // Bypass confirmation when holding down "shift" key. + if( e.shiftKey ) { + return this.delete(); + } + + // add class + this.$el.addClass('-hover'); + + // add tooltip + var tooltip = acf.newTooltip({ + confirmRemove: true, + target: $el, + context: this, + confirm: function(){ + this.delete(); + }, + cancel: function(){ + this.$el.removeClass('-hover'); + } + }); + }, + + removeAnimate: function(){ + + // vars + var field = this; + var $list = this.$el.parent(); + var $fields = acf.findFieldObjects({ + sibling: this.$el + }); + + // remove + acf.remove({ + target: this.$el, + endHeight: $fields.length ? 0 : 50, + complete: function(){ + field.remove(); + acf.doAction('removed_field_object', field, $list); + } + }); + + // action + acf.doAction('remove_field_object', field, $list); + }, + + duplicate: function(){ + + // vars + var newKey = acf.uniqid('field_'); + + // duplicate + var $newField = acf.duplicate({ + target: this.$el, + search: this.get('id'), + replace: newKey, + }); + + // set new key + $newField.attr('data-key', newKey); + + // get instance + var newField = acf.getFieldObject( $newField ); + + // open / close + if( this.isOpen() ) { + this.close(); + } else { + newField.open(); + } + + // focus label + var $label = newField.$setting('label input'); + setTimeout(function(){ + $label.trigger('focus'); + }, 251); + + // update newField label / name + var label = newField.prop('label'); + var name = newField.prop('name'); + var end = name.split('_').pop(); + var copy = acf.__('copy'); + + // increase suffix "1" + if( acf.isNumeric(end) ) { + var i = (end*1) + 1; + label = label.replace( end, i ); + name = name.replace( end, i ); + + // increase suffix "(copy1)" + } else if( end.indexOf(copy) === 0 ) { + var i = end.replace(copy, '') * 1; + i = i ? i+1 : 2; + + // replace + label = label.replace( end, copy + i ); + name = name.replace( end, copy + i ); + + // add default "(copy)" + } else { + label += ' (' + copy + ')'; + name += '_' + copy; + } + + newField.prop('ID', 0); + newField.prop('label', label); + newField.prop('name', name); + newField.prop('key', newKey); + + // action + acf.doAction('duplicate_field_object', this, newField); + acf.doAction('append_field_object', newField); + }, + + wipe: function(){ + + // vars + var prevId = this.get('id'); + var prevKey = this.get('key'); + var newKey = acf.uniqid('field_'); + + // rename + acf.rename({ + target: this.$el, + search: prevId, + replace: newKey, + }); + + // data + this.set('id', newKey); + this.set('prevId', prevId); + this.set('prevKey', prevKey); + + // props + this.prop('key', newKey); + this.prop('ID', 0); + + // attr + this.$el.attr('data-key', newKey); + this.$el.attr('data-id', newKey); + + // action + acf.doAction('wipe_field_object', this); + }, + + move: function(){ + + // helper + var hasChanged = function( field ){ + return (field.get('save') == 'settings'); + }; + + // vars + var changed = hasChanged(this); + + // has sub fields changed + if( !changed ) { + acf.getFieldObjects({ + parent: this.$el + }).map(function( field ){ + changed = hasChanged(field) || field.changed; + }); + } + + // bail early if changed + if( changed ) { + alert( acf.__('This field cannot be moved until its changes have been saved') ); + return; + } + + // step 1. + var id = this.prop('ID'); + var field = this; + var popup = false; + var step1 = function(){ + + // popup + popup = acf.newPopup({ + title: acf.__('Move Custom Field'), + loading: true, + width: '300px' + }); + + // ajax + var ajaxData = { + action: 'acf/field_group/move_field', + field_id: id + }; + + // get HTML + $.ajax({ + url: acf.get('ajaxurl'), + data: acf.prepareForAjax(ajaxData), + type: 'post', + dataType: 'html', + success: step2 + }); + }; + + var step2 = function( html ){ + + // update popup + popup.loading(false); + popup.content(html); + + // submit form + popup.on('submit', 'form', step3); + }; + + var step3 = function( e, $el ){ + + // prevent + e.preventDefault(); + + // disable + acf.startButtonLoading( popup.$('.button') ); + + // ajax + var ajaxData = { + action: 'acf/field_group/move_field', + field_id: id, + field_group_id: popup.$('select').val() + }; + + // get HTML + $.ajax({ + url: acf.get('ajaxurl'), + data: acf.prepareForAjax(ajaxData), + type: 'post', + dataType: 'html', + success: step4 + }); + }; + + var step4 = function( html ){ + + // update popup + popup.content(html); + + // remove element + field.removeAnimate(); + }; + + // start + step1(); + + }, + + onChangeType: function( e, $el ){ + + // clea previous timout + if( this.changeTimeout ) { + clearTimeout(this.changeTimeout); + } + + // set new timeout + // - prevents changing type multiple times whilst user types in newType + this.changeTimeout = this.setTimeout(function(){ + this.changeType( $el.val() ); + }, 300); + }, + + changeType: function( newType ){ + + // vars + var prevType = this.prop('type'); + var prevClass = acf.strSlugify( 'acf-field-object-' + prevType ); + var newClass = acf.strSlugify( 'acf-field-object-' + newType ); + + // update props + this.$el.removeClass(prevClass).addClass(newClass); + this.$el.attr('data-type', newType); + this.$el.data('type', newType); + + // abort XHR if this field is already loading AJAX data + if( this.has('xhr') ) { + this.get('xhr').abort(); + } + + // store settings + var $tbody = this.$('> .settings > table > tbody'); + var $settings = $tbody.children('[data-setting="' + prevType + '"]'); + this.set( 'settings-' + prevType, $settings ); + $settings.detach(); + + // show settings + if( this.has('settings-' + newType) ) { + var $newSettings = this.get('settings-' + newType); + this.$setting('conditional_logic').before( $newSettings ); + this.set('type', newType); + //this.refresh(); + return; + } + + // load settings + var $loading = $('
    '); + this.$setting('conditional_logic').before( $loading ); + + // ajax + var ajaxData = { + action: 'acf/field_group/render_field_settings', + field: this.serialize(), + prefix: this.getInputName() + }; + + // ajax + var xhr = $.ajax({ + url: acf.get('ajaxurl'), + data: acf.prepareForAjax(ajaxData), + type: 'post', + dataType: 'html', + context: this, + success: function( html ){ + + // bail early if no settings + if( !html ) return; + + // append settings + $loading.after( html ); + + // events + acf.doAction('append', $tbody); + }, + complete: function(){ + // also triggered by xhr.abort(); + $loading.remove(); + this.set('type', newType); + //this.refresh(); + } + }); + + // set + this.set('xhr', xhr); + + }, + + updateParent: function(){ + + // vars + var ID = acf.get('post_id'); + + // check parent + var parent = this.getParent(); + if( parent ) { + ID = parseInt(parent.prop('ID')) || parent.prop('key'); + } + + // update + this.prop('parent', ID); + } + + }); + })(jQuery); +(function($, undefined){ + + /** + * mid + * + * Calculates the model ID for a field type + * + * @date 15/12/17 + * @since 5.6.5 + * + * @param string type + * @return string + */ + + var modelId = function( type ) { + return acf.strPascalCase( type || '' ) + 'FieldSetting'; + }; + + /** + * registerFieldType + * + * description + * + * @date 14/12/17 + * @since 5.6.5 + * + * @param type $var Description. Default. + * @return type Description. + */ + + acf.registerFieldSetting = function( model ){ + var proto = model.prototype; + var mid = modelId(proto.type + ' ' + proto.name); + this.models[ mid ] = model; + }; + + /** + * newField + * + * description + * + * @date 14/12/17 + * @since 5.6.5 + * + * @param type $var Description. Default. + * @return type Description. + */ + + acf.newFieldSetting = function( field ){ + + // vars + var type = field.get('setting') || ''; + var name = field.get('name') || ''; + var mid = modelId( type + ' ' + name ); + var model = acf.models[ mid ] || null; + + // bail ealry if no setting + if( model === null ) return false; + + // instantiate + var setting = new model( field ); + + // return + return setting; + }; + + /** + * acf.getFieldSetting + * + * description + * + * @date 19/4/18 + * @since 5.6.9 + * + * @param type $var Description. Default. + * @return type Description. + */ + + acf.getFieldSetting = function( field ) { + + // allow jQuery + if( field instanceof jQuery ) { + field = acf.getField(field); + } + + // return + return field.setting; + }; + + /** + * settingsManager + * + * description + * + * @date 6/1/18 + * @since 5.6.5 + * + * @param type $var Description. Default. + * @return type Description. + */ + + var settingsManager = new acf.Model({ + actions: { + 'new_field': 'onNewField' + }, + onNewField: function( field ){ + field.setting = acf.newFieldSetting( field ); + } + }); + + /** + * acf.FieldSetting + * + * description + * + * @date 6/1/18 + * @since 5.6.5 + * + * @param type $var Description. Default. + * @return type Description. + */ + + acf.FieldSetting = acf.Model.extend({ -(function ($, undefined) { - /** - * mid - * - * Calculates the model ID for a field type - * - * @date 15/12/17 - * @since 5.6.5 - * - * @param string type - * @return string - */ - var modelId = function (type) { - return acf.strPascalCase(type || '') + 'FieldSetting'; - }; - /** - * registerFieldType - * - * description - * - * @date 14/12/17 - * @since 5.6.5 - * - * @param type $var Description. Default. - * @return type Description. - */ - - - acf.registerFieldSetting = function (model) { - var proto = model.prototype; - var mid = modelId(proto.type + ' ' + proto.name); - this.models[mid] = model; - }; - /** - * newField - * - * description - * - * @date 14/12/17 - * @since 5.6.5 - * - * @param type $var Description. Default. - * @return type Description. - */ - - - acf.newFieldSetting = function (field) { - // vars - var type = field.get('setting') || ''; - var name = field.get('name') || ''; - var mid = modelId(type + ' ' + name); - var model = acf.models[mid] || null; // bail ealry if no setting - - if (model === null) return false; // instantiate - - var setting = new model(field); // return - - return setting; - }; - /** - * acf.getFieldSetting - * - * description - * - * @date 19/4/18 - * @since 5.6.9 - * - * @param type $var Description. Default. - * @return type Description. - */ - - - acf.getFieldSetting = function (field) { - // allow jQuery - if (field instanceof jQuery) { - field = acf.getField(field); - } // return - - - return field.setting; - }; - /** - * settingsManager - * - * description - * - * @date 6/1/18 - * @since 5.6.5 - * - * @param type $var Description. Default. - * @return type Description. - */ - - - var settingsManager = new acf.Model({ - actions: { - 'new_field': 'onNewField' - }, - onNewField: function (field) { - field.setting = acf.newFieldSetting(field); - } - }); - /** - * acf.FieldSetting - * - * description - * - * @date 6/1/18 - * @since 5.6.5 - * - * @param type $var Description. Default. - * @return type Description. - */ - - acf.FieldSetting = acf.Model.extend({ - field: false, - type: '', - name: '', - wait: 'ready', - eventScope: '.acf-field', - events: { - 'change': 'render' - }, - setup: function (field) { - // vars - var $field = field.$el; // set props - - this.$el = $field; - this.field = field; - this.$fieldObject = $field.closest('.acf-field-object'); - this.fieldObject = acf.getFieldObject(this.$fieldObject); // inherit data - - $.extend(this.data, field.data); - }, - initialize: function () { - this.render(); - }, - render: function () {// do nothing - } - }); - /* - * Date Picker - * - * This field type requires some extra logic for its settings - * - * @type function - * @date 24/10/13 - * @since 5.0.0 - * - * @param n/a - * @return n/a - */ - - var DisplayFormatFieldSetting = acf.FieldSetting.extend({ - type: '', - name: '', - render: function () { - var $input = this.$('input[type="radio"]:checked'); - - if ($input.val() != 'other') { - this.$('input[type="text"]').val($input.val()); - } - } - }); - var DatePickerDisplayFormatFieldSetting = DisplayFormatFieldSetting.extend({ - type: 'date_picker', - name: 'display_format' - }); - var DatePickerReturnFormatFieldSetting = DisplayFormatFieldSetting.extend({ - type: 'date_picker', - name: 'return_format' - }); - acf.registerFieldSetting(DatePickerDisplayFormatFieldSetting); - acf.registerFieldSetting(DatePickerReturnFormatFieldSetting); - /* - * Date Time Picker - * - * This field type requires some extra logic for its settings - * - * @type function - * @date 24/10/13 - * @since 5.0.0 - * - * @param n/a - * @return n/a - */ - - var DateTimePickerDisplayFormatFieldSetting = DisplayFormatFieldSetting.extend({ - type: 'date_time_picker', - name: 'display_format' - }); - var DateTimePickerReturnFormatFieldSetting = DisplayFormatFieldSetting.extend({ - type: 'date_time_picker', - name: 'return_format' - }); - acf.registerFieldSetting(DateTimePickerDisplayFormatFieldSetting); - acf.registerFieldSetting(DateTimePickerReturnFormatFieldSetting); - /* - * Time Picker - * - * This field type requires some extra logic for its settings - * - * @type function - * @date 24/10/13 - * @since 5.0.0 - * - * @param n/a - * @return n/a - */ - - var TimePickerDisplayFormatFieldSetting = DisplayFormatFieldSetting.extend({ - type: 'time_picker', - name: 'display_format' - }); - var TimePickerReturnFormatFieldSetting = DisplayFormatFieldSetting.extend({ - name: 'time_picker', - name: 'return_format' - }); - acf.registerFieldSetting(TimePickerDisplayFormatFieldSetting); - acf.registerFieldSetting(TimePickerReturnFormatFieldSetting); + field: false, + type: '', + name: '', + wait: 'ready', + eventScope: '.acf-field', + + events: { + 'change': 'render' + }, + + setup: function( field ){ + + // vars + var $field = field.$el; + + // set props + this.$el = $field; + this.field = field; + this.$fieldObject = $field.closest('.acf-field-object'); + this.fieldObject = acf.getFieldObject( this.$fieldObject ); + + // inherit data + $.extend(this.data, field.data); + }, + + initialize: function(){ + this.render(); + }, + + render: function(){ + // do nothing + } + }); + + /* + * Date Picker + * + * This field type requires some extra logic for its settings + * + * @type function + * @date 24/10/13 + * @since 5.0.0 + * + * @param n/a + * @return n/a + */ + + var DisplayFormatFieldSetting = acf.FieldSetting.extend({ + type: '', + name: '', + render: function(){ + var $input = this.$('input[type="radio"]:checked'); + if( $input.val() != 'other' ) { + this.$('input[type="text"]').val( $input.val() ); + } + } + }); + + var DatePickerDisplayFormatFieldSetting = DisplayFormatFieldSetting.extend({ + type: 'date_picker', + name: 'display_format' + }); + + var DatePickerReturnFormatFieldSetting = DisplayFormatFieldSetting.extend({ + type: 'date_picker', + name: 'return_format' + }); + + acf.registerFieldSetting( DatePickerDisplayFormatFieldSetting ); + acf.registerFieldSetting( DatePickerReturnFormatFieldSetting ); + + /* + * Date Time Picker + * + * This field type requires some extra logic for its settings + * + * @type function + * @date 24/10/13 + * @since 5.0.0 + * + * @param n/a + * @return n/a + */ + + var DateTimePickerDisplayFormatFieldSetting = DisplayFormatFieldSetting.extend({ + type: 'date_time_picker', + name: 'display_format' + }); + + var DateTimePickerReturnFormatFieldSetting = DisplayFormatFieldSetting.extend({ + type: 'date_time_picker', + name: 'return_format' + }); + + acf.registerFieldSetting( DateTimePickerDisplayFormatFieldSetting ); + acf.registerFieldSetting( DateTimePickerReturnFormatFieldSetting ); + + /* + * Time Picker + * + * This field type requires some extra logic for its settings + * + * @type function + * @date 24/10/13 + * @since 5.0.0 + * + * @param n/a + * @return n/a + */ + + var TimePickerDisplayFormatFieldSetting = DisplayFormatFieldSetting.extend({ + type: 'time_picker', + name: 'display_format' + }); + + var TimePickerReturnFormatFieldSetting = DisplayFormatFieldSetting.extend({ + name: 'time_picker', + name: 'return_format' + }); + + acf.registerFieldSetting( TimePickerDisplayFormatFieldSetting ); + acf.registerFieldSetting( TimePickerReturnFormatFieldSetting ); + })(jQuery); - -(function ($, undefined) { - /** - * ConditionalLogicFieldSetting - * - * description - * - * @date 3/2/18 - * @since 5.6.5 - * - * @param type $var Description. Default. - * @return type Description. - */ - var ConditionalLogicFieldSetting = acf.FieldSetting.extend({ - type: '', - name: 'conditional_logic', - events: { - 'change .conditions-toggle': 'onChangeToggle', - 'click .add-conditional-group': 'onClickAddGroup', - 'focus .condition-rule-field': 'onFocusField', - 'change .condition-rule-field': 'onChangeField', - 'change .condition-rule-operator': 'onChangeOperator', - 'click .add-conditional-rule': 'onClickAdd', - 'click .remove-conditional-rule': 'onClickRemove' - }, - $rule: false, - scope: function ($rule) { - this.$rule = $rule; - return this; - }, - ruleData: function (name, value) { - return this.$rule.data.apply(this.$rule, arguments); - }, - $input: function (name) { - return this.$rule.find('.condition-rule-' + name); - }, - $td: function (name) { - return this.$rule.find('td.' + name); - }, - $toggle: function () { - return this.$('.conditions-toggle'); - }, - $control: function () { - return this.$('.rule-groups'); - }, - $groups: function () { - return this.$('.rule-group'); - }, - $rules: function () { - return this.$('.rule'); - }, - open: function () { - var $div = this.$control(); - $div.show(); - acf.enable($div); - }, - close: function () { - var $div = this.$control(); - $div.hide(); - acf.disable($div); - }, - render: function () { - // show - if (this.$toggle().prop('checked')) { - this.renderRules(); - this.open(); // hide - } else { - this.close(); - } - }, - renderRules: function () { - // vars - var self = this; // loop - - this.$rules().each(function () { - self.renderRule($(this)); - }); - }, - renderRule: function ($rule) { - this.scope($rule); - this.renderField(); - this.renderOperator(); - this.renderValue(); - }, - renderField: function () { - // vars - var choices = []; - var validFieldTypes = []; - var cid = this.fieldObject.cid; - var $select = this.$input('field'); // loop - - acf.getFieldObjects().map(function (fieldObject) { - // vars - var choice = { - id: fieldObject.getKey(), - text: fieldObject.getLabel() - }; // bail early if is self - - if (fieldObject.cid === cid) { - choice.text += acf.__('(this field)'); - choice.disabled = true; - } // get selected field conditions - - - var conditionTypes = acf.getConditionTypes({ - fieldType: fieldObject.getType() - }); // bail early if no types - - if (!conditionTypes.length) { - choice.disabled = true; - } // calulate indents - - - var indents = fieldObject.getParents().length; - choice.text = '- '.repeat(indents) + choice.text; // append - - choices.push(choice); - }); // allow for scenario where only one field exists - - if (!choices.length) { - choices.push({ - id: '', - text: acf.__('No toggle fields available') - }); - } // render - - - acf.renderSelect($select, choices); // set - - this.ruleData('field', $select.val()); - }, - renderOperator: function () { - // bail early if no field selected - if (!this.ruleData('field')) { - return; - } // vars - - - var $select = this.$input('operator'); - var val = $select.val(); - var choices = []; // set saved value on first render - // - this allows the 2nd render to correctly select an option - - if ($select.val() === null) { - acf.renderSelect($select, [{ - id: this.ruleData('operator'), - text: '' - }]); - } // get selected field - - - var $field = acf.findFieldObject(this.ruleData('field')); - var field = acf.getFieldObject($field); // get selected field conditions - - var conditionTypes = acf.getConditionTypes({ - fieldType: field.getType() - }); // html - - conditionTypes.map(function (model) { - choices.push({ - id: model.prototype.operator, - text: model.prototype.label - }); - }); // render - - acf.renderSelect($select, choices); // set - - this.ruleData('operator', $select.val()); - }, - renderValue: function () { - // bail early if no field selected - if (!this.ruleData('field') || !this.ruleData('operator')) { - return; - } // vars - - - var $select = this.$input('value'); - var $td = this.$td('value'); - var val = $select.val(); // get selected field - - var $field = acf.findFieldObject(this.ruleData('field')); - var field = acf.getFieldObject($field); // get selected field conditions - - var conditionTypes = acf.getConditionTypes({ - fieldType: field.getType(), - operator: this.ruleData('operator') - }); // html - - var conditionType = conditionTypes[0].prototype; - var choices = conditionType.choices(field); // create html: array - - if (choices instanceof Array) { - var $newSelect = $(''); - acf.renderSelect($newSelect, choices); // create html: string () - } else { - var $newSelect = $(choices); - } // append - - - $select.detach(); - $td.html($newSelect); // copy attrs - // timeout needed to avoid browser bug where "disabled" attribute is not applied - - setTimeout(function () { - ['class', 'name', 'id'].map(function (attr) { - $newSelect.attr(attr, $select.attr(attr)); - }); - }, 0); // select existing value (if not a disabled input) - - if (!$newSelect.prop('disabled')) { - acf.val($newSelect, val, true); - } // set - - - this.ruleData('value', $newSelect.val()); - }, - onChangeToggle: function () { - this.render(); - }, - onClickAddGroup: function (e, $el) { - this.addGroup(); - }, - addGroup: function () { - // vars - var $group = this.$('.rule-group:last'); // duplicate - - var $group2 = acf.duplicate($group); // update h4 - - $group2.find('h4').text(acf.__('or')); // remove all tr's except the first one - - $group2.find('tr').not(':first').remove(); // save field - - this.fieldObject.save(); - }, - onFocusField: function (e, $el) { - this.renderField(); - }, - onChangeField: function (e, $el) { - // scope - this.scope($el.closest('.rule')); // set data - - this.ruleData('field', $el.val()); // render - - this.renderOperator(); - this.renderValue(); - }, - onChangeOperator: function (e, $el) { - // scope - this.scope($el.closest('.rule')); // set data - - this.ruleData('operator', $el.val()); // render - - this.renderValue(); - }, - onClickAdd: function (e, $el) { - // duplciate - var $rule = acf.duplicate($el.closest('.rule')); // render - - this.renderRule($rule); - }, - onClickRemove: function (e, $el) { - // vars - var $rule = $el.closest('.rule'); // save field - - this.fieldObject.save(); // remove group - - if ($rule.siblings('.rule').length == 0) { - $rule.closest('.rule-group').remove(); - } // remove - - - $rule.remove(); - } - }); - acf.registerFieldSetting(ConditionalLogicFieldSetting); - /** - * conditionalLogicHelper - * - * description - * - * @date 20/4/18 - * @since 5.6.9 - * - * @param type $var Description. Default. - * @return type Description. - */ - - var conditionalLogicHelper = new acf.Model({ - actions: { - 'duplicate_field_objects': 'onDuplicateFieldObjects' - }, - onDuplicateFieldObjects: function (children, newField, prevField) { - // vars - var data = {}; - var $selects = $(); // reference change in key - - children.map(function (child) { - // store reference of changed key - data[child.get('prevKey')] = child.get('key'); // append condition select - - $selects = $selects.add(child.$('.condition-rule-field')); - }); // loop - - $selects.each(function () { - // vars - var $select = $(this); - var val = $select.val(); // bail early if val is not a ref key - - if (!val || !data[val]) { - return; - } // modify selected option - - - $select.find('option:selected').attr('value', data[val]); // set new val - - $select.val(data[val]); - }); - } - }); +(function($, undefined){ + + /** + * ConditionalLogicFieldSetting + * + * description + * + * @date 3/2/18 + * @since 5.6.5 + * + * @param type $var Description. Default. + * @return type Description. + */ + + var ConditionalLogicFieldSetting = acf.FieldSetting.extend({ + type: '', + name: 'conditional_logic', + events: { + 'change .conditions-toggle': 'onChangeToggle', + 'click .add-conditional-group': 'onClickAddGroup', + 'focus .condition-rule-field': 'onFocusField', + 'change .condition-rule-field': 'onChangeField', + 'change .condition-rule-operator': 'onChangeOperator', + 'click .add-conditional-rule': 'onClickAdd', + 'click .remove-conditional-rule': 'onClickRemove' + }, + + $rule: false, + + scope: function( $rule ){ + this.$rule = $rule; + return this; + }, + + ruleData: function( name, value ){ + return this.$rule.data.apply( this.$rule, arguments ); + }, + + $input: function( name ){ + return this.$rule.find('.condition-rule-' + name); + }, + + $td: function( name ){ + return this.$rule.find('td.' + name); + }, + + $toggle: function(){ + return this.$('.conditions-toggle'); + }, + + $control: function(){ + return this.$('.rule-groups'); + }, + + $groups: function(){ + return this.$('.rule-group'); + }, + + $rules: function(){ + return this.$('.rule'); + }, + + open: function(){ + var $div = this.$control(); + $div.show(); + acf.enable( $div ); + }, + + close: function(){ + var $div = this.$control(); + $div.hide(); + acf.disable( $div ); + }, + + render: function(){ + + // show + if( this.$toggle().prop('checked') ) { + this.renderRules(); + this.open(); + + // hide + } else { + this.close(); + } + }, + + renderRules: function(){ + + // vars + var self = this; + + // loop + this.$rules().each(function(){ + self.renderRule( $(this) ); + }); + }, + + renderRule: function( $rule ){ + this.scope( $rule ); + this.renderField(); + this.renderOperator(); + this.renderValue(); + }, + + renderField: function(){ + + // vars + var choices = []; + var validFieldTypes = []; + var cid = this.fieldObject.cid; + var $select = this.$input('field'); + + // loop + acf.getFieldObjects().map(function( fieldObject ){ + + // vars + var choice = { + id: fieldObject.getKey(), + text: fieldObject.getLabel() + }; + + // bail early if is self + if( fieldObject.cid === cid ) { + choice.text += acf.__('(this field)'); + choice.disabled = true; + } + + // get selected field conditions + var conditionTypes = acf.getConditionTypes({ + fieldType: fieldObject.getType() + }); + + // bail early if no types + if( !conditionTypes.length ) { + choice.disabled = true; + } + + // calulate indents + var indents = fieldObject.getParents().length; + choice.text = '- '.repeat(indents) + choice.text; + + // append + choices.push(choice); + }); + + // allow for scenario where only one field exists + if( !choices.length ) { + choices.push({ + id: '', + text: acf.__('No toggle fields available'), + }); + } + + // render + acf.renderSelect( $select, choices ); + + // set + this.ruleData('field', $select.val()); + }, + + renderOperator: function(){ + + // bail early if no field selected + if( !this.ruleData('field') ) { + return; + } + + // vars + var $select = this.$input('operator'); + var val = $select.val(); + var choices = []; + + // set saved value on first render + // - this allows the 2nd render to correctly select an option + if( $select.val() === null ) { + acf.renderSelect($select, [{ + id: this.ruleData('operator'), + text: '' + }]); + } + + // get selected field + var $field = acf.findFieldObject( this.ruleData('field') ); + var field = acf.getFieldObject( $field ); + + // get selected field conditions + var conditionTypes = acf.getConditionTypes({ + fieldType: field.getType() + }); + + // html + conditionTypes.map(function( model ){ + choices.push({ + id: model.prototype.operator, + text: model.prototype.label + }); + }); + + // render + acf.renderSelect( $select, choices ); + + // set + this.ruleData('operator', $select.val()); + }, + + renderValue: function(){ + + // bail early if no field selected + if( !this.ruleData('field') || !this.ruleData('operator') ) { + return; + } + + // vars + var $select = this.$input('value'); + var $td = this.$td('value'); + var val = $select.val(); + + // get selected field + var $field = acf.findFieldObject( this.ruleData('field') ); + var field = acf.getFieldObject( $field ); + + // get selected field conditions + var conditionTypes = acf.getConditionTypes({ + fieldType: field.getType(), + operator: this.ruleData('operator') + }); + + // html + var conditionType = conditionTypes[0].prototype; + var choices = conditionType.choices( field ); + + // create html: array + if( choices instanceof Array ) { + var $newSelect = $(''); + acf.renderSelect( $newSelect, choices ); + + // create html: string () + } else { + var $newSelect = $(choices); + } + + // append + $select.detach(); + $td.html( $newSelect ); + + // copy attrs + // timeout needed to avoid browser bug where "disabled" attribute is not applied + setTimeout(function(){ + ['class', 'name', 'id'].map(function( attr ){ + $newSelect.attr( attr, $select.attr(attr)); + }); + }, 0); + + // select existing value (if not a disabled input) + if( !$newSelect.prop('disabled') ) { + acf.val( $newSelect, val, true ); + } + + // set + this.ruleData('value', $newSelect.val()); + }, + + onChangeToggle: function(){ + this.render(); + }, + + onClickAddGroup: function( e, $el ){ + this.addGroup(); + }, + + addGroup: function(){ + + // vars + var $group = this.$('.rule-group:last'); + + // duplicate + var $group2 = acf.duplicate( $group ); + + // update h4 + $group2.find('h4').text( acf.__('or') ); + + // remove all tr's except the first one + $group2.find('tr').not(':first').remove(); + + // save field + this.fieldObject.save(); + }, + + onFocusField: function( e, $el ){ + this.renderField(); + }, + + onChangeField: function( e, $el ){ + + // scope + this.scope( $el.closest('.rule') ); + + // set data + this.ruleData('field', $el.val()); + + // render + this.renderOperator(); + this.renderValue(); + }, + + onChangeOperator: function( e, $el ){ + + // scope + this.scope( $el.closest('.rule') ); + + // set data + this.ruleData('operator', $el.val()); + + // render + this.renderValue(); + }, + + onClickAdd: function( e, $el ){ + + // duplciate + var $rule = acf.duplicate( $el.closest('.rule') ); + + // render + this.renderRule( $rule ); + }, + + onClickRemove: function( e, $el ){ + + // vars + var $rule = $el.closest('.rule'); + + // save field + this.fieldObject.save(); + + // remove group + if( $rule.siblings('.rule').length == 0 ) { + $rule.closest('.rule-group').remove(); + } + + // remove + $rule.remove(); + } + }); + + acf.registerFieldSetting( ConditionalLogicFieldSetting ); + + + /** + * conditionalLogicHelper + * + * description + * + * @date 20/4/18 + * @since 5.6.9 + * + * @param type $var Description. Default. + * @return type Description. + */ + + var conditionalLogicHelper = new acf.Model({ + actions: { + 'duplicate_field_objects': 'onDuplicateFieldObjects', + }, + + onDuplicateFieldObjects: function( children, newField, prevField ){ + + // vars + var data = {}; + var $selects = $(); + + // reference change in key + children.map(function( child ){ + + // store reference of changed key + data[ child.get('prevKey') ] = child.get('key'); + + // append condition select + $selects = $selects.add( child.$('.condition-rule-field') ); + }); + + // loop + $selects.each(function(){ + + // vars + var $select = $(this); + var val = $select.val(); + + // bail early if val is not a ref key + if( !val || !data[val] ) { + return; + } + + // modify selected option + $select.find('option:selected').attr('value', data[val]); + + // set new val + $select.val( data[val] ); + + }); + }, + }); })(jQuery); - -(function ($, undefined) { - /** - * acf.findFieldObject - * - * Returns a single fieldObject $el for a given field key - * - * @date 1/2/18 - * @since 5.7.0 - * - * @param string key The field key - * @return jQuery - */ - acf.findFieldObject = function (key) { - return acf.findFieldObjects({ - key: key, - limit: 1 - }); - }; - /** - * acf.findFieldObjects - * - * Returns an array of fieldObject $el for the given args - * - * @date 1/2/18 - * @since 5.7.0 - * - * @param object args - * @return jQuery - */ - - - acf.findFieldObjects = function (args) { - // vars - args = args || {}; - var selector = '.acf-field-object'; - var $fields = false; // args - - args = acf.parseArgs(args, { - id: '', - key: '', - type: '', - limit: false, - list: null, - parent: false, - sibling: false, - child: false - }); // id - - if (args.id) { - selector += '[data-id="' + args.id + '"]'; - } // key - - - if (args.key) { - selector += '[data-key="' + args.key + '"]'; - } // type - - - if (args.type) { - selector += '[data-type="' + args.type + '"]'; - } // query - - - if (args.list) { - $fields = args.list.children(selector); - } else if (args.parent) { - $fields = args.parent.find(selector); - } else if (args.sibling) { - $fields = args.sibling.siblings(selector); - } else if (args.child) { - $fields = args.child.parents(selector); - } else { - $fields = $(selector); - } // limit - - - if (args.limit) { - $fields = $fields.slice(0, args.limit); - } // return - - - return $fields; - }; - /** - * acf.getFieldObject - * - * Returns a single fieldObject instance for a given $el|key - * - * @date 1/2/18 - * @since 5.7.0 - * - * @param string|jQuery $field The field $el or key - * @return jQuery - */ - - - acf.getFieldObject = function ($field) { - // allow key - if (typeof $field === 'string') { - $field = acf.findFieldObject($field); - } // instantiate - - - var field = $field.data('acf'); - - if (!field) { - field = acf.newFieldObject($field); - } // return - - - return field; - }; - /** - * acf.getFieldObjects - * - * Returns an array of fieldObject instances for the given args - * - * @date 1/2/18 - * @since 5.7.0 - * - * @param object args - * @return array - */ - - - acf.getFieldObjects = function (args) { - // query - var $fields = acf.findFieldObjects(args); // loop - - var fields = []; - $fields.each(function () { - var field = acf.getFieldObject($(this)); - fields.push(field); - }); // return - - return fields; - }; - /** - * acf.newFieldObject - * - * Initializes and returns a new FieldObject instance - * - * @date 1/2/18 - * @since 5.7.0 - * - * @param jQuery $field The field $el - * @return object - */ - - - acf.newFieldObject = function ($field) { - // instantiate - var field = new acf.FieldObject($field); // action - - acf.doAction('new_field_object', field); // return - - return field; - }; - /** - * actionManager - * - * description - * - * @date 15/12/17 - * @since 5.6.5 - * - * @param type $var Description. Default. - * @return type Description. - */ - - - var eventManager = new acf.Model({ - priority: 5, - initialize: function () { - // actions - var actions = ['prepare', 'ready', 'append', 'remove']; // loop - - actions.map(function (action) { - this.addFieldActions(action); - }, this); - }, - addFieldActions: function (action) { - // vars - var pluralAction = action + '_field_objects'; // ready_field_objects - - var singleAction = action + '_field_object'; // ready_field_object - - var singleEvent = action + 'FieldObject'; // readyFieldObject - // global action - - var callback = function ($el - /*, arg1, arg2, etc*/ - ) { - // vars - var fieldObjects = acf.getFieldObjects({ - parent: $el - }); // call plural - - if (fieldObjects.length) { - /// get args [$el, arg1] - var args = acf.arrayArgs(arguments); // modify args [pluralAction, fields, arg1] - - args.splice(0, 1, pluralAction, fieldObjects); - acf.doAction.apply(null, args); - } - }; // plural action - - - var pluralCallback = function (fieldObjects - /*, arg1, arg2, etc*/ - ) { - /// get args [fields, arg1] - var args = acf.arrayArgs(arguments); // modify args [singleAction, fields, arg1] - - args.unshift(singleAction); // loop - - fieldObjects.map(function (fieldObject) { - // modify args [singleAction, field, arg1] - args[1] = fieldObject; - acf.doAction.apply(null, args); - }); - }; // single action - - - var singleCallback = function (fieldObject - /*, arg1, arg2, etc*/ - ) { - /// get args [$field, arg1] - var args = acf.arrayArgs(arguments); // modify args [singleAction, $field, arg1] - - args.unshift(singleAction); // action variations (ready_field/type=image) - - var variations = ['type', 'name', 'key']; - variations.map(function (variation) { - args[0] = singleAction + '/' + variation + '=' + fieldObject.get(variation); - acf.doAction.apply(null, args); - }); // modify args [arg1] - - args.splice(0, 2); // event - - fieldObject.trigger(singleEvent, args); - }; // add actions - - - acf.addAction(action, callback, 5); - acf.addAction(pluralAction, pluralCallback, 5); - acf.addAction(singleAction, singleCallback, 5); - } - }); - /** - * fieldManager - * - * description - * - * @date 4/1/18 - * @since 5.6.5 - * - * @param type $var Description. Default. - * @return type Description. - */ - - var fieldManager = new acf.Model({ - id: 'fieldManager', - events: { - 'submit #post': 'onSubmit', - 'mouseenter .acf-field-list': 'onHoverSortable', - 'click .add-field': 'onClickAdd' - }, - actions: { - 'removed_field_object': 'onRemovedField', - 'sortstop_field_object': 'onReorderField', - 'delete_field_object': 'onDeleteField', - 'change_field_object_type': 'onChangeFieldType', - 'duplicate_field_object': 'onDuplicateField' - }, - onSubmit: function (e, $el) { - // vars - var fields = acf.getFieldObjects(); // loop - - fields.map(function (field) { - field.submit(); - }); - }, - setFieldMenuOrder: function (field) { - this.renderFields(field.$el.parent()); - }, - onHoverSortable: function (e, $el) { - // bail early if already sortable - if ($el.hasClass('ui-sortable')) return; // sortable - - $el.sortable({ - handle: '.acf-sortable-handle', - connectWith: '.acf-field-list', - start: function (e, ui) { - var field = acf.getFieldObject(ui.item); - ui.placeholder.height(ui.item.height()); - acf.doAction('sortstart_field_object', field, $el); - }, - update: function (e, ui) { - var field = acf.getFieldObject(ui.item); - acf.doAction('sortstop_field_object', field, $el); - } - }); - }, - onRemovedField: function (field, $list) { - this.renderFields($list); - }, - onReorderField: function (field, $list) { - field.updateParent(); - this.renderFields($list); - }, - onDeleteField: function (field) { - // delete children - field.getFields().map(function (child) { - child.delete({ - animate: false - }); - }); - }, - onChangeFieldType: function (field) {// this caused sub fields to disapear if changing type back... - //this.onDeleteField( field ); - }, - onDuplicateField: function (field, newField) { - // check for children - var children = newField.getFields(); - - if (children.length) { - // loop - children.map(function (child) { - // wipe field - child.wipe(); // update parent - - child.updateParent(); - }); // action - - acf.doAction('duplicate_field_objects', children, newField, field); - } // set menu order - - - this.setFieldMenuOrder(newField); - }, - renderFields: function ($list) { - // vars - var fields = acf.getFieldObjects({ - list: $list - }); // no fields - - if (!fields.length) { - $list.addClass('-empty'); - return; - } // has fields - - - $list.removeClass('-empty'); // prop - - fields.map(function (field, i) { - field.prop('menu_order', i); - }); - }, - onClickAdd: function (e, $el) { - var $list = $el.closest('.acf-tfoot').siblings('.acf-field-list'); - this.addField($list); - }, - addField: function ($list) { - // vars - var html = $('#tmpl-acf-field').html(); - var $el = $(html); - var prevId = $el.data('id'); - var newKey = acf.uniqid('field_'); // duplicate - - var $newField = acf.duplicate({ - target: $el, - search: prevId, - replace: newKey, - append: function ($el, $el2) { - $list.append($el2); - } - }); // get instance - - var newField = acf.getFieldObject($newField); // props - - newField.prop('key', newKey); - newField.prop('ID', 0); - newField.prop('label', ''); - newField.prop('name', ''); // attr - - $newField.attr('data-key', newKey); - $newField.attr('data-id', newKey); // update parent prop - - newField.updateParent(); // focus label - - var $label = newField.$input('label'); - setTimeout(function () { - $label.trigger('focus'); - }, 251); // open - - newField.open(); // set menu order - - this.renderFields($list); // action - - acf.doAction('add_field_object', newField); - acf.doAction('append_field_object', newField); - } - }); +(function($, undefined){ + + /** + * acf.findFieldObject + * + * Returns a single fieldObject $el for a given field key + * + * @date 1/2/18 + * @since 5.7.0 + * + * @param string key The field key + * @return jQuery + */ + + acf.findFieldObject = function( key ){ + return acf.findFieldObjects({ + key: key, + limit: 1 + }); + }; + + /** + * acf.findFieldObjects + * + * Returns an array of fieldObject $el for the given args + * + * @date 1/2/18 + * @since 5.7.0 + * + * @param object args + * @return jQuery + */ + + acf.findFieldObjects = function( args ){ + + // vars + args = args || {}; + var selector = '.acf-field-object'; + var $fields = false; + + // args + args = acf.parseArgs(args, { + id: '', + key: '', + type: '', + limit: false, + list: null, + parent: false, + sibling: false, + child: false, + }); + + // id + if( args.id ) { + selector += '[data-id="' + args.id + '"]'; + } + + // key + if( args.key ) { + selector += '[data-key="' + args.key + '"]'; + } + + // type + if( args.type ) { + selector += '[data-type="' + args.type + '"]'; + } + + // query + if( args.list ) { + $fields = args.list.children( selector ); + } else if( args.parent ) { + $fields = args.parent.find( selector ); + } else if( args.sibling ) { + $fields = args.sibling.siblings( selector ); + } else if( args.child ) { + $fields = args.child.parents( selector ); + } else { + $fields = $( selector ); + } + + // limit + if( args.limit ) { + $fields = $fields.slice( 0, args.limit ); + } + + // return + return $fields; + }; + + /** + * acf.getFieldObject + * + * Returns a single fieldObject instance for a given $el|key + * + * @date 1/2/18 + * @since 5.7.0 + * + * @param string|jQuery $field The field $el or key + * @return jQuery + */ + + acf.getFieldObject = function( $field ){ + + // allow key + if( typeof $field === 'string' ) { + $field = acf.findFieldObject( $field ); + } + + // instantiate + var field = $field.data('acf'); + if( !field ) { + field = acf.newFieldObject( $field ); + } + + // return + return field; + }; + + /** + * acf.getFieldObjects + * + * Returns an array of fieldObject instances for the given args + * + * @date 1/2/18 + * @since 5.7.0 + * + * @param object args + * @return array + */ + + acf.getFieldObjects = function( args ){ + + // query + var $fields = acf.findFieldObjects( args ); + + // loop + var fields = []; + $fields.each(function(){ + var field = acf.getFieldObject( $(this) ); + fields.push( field ); + }); + + // return + return fields; + }; + + /** + * acf.newFieldObject + * + * Initializes and returns a new FieldObject instance + * + * @date 1/2/18 + * @since 5.7.0 + * + * @param jQuery $field The field $el + * @return object + */ + + acf.newFieldObject = function( $field ){ + + // instantiate + var field = new acf.FieldObject( $field ); + + // action + acf.doAction('new_field_object', field); + + // return + return field; + }; + + /** + * actionManager + * + * description + * + * @date 15/12/17 + * @since 5.6.5 + * + * @param type $var Description. Default. + * @return type Description. + */ + + var eventManager = new acf.Model({ + + priority: 5, + + initialize: function(){ + + // actions + var actions = [ + 'prepare', + 'ready', + 'append', + 'remove' + ]; + + // loop + actions.map(function( action ){ + this.addFieldActions( action ); + }, this); + }, + + addFieldActions: function( action ){ + + // vars + var pluralAction = action + '_field_objects'; // ready_field_objects + var singleAction = action + '_field_object'; // ready_field_object + var singleEvent = action + 'FieldObject'; // readyFieldObject + + // global action + var callback = function( $el /*, arg1, arg2, etc*/ ){ + + // vars + var fieldObjects = acf.getFieldObjects({ parent: $el }); + + // call plural + if( fieldObjects.length ) { + + /// get args [$el, arg1] + var args = acf.arrayArgs( arguments ); + + // modify args [pluralAction, fields, arg1] + args.splice(0, 1, pluralAction, fieldObjects); + acf.doAction.apply(null, args); + } + }; + + // plural action + var pluralCallback = function( fieldObjects /*, arg1, arg2, etc*/ ){ + + /// get args [fields, arg1] + var args = acf.arrayArgs( arguments ); + + // modify args [singleAction, fields, arg1] + args.unshift(singleAction); + + // loop + fieldObjects.map(function( fieldObject ){ + + // modify args [singleAction, field, arg1] + args[1] = fieldObject; + acf.doAction.apply(null, args); + }); + }; + + // single action + var singleCallback = function( fieldObject /*, arg1, arg2, etc*/ ){ + + /// get args [$field, arg1] + var args = acf.arrayArgs( arguments ); + + // modify args [singleAction, $field, arg1] + args.unshift(singleAction); + + // action variations (ready_field/type=image) + var variations = ['type', 'name', 'key']; + variations.map(function( variation ){ + args[0] = singleAction + '/' + variation + '=' + fieldObject.get(variation); + acf.doAction.apply(null, args); + }); + + // modify args [arg1] + args.splice(0, 2); + + // event + fieldObject.trigger(singleEvent, args); + }; + + // add actions + acf.addAction(action, callback, 5); + acf.addAction(pluralAction, pluralCallback, 5); + acf.addAction(singleAction, singleCallback, 5); + + } + }); + + /** + * fieldManager + * + * description + * + * @date 4/1/18 + * @since 5.6.5 + * + * @param type $var Description. Default. + * @return type Description. + */ + + var fieldManager = new acf.Model({ + + id: 'fieldManager', + + events: { + 'submit #post': 'onSubmit', + 'mouseenter .acf-field-list': 'onHoverSortable', + 'click .add-field': 'onClickAdd', + }, + + actions: { + 'removed_field_object': 'onRemovedField', + 'sortstop_field_object': 'onReorderField', + 'delete_field_object': 'onDeleteField', + 'change_field_object_type': 'onChangeFieldType', + 'duplicate_field_object': 'onDuplicateField' + }, + + onSubmit: function( e, $el ){ + + // vars + var fields = acf.getFieldObjects(); + + // loop + fields.map(function( field ){ + field.submit(); + }); + }, + + setFieldMenuOrder: function( field ){ + this.renderFields( field.$el.parent() ); + }, + + onHoverSortable: function( e, $el ){ + + // bail early if already sortable + if( $el.hasClass('ui-sortable') ) return; + + // sortable + $el.sortable({ + handle: '.acf-sortable-handle', + connectWith: '.acf-field-list', + start: function( e, ui ){ + var field = acf.getFieldObject( ui.item ); + ui.placeholder.height( ui.item.height() ); + acf.doAction('sortstart_field_object', field, $el); + }, + update: function( e, ui ){ + var field = acf.getFieldObject( ui.item ); + acf.doAction('sortstop_field_object', field, $el); + } + }); + }, + + onRemovedField: function( field, $list ){ + this.renderFields( $list ); + }, + + onReorderField: function( field, $list ){ + field.updateParent(); + this.renderFields( $list ); + }, + + onDeleteField: function( field ){ + + // delete children + field.getFields().map(function( child ){ + child.delete({ animate: false }); + }); + }, + + onChangeFieldType: function( field ){ + // this caused sub fields to disapear if changing type back... + //this.onDeleteField( field ); + }, + + onDuplicateField: function( field, newField ){ + + // check for children + var children = newField.getFields(); + if( children.length ) { + + // loop + children.map(function( child ){ + + // wipe field + child.wipe(); + + // update parent + child.updateParent(); + }); + + // action + acf.doAction('duplicate_field_objects', children, newField, field); + } + + // set menu order + this.setFieldMenuOrder( newField ); + }, + + renderFields: function( $list ){ + + // vars + var fields = acf.getFieldObjects({ + list: $list + }); + + // no fields + if( !fields.length ) { + $list.addClass('-empty'); + return; + } + + // has fields + $list.removeClass('-empty'); + + // prop + fields.map(function( field, i ){ + field.prop('menu_order', i); + }); + }, + + onClickAdd: function( e, $el ){ + var $list = $el.closest('.acf-tfoot').siblings('.acf-field-list'); + this.addField( $list ); + }, + + addField: function( $list ){ + + // vars + var html = $('#tmpl-acf-field').html(); + var $el = $(html); + var prevId = $el.data('id'); + var newKey = acf.uniqid('field_'); + + // duplicate + var $newField = acf.duplicate({ + target: $el, + search: prevId, + replace: newKey, + append: function( $el, $el2 ){ + $list.append( $el2 ); + } + }); + + // get instance + var newField = acf.getFieldObject( $newField ); + + // props + newField.prop('key', newKey); + newField.prop('ID', 0); + newField.prop('label', ''); + newField.prop('name', ''); + + // attr + $newField.attr('data-key', newKey); + $newField.attr('data-id', newKey); + + // update parent prop + newField.updateParent(); + + // focus label + var $label = newField.$input('label'); + setTimeout(function(){ + $label.trigger('focus'); + }, 251); + + // open + newField.open(); + + // set menu order + this.renderFields( $list ); + + // action + acf.doAction('add_field_object', newField); + acf.doAction('append_field_object', newField); + } + }); + })(jQuery); - -(function ($, undefined) { - /** - * locationManager - * - * Field group location rules functionality - * - * @date 15/12/17 - * @since 5.7.0 - * - * @param void - * @return void - */ - var locationManager = new acf.Model({ - id: 'locationManager', - wait: 'ready', - events: { - 'click .add-location-rule': 'onClickAddRule', - 'click .add-location-group': 'onClickAddGroup', - 'click .remove-location-rule': 'onClickRemoveRule', - 'change .refresh-location-rule': 'onChangeRemoveRule' - }, - initialize: function () { - this.$el = $('#acf-field-group-locations'); - }, - onClickAddRule: function (e, $el) { - this.addRule($el.closest('tr')); - }, - onClickRemoveRule: function (e, $el) { - this.removeRule($el.closest('tr')); - }, - onChangeRemoveRule: function (e, $el) { - this.changeRule($el.closest('tr')); - }, - onClickAddGroup: function (e, $el) { - this.addGroup(); - }, - addRule: function ($tr) { - acf.duplicate($tr); - }, - removeRule: function ($tr) { - if ($tr.siblings('tr').length == 0) { - $tr.closest('.rule-group').remove(); - } else { - $tr.remove(); - } - }, - changeRule: function ($rule) { - // vars - var $group = $rule.closest('.rule-group'); - var prefix = $rule.find('td.param select').attr('name').replace('[param]', ''); // ajaxdata - - var ajaxdata = {}; - ajaxdata.action = 'acf/field_group/render_location_rule'; - ajaxdata.rule = acf.serialize($rule, prefix); - ajaxdata.rule.id = $rule.data('id'); - ajaxdata.rule.group = $group.data('id'); // temp disable - - acf.disable($rule.find('td.value')); // ajax - - $.ajax({ - url: acf.get('ajaxurl'), - data: acf.prepareForAjax(ajaxdata), - type: 'post', - dataType: 'html', - success: function (html) { - if (!html) return; - $rule.replaceWith(html); - } - }); - }, - addGroup: function () { - // vars - var $group = this.$('.rule-group:last'); // duplicate - - $group2 = acf.duplicate($group); // update h4 - - $group2.find('h4').text(acf.__('or')); // remove all tr's except the first one - - $group2.find('tr').not(':first').remove(); - } - }); +(function($, undefined){ + + /** + * locationManager + * + * Field group location rules functionality + * + * @date 15/12/17 + * @since 5.7.0 + * + * @param void + * @return void + */ + + var locationManager = new acf.Model({ + + id: 'locationManager', + wait: 'ready', + + events: { + 'click .add-location-rule': 'onClickAddRule', + 'click .add-location-group': 'onClickAddGroup', + 'click .remove-location-rule': 'onClickRemoveRule', + 'change .refresh-location-rule': 'onChangeRemoveRule' + }, + + initialize: function(){ + this.$el = $('#acf-field-group-locations'); + }, + + onClickAddRule: function( e, $el ){ + this.addRule( $el.closest('tr') ); + }, + + onClickRemoveRule: function( e, $el ){ + this.removeRule( $el.closest('tr') ); + }, + + onChangeRemoveRule: function( e, $el ){ + this.changeRule( $el.closest('tr') ); + }, + + onClickAddGroup: function( e, $el ){ + this.addGroup(); + }, + + addRule: function( $tr ){ + acf.duplicate( $tr ); + }, + + removeRule: function( $tr ){ + if( $tr.siblings('tr').length == 0 ) { + $tr.closest('.rule-group').remove(); + } else { + $tr.remove(); + } + }, + + changeRule: function( $rule ){ + + // vars + var $group = $rule.closest('.rule-group'); + var prefix = $rule.find('td.param select').attr('name').replace('[param]', ''); + + // ajaxdata + var ajaxdata = {}; + ajaxdata.action = 'acf/field_group/render_location_rule'; + ajaxdata.rule = acf.serialize( $rule, prefix ); + ajaxdata.rule.id = $rule.data('id'); + ajaxdata.rule.group = $group.data('id'); + + // temp disable + acf.disable( $rule.find('td.value') ); + + // ajax + $.ajax({ + url: acf.get('ajaxurl'), + data: acf.prepareForAjax(ajaxdata), + type: 'post', + dataType: 'html', + success: function( html ){ + if( !html ) return; + $rule.replaceWith( html ); + } + }); + }, + + addGroup: function(){ + + // vars + var $group = this.$('.rule-group:last'); + + // duplicate + $group2 = acf.duplicate( $group ); + + // update h4 + $group2.find('h4').text( acf.__('or') ); + + // remove all tr's except the first one + $group2.find('tr').not(':first').remove(); + } + }); + })(jQuery); +(function($, undefined){ + + var _acf = acf.getCompatibility( acf ); + + /** + * fieldGroupCompatibility + * + * Compatibility layer for extinct acf.field_group + * + * @date 15/12/17 + * @since 5.7.0 + * + * @param void + * @return void + */ + + _acf.field_group = { + + save_field: function( $field, type ){ + type = (type !== undefined) ? type : 'settings'; + acf.getFieldObject( $field ).save( type ); + }, + + delete_field: function( $field, animate ){ + animate = (animate !== undefined) ? animate : true; + acf.getFieldObject( $field ).delete({ + animate: animate + }); + }, + + update_field_meta: function( $field, name, value ){ + acf.getFieldObject( $field ).prop( name, value ); + }, + + delete_field_meta: function( $field, name ){ + acf.getFieldObject( $field ).prop( name, null ); + } + }; + + /** + * fieldGroupCompatibility.field_object + * + * Compatibility layer for extinct acf.field_group.field_object + * + * @date 15/12/17 + * @since 5.7.0 + * + * @param void + * @return void + */ + + _acf.field_group.field_object = acf.model.extend({ + + // vars + type: '', + o: {}, + $field: null, + $settings: null, + + tag: function( tag ) { + + // vars + var type = this.type; + + + // explode, add 'field' and implode + // - open => open_field + // - change_type => change_field_type + var tags = tag.split('_'); + tags.splice(1, 0, 'field'); + tag = tags.join('_'); + + + // add type + if( type ) { + tag += '/type=' + type; + } + + + // return + return tag; + + }, + + selector: function(){ + + // vars + var selector = '.acf-field-object'; + var type = this.type; + -(function ($, undefined) { - var _acf = acf.getCompatibility(acf); - /** - * fieldGroupCompatibility - * - * Compatibility layer for extinct acf.field_group - * - * @date 15/12/17 - * @since 5.7.0 - * - * @param void - * @return void - */ - - - _acf.field_group = { - save_field: function ($field, type) { - type = type !== undefined ? type : 'settings'; - acf.getFieldObject($field).save(type); - }, - delete_field: function ($field, animate) { - animate = animate !== undefined ? animate : true; - acf.getFieldObject($field).delete({ - animate: animate - }); - }, - update_field_meta: function ($field, name, value) { - acf.getFieldObject($field).prop(name, value); - }, - delete_field_meta: function ($field, name) { - acf.getFieldObject($field).prop(name, null); - } - }; - /** - * fieldGroupCompatibility.field_object - * - * Compatibility layer for extinct acf.field_group.field_object - * - * @date 15/12/17 - * @since 5.7.0 - * - * @param void - * @return void - */ - - _acf.field_group.field_object = acf.model.extend({ - // vars - type: '', - o: {}, - $field: null, - $settings: null, - tag: function (tag) { - // vars - var type = this.type; // explode, add 'field' and implode - // - open => open_field - // - change_type => change_field_type - - var tags = tag.split('_'); - tags.splice(1, 0, 'field'); - tag = tags.join('_'); // add type - - if (type) { - tag += '/type=' + type; - } // return - - - return tag; - }, - selector: function () { - // vars - var selector = '.acf-field-object'; - var type = this.type; // add type - - if (type) { - selector += '-' + type; - selector = acf.str_replace('_', '-', selector); - } // return - - - return selector; - }, - _add_action: function (name, callback) { - // vars - var model = this; // add action - - acf.add_action(this.tag(name), function ($field) { - // focus - model.set('$field', $field); // callback - - model[callback].apply(model, arguments); - }); - }, - _add_filter: function (name, callback) { - // vars - var model = this; // add action - - acf.add_filter(this.tag(name), function ($field) { - // focus - model.set('$field', $field); // callback - - model[callback].apply(model, arguments); - }); - }, - _add_event: function (name, callback) { - // vars - var model = this; - var event = name.substr(0, name.indexOf(' ')); - var selector = name.substr(name.indexOf(' ') + 1); - var context = this.selector(); // add event - - $(document).on(event, context + ' ' + selector, function (e) { - // append $el to event object - e.$el = $(this); - e.$field = e.$el.closest('.acf-field-object'); // focus - - model.set('$field', e.$field); // callback - - model[callback].apply(model, [e]); - }); - }, - _set_$field: function () { - // vars - this.o = this.$field.data(); // els - - this.$settings = this.$field.find('> .settings > table > tbody'); // focus - - this.focus(); - }, - focus: function () {// do nothing - }, - setting: function (name) { - return this.$settings.find('> .acf-field-setting-' + name); - } - }); - /* - * field - * - * This model fires actions and filters for registered fields - * - * @type function - * @date 21/02/2014 - * @since 3.5.1 - * - * @param n/a - * @return n/a - */ - - var actionManager = new acf.Model({ - actions: { - 'open_field_object': 'onOpenFieldObject', - 'close_field_object': 'onCloseFieldObject', - 'add_field_object': 'onAddFieldObject', - 'duplicate_field_object': 'onDuplicateFieldObject', - 'delete_field_object': 'onDeleteFieldObject', - 'change_field_object_type': 'onChangeFieldObjectType', - 'change_field_object_label': 'onChangeFieldObjectLabel', - 'change_field_object_name': 'onChangeFieldObjectName', - 'change_field_object_parent': 'onChangeFieldObjectParent', - 'sortstop_field_object': 'onChangeFieldObjectParent' - }, - onOpenFieldObject: function (field) { - acf.doAction('open_field', field.$el); - acf.doAction('open_field/type=' + field.get('type'), field.$el); - acf.doAction('render_field_settings', field.$el); - acf.doAction('render_field_settings/type=' + field.get('type'), field.$el); - }, - onCloseFieldObject: function (field) { - acf.doAction('close_field', field.$el); - acf.doAction('close_field/type=' + field.get('type'), field.$el); - }, - onAddFieldObject: function (field) { - acf.doAction('add_field', field.$el); - acf.doAction('add_field/type=' + field.get('type'), field.$el); - }, - onDuplicateFieldObject: function (field) { - acf.doAction('duplicate_field', field.$el); - acf.doAction('duplicate_field/type=' + field.get('type'), field.$el); - }, - onDeleteFieldObject: function (field) { - acf.doAction('delete_field', field.$el); - acf.doAction('delete_field/type=' + field.get('type'), field.$el); - }, - onChangeFieldObjectType: function (field) { - acf.doAction('change_field_type', field.$el); - acf.doAction('change_field_type/type=' + field.get('type'), field.$el); - acf.doAction('render_field_settings', field.$el); - acf.doAction('render_field_settings/type=' + field.get('type'), field.$el); - }, - onChangeFieldObjectLabel: function (field) { - acf.doAction('change_field_label', field.$el); - acf.doAction('change_field_label/type=' + field.get('type'), field.$el); - }, - onChangeFieldObjectName: function (field) { - acf.doAction('change_field_name', field.$el); - acf.doAction('change_field_name/type=' + field.get('type'), field.$el); - }, - onChangeFieldObjectParent: function (field) { - acf.doAction('update_field_parent', field.$el); - } - }); + // add type + if( type ) { + selector += '-' + type; + selector = acf.str_replace('_', '-', selector); + } + + + // return + return selector; + + }, + + _add_action: function( name, callback ) { + + // vars + var model = this; + + + // add action + acf.add_action( this.tag(name), function( $field ){ + + // focus + model.set('$field', $field); + + + // callback + model[ callback ].apply(model, arguments); + + }); + + }, + + _add_filter: function( name, callback ) { + + // vars + var model = this; + + + // add action + acf.add_filter( this.tag(name), function( $field ){ + + // focus + model.set('$field', $field); + + + // callback + model[ callback ].apply(model, arguments); + + }); + + }, + + _add_event: function( name, callback ) { + + // vars + var model = this; + var event = name.substr(0,name.indexOf(' ')); + var selector = name.substr(name.indexOf(' ')+1); + var context = this.selector(); + + + // add event + $(document).on(event, context + ' ' + selector, function( e ){ + + // append $el to event object + e.$el = $(this); + e.$field = e.$el.closest('.acf-field-object'); + + + // focus + model.set('$field', e.$field); + + + // callback + model[ callback ].apply(model, [e]); + + }); + + }, + + _set_$field: function(){ + + // vars + this.o = this.$field.data(); + + + // els + this.$settings = this.$field.find('> .settings > table > tbody'); + + + // focus + this.focus(); + + }, + + focus: function(){ + + // do nothing + + }, + + setting: function( name ) { + + return this.$settings.find('> .acf-field-setting-' + name); + + } + + }); + + + /* + * field + * + * This model fires actions and filters for registered fields + * + * @type function + * @date 21/02/2014 + * @since 3.5.1 + * + * @param n/a + * @return n/a + */ + + var actionManager = new acf.Model({ + + actions: { + 'open_field_object': 'onOpenFieldObject', + 'close_field_object': 'onCloseFieldObject', + 'add_field_object': 'onAddFieldObject', + 'duplicate_field_object': 'onDuplicateFieldObject', + 'delete_field_object': 'onDeleteFieldObject', + 'change_field_object_type': 'onChangeFieldObjectType', + 'change_field_object_label': 'onChangeFieldObjectLabel', + 'change_field_object_name': 'onChangeFieldObjectName', + 'change_field_object_parent': 'onChangeFieldObjectParent', + 'sortstop_field_object': 'onChangeFieldObjectParent' + }, + + onOpenFieldObject: function( field ){ + acf.doAction('open_field', field.$el); + acf.doAction('open_field/type=' + field.get('type'), field.$el); + + acf.doAction('render_field_settings', field.$el); + acf.doAction('render_field_settings/type=' + field.get('type'), field.$el); + }, + + onCloseFieldObject: function( field ){ + acf.doAction('close_field', field.$el); + acf.doAction('close_field/type=' + field.get('type'), field.$el); + }, + + onAddFieldObject: function( field ){ + acf.doAction('add_field', field.$el); + acf.doAction('add_field/type=' + field.get('type'), field.$el); + }, + + onDuplicateFieldObject: function( field ){ + acf.doAction('duplicate_field', field.$el); + acf.doAction('duplicate_field/type=' + field.get('type'), field.$el); + }, + + onDeleteFieldObject: function( field ){ + acf.doAction('delete_field', field.$el); + acf.doAction('delete_field/type=' + field.get('type'), field.$el); + }, + + onChangeFieldObjectType: function( field ){ + acf.doAction('change_field_type', field.$el); + acf.doAction('change_field_type/type=' + field.get('type'), field.$el); + + acf.doAction('render_field_settings', field.$el); + acf.doAction('render_field_settings/type=' + field.get('type'), field.$el); + }, + + onChangeFieldObjectLabel: function( field ){ + acf.doAction('change_field_label', field.$el); + acf.doAction('change_field_label/type=' + field.get('type'), field.$el); + }, + + onChangeFieldObjectName: function( field ){ + acf.doAction('change_field_name', field.$el); + acf.doAction('change_field_name/type=' + field.get('type'), field.$el); + }, + + onChangeFieldObjectParent: function( field ){ + acf.doAction('update_field_parent', field.$el); + } + }); + })(jQuery); \ No newline at end of file diff --git a/assets/build/js/acf-input.js b/assets/build/js/acf-input.js index 992c08c..37aec7b 100644 --- a/assets/build/js/acf-input.js +++ b/assets/build/js/acf-input.js @@ -1,9253 +1,10883 @@ -(function ($, undefined) { - // vars - var storage = []; - /** - * acf.Field - * - * description - * - * @date 23/3/18 - * @since 5.6.9 - * - * @param type $var Description. Default. - * @return type Description. - */ - - acf.Field = acf.Model.extend({ - // field type - type: '', - // class used to avoid nested event triggers - eventScope: '.acf-field', - // initialize events on 'ready' - wait: 'ready', - - /** - * setup - * - * Called during the constructor function to setup this field ready for initialization - * - * @date 8/5/18 - * @since 5.6.9 - * - * @param jQuery $field The field element. - * @return void - */ - setup: function ($field) { - // set $el - this.$el = $field; // inherit $field data - - this.inherit($field); // inherit controll data - - this.inherit(this.$control()); - }, - - /** - * val - * - * Sets or returns the field's value - * - * @date 8/5/18 - * @since 5.6.9 - * - * @param mixed val Optional. The value to set - * @return mixed - */ - val: function (val) { - // Set. - if (val !== undefined) { - return this.setValue(val); // Get. - } else { - return this.prop('disabled') ? null : this.getValue(); - } - }, - - /** - * getValue - * - * returns the field's value - * - * @date 8/5/18 - * @since 5.6.9 - * - * @param void - * @return mixed - */ - getValue: function () { - return this.$input().val(); - }, - - /** - * setValue - * - * sets the field's value and returns true if changed - * - * @date 8/5/18 - * @since 5.6.9 - * - * @param mixed val - * @return boolean. True if changed. - */ - setValue: function (val) { - return acf.val(this.$input(), val); - }, - - /** - * __ - * - * i18n helper to be removed - * - * @date 8/5/18 - * @since 5.6.9 - * - * @param type $var Description. Default. - * @return type Description. - */ - __: function (string) { - return acf._e(this.type, string); - }, - - /** - * $control - * - * returns the control jQuery element used for inheriting data. Uses this.control setting. - * - * @date 8/5/18 - * @since 5.6.9 - * - * @param void - * @return jQuery - */ - $control: function () { - return false; - }, - - /** - * $input - * - * returns the input jQuery element used for saving values. Uses this.input setting. - * - * @date 8/5/18 - * @since 5.6.9 - * - * @param void - * @return jQuery - */ - $input: function () { - return this.$('[name]:first'); - }, - - /** - * $inputWrap - * - * description - * - * @date 12/5/18 - * @since 5.6.9 - * - * @param type $var Description. Default. - * @return type Description. - */ - $inputWrap: function () { - return this.$('.acf-input:first'); - }, - - /** - * $inputWrap - * - * description - * - * @date 12/5/18 - * @since 5.6.9 - * - * @param type $var Description. Default. - * @return type Description. - */ - $labelWrap: function () { - return this.$('.acf-label:first'); - }, - - /** - * getInputName - * - * Returns the field's input name - * - * @date 8/5/18 - * @since 5.6.9 - * - * @param void - * @return string - */ - getInputName: function () { - return this.$input().attr('name') || ''; - }, - - /** - * parent - * - * returns the field's parent field or false on failure. - * - * @date 8/5/18 - * @since 5.6.9 - * - * @param void - * @return object|false - */ - parent: function () { - // vars - var parents = this.parents(); // return - - return parents.length ? parents[0] : false; - }, - - /** - * parents - * - * description - * - * @date 9/7/18 - * @since 5.6.9 - * - * @param type $var Description. Default. - * @return type Description. - */ - parents: function () { - // vars - var $parents = this.$el.parents('.acf-field'); // convert - - var parents = acf.getFields($parents); // return - - return parents; - }, - show: function (lockKey, context) { - // show field and store result - var changed = acf.show(this.$el, lockKey); // do action if visibility has changed - - if (changed) { - this.prop('hidden', false); - acf.doAction('show_field', this, context); - } // return - - - return changed; - }, - hide: function (lockKey, context) { - // hide field and store result - var changed = acf.hide(this.$el, lockKey); // do action if visibility has changed - - if (changed) { - this.prop('hidden', true); - acf.doAction('hide_field', this, context); - } // return - - - return changed; - }, - enable: function (lockKey, context) { - // enable field and store result - var changed = acf.enable(this.$el, lockKey); // do action if disabled has changed - - if (changed) { - this.prop('disabled', false); - acf.doAction('enable_field', this, context); - } // return - - - return changed; - }, - disable: function (lockKey, context) { - // disabled field and store result - var changed = acf.disable(this.$el, lockKey); // do action if disabled has changed - - if (changed) { - this.prop('disabled', true); - acf.doAction('disable_field', this, context); - } // return - - - return changed; - }, - showEnable: function (lockKey, context) { - // enable - this.enable.apply(this, arguments); // show and return true if changed - - return this.show.apply(this, arguments); - }, - hideDisable: function (lockKey, context) { - // disable - this.disable.apply(this, arguments); // hide and return true if changed - - return this.hide.apply(this, arguments); - }, - showNotice: function (props) { - // ensure object - if (typeof props !== 'object') { - props = { - text: props - }; - } // remove old notice - - - if (this.notice) { - this.notice.remove(); - } // create new notice - - - props.target = this.$inputWrap(); - this.notice = acf.newNotice(props); - }, - removeNotice: function (timeout) { - if (this.notice) { - this.notice.away(timeout || 0); - this.notice = false; - } - }, - showError: function (message) { - // add class - this.$el.addClass('acf-error'); // add message - - if (message !== undefined) { - this.showNotice({ - text: message, - type: 'error', - dismiss: false - }); - } // action - - - acf.doAction('invalid_field', this); // add event - - this.$el.one('focus change', 'input, select, textarea', $.proxy(this.removeError, this)); - }, - removeError: function () { - // remove class - this.$el.removeClass('acf-error'); // remove notice - - this.removeNotice(250); // action - - acf.doAction('valid_field', this); - }, - trigger: function (name, args, bubbles) { - // allow some events to bubble - if (name == 'invalidField') { - bubbles = true; - } // return - - - return acf.Model.prototype.trigger.apply(this, [name, args, bubbles]); - } - }); - /** - * newField - * - * description - * - * @date 14/12/17 - * @since 5.6.5 - * - * @param type $var Description. Default. - * @return type Description. - */ - - acf.newField = function ($field) { - // vars - var type = $field.data('type'); - var mid = modelId(type); - var model = acf.models[mid] || acf.Field; // instantiate - - var field = new model($field); // actions - - acf.doAction('new_field', field); // return - - return field; - }; - /** - * mid - * - * Calculates the model ID for a field type - * - * @date 15/12/17 - * @since 5.6.5 - * - * @param string type - * @return string - */ - - - var modelId = function (type) { - return acf.strPascalCase(type || '') + 'Field'; - }; - /** - * registerFieldType - * - * description - * - * @date 14/12/17 - * @since 5.6.5 - * - * @param type $var Description. Default. - * @return type Description. - */ - - - acf.registerFieldType = function (model) { - // vars - var proto = model.prototype; - var type = proto.type; - var mid = modelId(type); // store model - - acf.models[mid] = model; // store reference - - storage.push(type); - }; - /** - * acf.getFieldType - * - * description - * - * @date 1/2/18 - * @since 5.6.5 - * - * @param type $var Description. Default. - * @return type Description. - */ - - - acf.getFieldType = function (type) { - var mid = modelId(type); - return acf.models[mid] || false; - }; - /** - * acf.getFieldTypes - * - * description - * - * @date 1/2/18 - * @since 5.6.5 - * - * @param type $var Description. Default. - * @return type Description. - */ - - - acf.getFieldTypes = function (args) { - // defaults - args = acf.parseArgs(args, { - category: '' // hasValue: true - - }); // clonse available types - - var types = []; // loop - - storage.map(function (type) { - // vars - var model = acf.getFieldType(type); - var proto = model.prototype; // check operator - - if (args.category && proto.category !== args.category) { - return; - } // append - - - types.push(model); - }); // return - - return types; - }; +(function($, undefined){ + + // vars + var storage = []; + + /** + * acf.Field + * + * description + * + * @date 23/3/18 + * @since 5.6.9 + * + * @param type $var Description. Default. + * @return type Description. + */ + + acf.Field = acf.Model.extend({ + + // field type + type: '', + + // class used to avoid nested event triggers + eventScope: '.acf-field', + + // initialize events on 'ready' + wait: 'ready', + + /** + * setup + * + * Called during the constructor function to setup this field ready for initialization + * + * @date 8/5/18 + * @since 5.6.9 + * + * @param jQuery $field The field element. + * @return void + */ + + setup: function( $field ){ + + // set $el + this.$el = $field; + + // inherit $field data + this.inherit( $field ); + + // inherit controll data + this.inherit( this.$control() ); + }, + + /** + * val + * + * Sets or returns the field's value + * + * @date 8/5/18 + * @since 5.6.9 + * + * @param mixed val Optional. The value to set + * @return mixed + */ + + val: function( val ){ + + // Set. + if( val !== undefined ) { + return this.setValue( val ); + + // Get. + } else { + return this.prop('disabled') ? null : this.getValue(); + } + }, + + /** + * getValue + * + * returns the field's value + * + * @date 8/5/18 + * @since 5.6.9 + * + * @param void + * @return mixed + */ + + getValue: function(){ + return this.$input().val(); + }, + + /** + * setValue + * + * sets the field's value and returns true if changed + * + * @date 8/5/18 + * @since 5.6.9 + * + * @param mixed val + * @return boolean. True if changed. + */ + + setValue: function( val ){ + return acf.val( this.$input(), val ); + }, + + /** + * __ + * + * i18n helper to be removed + * + * @date 8/5/18 + * @since 5.6.9 + * + * @param type $var Description. Default. + * @return type Description. + */ + + __: function( string ){ + return acf._e( this.type, string ); + }, + + /** + * $control + * + * returns the control jQuery element used for inheriting data. Uses this.control setting. + * + * @date 8/5/18 + * @since 5.6.9 + * + * @param void + * @return jQuery + */ + + $control: function(){ + return false; + }, + + /** + * $input + * + * returns the input jQuery element used for saving values. Uses this.input setting. + * + * @date 8/5/18 + * @since 5.6.9 + * + * @param void + * @return jQuery + */ + + $input: function(){ + return this.$('[name]:first'); + }, + + /** + * $inputWrap + * + * description + * + * @date 12/5/18 + * @since 5.6.9 + * + * @param type $var Description. Default. + * @return type Description. + */ + + $inputWrap: function(){ + return this.$('.acf-input:first'); + }, + + /** + * $inputWrap + * + * description + * + * @date 12/5/18 + * @since 5.6.9 + * + * @param type $var Description. Default. + * @return type Description. + */ + + $labelWrap: function(){ + return this.$('.acf-label:first'); + }, + + /** + * getInputName + * + * Returns the field's input name + * + * @date 8/5/18 + * @since 5.6.9 + * + * @param void + * @return string + */ + + getInputName: function(){ + return this.$input().attr('name') || ''; + }, + + /** + * parent + * + * returns the field's parent field or false on failure. + * + * @date 8/5/18 + * @since 5.6.9 + * + * @param void + * @return object|false + */ + + parent: function() { + + // vars + var parents = this.parents(); + + // return + return parents.length ? parents[0] : false; + }, + + /** + * parents + * + * description + * + * @date 9/7/18 + * @since 5.6.9 + * + * @param type $var Description. Default. + * @return type Description. + */ + + parents: function(){ + + // vars + var $parents = this.$el.parents('.acf-field'); + + // convert + var parents = acf.getFields( $parents ); + + // return + return parents; + }, + + show: function( lockKey, context ){ + + // show field and store result + var changed = acf.show( this.$el, lockKey ); + + // do action if visibility has changed + if( changed ) { + this.prop('hidden', false); + acf.doAction('show_field', this, context); + } + + // return + return changed; + }, + + hide: function( lockKey, context ){ + + // hide field and store result + var changed = acf.hide( this.$el, lockKey ); + + // do action if visibility has changed + if( changed ) { + this.prop('hidden', true); + acf.doAction('hide_field', this, context); + } + + // return + return changed; + }, + + enable: function( lockKey, context ){ + + // enable field and store result + var changed = acf.enable( this.$el, lockKey ); + + // do action if disabled has changed + if( changed ) { + this.prop('disabled', false); + acf.doAction('enable_field', this, context); + } + + // return + return changed; + }, + + disable: function( lockKey, context ){ + + // disabled field and store result + var changed = acf.disable( this.$el, lockKey ); + + // do action if disabled has changed + if( changed ) { + this.prop('disabled', true); + acf.doAction('disable_field', this, context); + } + + // return + return changed; + }, + + showEnable: function( lockKey, context ){ + + // enable + this.enable.apply(this, arguments); + + // show and return true if changed + return this.show.apply(this, arguments); + }, + + hideDisable: function( lockKey, context ){ + + // disable + this.disable.apply(this, arguments); + + // hide and return true if changed + return this.hide.apply(this, arguments); + }, + + showNotice: function( props ){ + + // ensure object + if( typeof props !== 'object' ) { + props = { text: props }; + } + + // remove old notice + if( this.notice ) { + this.notice.remove(); + } + + // create new notice + props.target = this.$inputWrap(); + this.notice = acf.newNotice( props ); + }, + + removeNotice: function( timeout ){ + if( this.notice ) { + this.notice.away( timeout || 0 ); + this.notice = false; + } + }, + + showError: function( message ){ + + // add class + this.$el.addClass('acf-error'); + + // add message + if( message !== undefined ) { + this.showNotice({ + text: message, + type: 'error', + dismiss: false + }); + } + + // action + acf.doAction('invalid_field', this); + + // add event + this.$el.one('focus change', 'input, select, textarea', $.proxy( this.removeError, this )); + }, + + removeError: function(){ + + // remove class + this.$el.removeClass('acf-error'); + + // remove notice + this.removeNotice( 250 ); + + // action + acf.doAction('valid_field', this); + }, + + trigger: function( name, args, bubbles ){ + + // allow some events to bubble + if( name == 'invalidField' ) { + bubbles = true; + } + + // return + return acf.Model.prototype.trigger.apply(this, [name, args, bubbles]); + }, + }); + + /** + * newField + * + * description + * + * @date 14/12/17 + * @since 5.6.5 + * + * @param type $var Description. Default. + * @return type Description. + */ + + acf.newField = function( $field ){ + + // vars + var type = $field.data('type'); + var mid = modelId( type ); + var model = acf.models[ mid ] || acf.Field; + + // instantiate + var field = new model( $field ); + + // actions + acf.doAction('new_field', field); + + // return + return field; + }; + + /** + * mid + * + * Calculates the model ID for a field type + * + * @date 15/12/17 + * @since 5.6.5 + * + * @param string type + * @return string + */ + + var modelId = function( type ) { + return acf.strPascalCase( type || '' ) + 'Field'; + }; + + /** + * registerFieldType + * + * description + * + * @date 14/12/17 + * @since 5.6.5 + * + * @param type $var Description. Default. + * @return type Description. + */ + + acf.registerFieldType = function( model ){ + + // vars + var proto = model.prototype; + var type = proto.type; + var mid = modelId( type ); + + // store model + acf.models[ mid ] = model; + + // store reference + storage.push( type ); + }; + + /** + * acf.getFieldType + * + * description + * + * @date 1/2/18 + * @since 5.6.5 + * + * @param type $var Description. Default. + * @return type Description. + */ + + acf.getFieldType = function( type ){ + var mid = modelId( type ); + return acf.models[ mid ] || false; + } + + /** + * acf.getFieldTypes + * + * description + * + * @date 1/2/18 + * @since 5.6.5 + * + * @param type $var Description. Default. + * @return type Description. + */ + + acf.getFieldTypes = function( args ){ + + // defaults + args = acf.parseArgs(args, { + category: '', + // hasValue: true + }); + + // clonse available types + var types = []; + + // loop + storage.map(function( type ){ + + // vars + var model = acf.getFieldType(type); + var proto = model.prototype; + + // check operator + if( args.category && proto.category !== args.category ) { + return; + } + + // append + types.push( model ); + }); + + // return + return types; + }; + })(jQuery); - -(function ($, undefined) { - /** - * findFields - * - * Returns a jQuery selection object of acf fields. - * - * @date 14/12/17 - * @since 5.6.5 - * - * @param object $args { - * Optional. Arguments to find fields. - * - * @type string key The field's key (data-attribute). - * @type string name The field's name (data-attribute). - * @type string type The field's type (data-attribute). - * @type string is jQuery selector to compare against. - * @type jQuery parent jQuery element to search within. - * @type jQuery sibling jQuery element to search alongside. - * @type limit int The number of fields to find. - * @type suppressFilters bool Whether to allow filters to add/remove results. Default behaviour will ignore clone fields. - * } - * @return jQuery - */ - acf.findFields = function (args) { - // vars - var selector = '.acf-field'; - var $fields = false; // args - - args = acf.parseArgs(args, { - key: '', - name: '', - type: '', - is: '', - parent: false, - sibling: false, - limit: false, - visible: false, - suppressFilters: false - }); // filter args - - if (!args.suppressFilters) { - args = acf.applyFilters('find_fields_args', args); - } // key - - - if (args.key) { - selector += '[data-key="' + args.key + '"]'; - } // type - - - if (args.type) { - selector += '[data-type="' + args.type + '"]'; - } // name - - - if (args.name) { - selector += '[data-name="' + args.name + '"]'; - } // is - - - if (args.is) { - selector += args.is; - } // visibility - - - if (args.visible) { - selector += ':visible'; - } // query - - - if (args.parent) { - $fields = args.parent.find(selector); - } else if (args.sibling) { - $fields = args.sibling.siblings(selector); - } else { - $fields = $(selector); - } // filter - - - if (!args.suppressFilters) { - $fields = $fields.not('.acf-clone .acf-field'); - $fields = acf.applyFilters('find_fields', $fields); - } // limit - - - if (args.limit) { - $fields = $fields.slice(0, args.limit); - } // return - - - return $fields; - }; - /** - * findField - * - * Finds a specific field with jQuery - * - * @date 14/12/17 - * @since 5.6.5 - * - * @param string key The field's key. - * @param jQuery $parent jQuery element to search within. - * @return jQuery - */ - - - acf.findField = function (key, $parent) { - return acf.findFields({ - key: key, - limit: 1, - parent: $parent, - suppressFilters: true - }); - }; - /** - * getField - * - * Returns a field instance - * - * @date 14/12/17 - * @since 5.6.5 - * - * @param jQuery|string $field jQuery element or field key. - * @return object - */ - - - acf.getField = function ($field) { - // allow jQuery - if ($field instanceof jQuery) {// find fields - } else { - $field = acf.findField($field); - } // instantiate - - - var field = $field.data('acf'); - - if (!field) { - field = acf.newField($field); - } // return - - - return field; - }; - /** - * getFields - * - * Returns multiple field instances - * - * @date 14/12/17 - * @since 5.6.5 - * - * @param jQuery|object $fields jQuery elements or query args. - * @return array - */ - - - acf.getFields = function ($fields) { - // allow jQuery - if ($fields instanceof jQuery) {// find fields - } else { - $fields = acf.findFields($fields); - } // loop - - - var fields = []; - $fields.each(function () { - var field = acf.getField($(this)); - fields.push(field); - }); // return - - return fields; - }; - /** - * findClosestField - * - * Returns the closest jQuery field element - * - * @date 9/4/18 - * @since 5.6.9 - * - * @param jQuery $el - * @return jQuery - */ - - - acf.findClosestField = function ($el) { - return $el.closest('.acf-field'); - }; - /** - * getClosestField - * - * Returns the closest field instance - * - * @date 22/1/18 - * @since 5.6.5 - * - * @param jQuery $el - * @return object - */ - - - acf.getClosestField = function ($el) { - var $field = acf.findClosestField($el); - return this.getField($field); - }; - /** - * addGlobalFieldAction - * - * Sets up callback logic for global field actions - * - * @date 15/6/18 - * @since 5.6.9 - * - * @param string action - * @return void - */ - - - var addGlobalFieldAction = function (action) { - // vars - var globalAction = action; - var pluralAction = action + '_fields'; // ready_fields - - var singleAction = action + '_field'; // ready_field - // global action - - var globalCallback = function ($el - /*, arg1, arg2, etc*/ - ) { - //console.log( action, arguments ); - // get args [$el, ...] - var args = acf.arrayArgs(arguments); - var extraArgs = args.slice(1); // find fields - - var fields = acf.getFields({ - parent: $el - }); // check - - if (fields.length) { - // pluralAction - var pluralArgs = [pluralAction, fields].concat(extraArgs); - acf.doAction.apply(null, pluralArgs); - } - }; // plural action - - - var pluralCallback = function (fields - /*, arg1, arg2, etc*/ - ) { - //console.log( pluralAction, arguments ); - // get args [fields, ...] - var args = acf.arrayArgs(arguments); - var extraArgs = args.slice(1); // loop - - fields.map(function (field, i) { - //setTimeout(function(){ - // singleAction - var singleArgs = [singleAction, field].concat(extraArgs); - acf.doAction.apply(null, singleArgs); //}, i * 100); - }); - }; // add actions - - - acf.addAction(globalAction, globalCallback); - acf.addAction(pluralAction, pluralCallback); // also add single action - - addSingleFieldAction(action); - }; - /** - * addSingleFieldAction - * - * Sets up callback logic for single field actions - * - * @date 15/6/18 - * @since 5.6.9 - * - * @param string action - * @return void - */ - - - var addSingleFieldAction = function (action) { - // vars - var singleAction = action + '_field'; // ready_field - - var singleEvent = action + 'Field'; // readyField - // single action - - var singleCallback = function (field - /*, arg1, arg2, etc*/ - ) { - //console.log( singleAction, arguments ); - // get args [field, ...] - var args = acf.arrayArgs(arguments); - var extraArgs = args.slice(1); // action variations (ready_field/type=image) - - var variations = ['type', 'name', 'key']; - variations.map(function (variation) { - // vars - var prefix = '/' + variation + '=' + field.get(variation); // singleAction - - args = [singleAction + prefix, field].concat(extraArgs); - acf.doAction.apply(null, args); - }); // event - - if (singleFieldEvents.indexOf(action) > -1) { - field.trigger(singleEvent, extraArgs); - } - }; // add actions - - - acf.addAction(singleAction, singleCallback); - }; // vars - - - var globalFieldActions = ['prepare', 'ready', 'load', 'append', 'remove', 'unmount', 'remount', 'sortstart', 'sortstop', 'show', 'hide', 'unload']; - var singleFieldActions = ['valid', 'invalid', 'enable', 'disable', 'new', 'duplicate']; - var singleFieldEvents = ['remove', 'unmount', 'remount', 'sortstart', 'sortstop', 'show', 'hide', 'unload', 'valid', 'invalid', 'enable', 'disable', 'duplicate']; // add - - globalFieldActions.map(addGlobalFieldAction); - singleFieldActions.map(addSingleFieldAction); - /** - * fieldsEventManager - * - * Manages field actions and events - * - * @date 15/12/17 - * @since 5.6.5 - * - * @param void - * @param void - */ - - var fieldsEventManager = new acf.Model({ - id: 'fieldsEventManager', - events: { - 'click .acf-field a[href="#"]': 'onClick', - 'change .acf-field': 'onChange' - }, - onClick: function (e) { - // prevent default of any link with an href of # - e.preventDefault(); - }, - onChange: function () { - // preview hack allows post to save with no title or content - $('#_acf_changed').val(1); - } - }); - var duplicateFieldsManager = new acf.Model({ - id: 'duplicateFieldsManager', - actions: { - 'duplicate': 'onDuplicate', - 'duplicate_fields': 'onDuplicateFields' - }, - onDuplicate: function ($el, $el2) { - var fields = acf.getFields({ - parent: $el - }); - - if (fields.length) { - var $fields = acf.findFields({ - parent: $el2 - }); - acf.doAction('duplicate_fields', fields, $fields); - } - }, - onDuplicateFields: function (fields, duplicates) { - fields.map(function (field, i) { - acf.doAction('duplicate_field', field, $(duplicates[i])); - }); - } - }); +(function($, undefined){ + + /** + * findFields + * + * Returns a jQuery selection object of acf fields. + * + * @date 14/12/17 + * @since 5.6.5 + * + * @param object $args { + * Optional. Arguments to find fields. + * + * @type string key The field's key (data-attribute). + * @type string name The field's name (data-attribute). + * @type string type The field's type (data-attribute). + * @type string is jQuery selector to compare against. + * @type jQuery parent jQuery element to search within. + * @type jQuery sibling jQuery element to search alongside. + * @type limit int The number of fields to find. + * @type suppressFilters bool Whether to allow filters to add/remove results. Default behaviour will ignore clone fields. + * } + * @return jQuery + */ + + acf.findFields = function( args ){ + + // vars + var selector = '.acf-field'; + var $fields = false; + + // args + args = acf.parseArgs(args, { + key: '', + name: '', + type: '', + is: '', + parent: false, + sibling: false, + limit: false, + visible: false, + suppressFilters: false, + }); + + // filter args + if( !args.suppressFilters ) { + args = acf.applyFilters('find_fields_args', args); + } + + // key + if( args.key ) { + selector += '[data-key="' + args.key + '"]'; + } + + // type + if( args.type ) { + selector += '[data-type="' + args.type + '"]'; + } + + // name + if( args.name ) { + selector += '[data-name="' + args.name + '"]'; + } + + // is + if( args.is ) { + selector += args.is; + } + + // visibility + if( args.visible ) { + selector += ':visible'; + } + + // query + if( args.parent ) { + $fields = args.parent.find( selector ); + } else if( args.sibling ) { + $fields = args.sibling.siblings( selector ); + } else { + $fields = $( selector ); + } + + // filter + if( !args.suppressFilters ) { + $fields = $fields.not('.acf-clone .acf-field'); + $fields = acf.applyFilters('find_fields', $fields); + } + + // limit + if( args.limit ) { + $fields = $fields.slice( 0, args.limit ); + } + + // return + return $fields; + + }; + + /** + * findField + * + * Finds a specific field with jQuery + * + * @date 14/12/17 + * @since 5.6.5 + * + * @param string key The field's key. + * @param jQuery $parent jQuery element to search within. + * @return jQuery + */ + + acf.findField = function( key, $parent ){ + return acf.findFields({ + key: key, + limit: 1, + parent: $parent, + suppressFilters: true + }); + }; + + /** + * getField + * + * Returns a field instance + * + * @date 14/12/17 + * @since 5.6.5 + * + * @param jQuery|string $field jQuery element or field key. + * @return object + */ + + acf.getField = function( $field ){ + + // allow jQuery + if( $field instanceof jQuery ) { + + // find fields + } else { + $field = acf.findField( $field ); + } + + // instantiate + var field = $field.data('acf'); + if( !field ) { + field = acf.newField( $field ); + } + + // return + return field; + }; + + /** + * getFields + * + * Returns multiple field instances + * + * @date 14/12/17 + * @since 5.6.5 + * + * @param jQuery|object $fields jQuery elements or query args. + * @return array + */ + + acf.getFields = function( $fields ){ + + // allow jQuery + if( $fields instanceof jQuery ) { + + // find fields + } else { + $fields = acf.findFields( $fields ); + } + + // loop + var fields = []; + $fields.each(function(){ + var field = acf.getField( $(this) ); + fields.push( field ); + }); + + // return + return fields; + }; + + /** + * findClosestField + * + * Returns the closest jQuery field element + * + * @date 9/4/18 + * @since 5.6.9 + * + * @param jQuery $el + * @return jQuery + */ + + acf.findClosestField = function( $el ){ + return $el.closest('.acf-field'); + }; + + /** + * getClosestField + * + * Returns the closest field instance + * + * @date 22/1/18 + * @since 5.6.5 + * + * @param jQuery $el + * @return object + */ + + acf.getClosestField = function( $el ){ + var $field = acf.findClosestField( $el ); + return this.getField( $field ); + }; + + /** + * addGlobalFieldAction + * + * Sets up callback logic for global field actions + * + * @date 15/6/18 + * @since 5.6.9 + * + * @param string action + * @return void + */ + + var addGlobalFieldAction = function( action ){ + + // vars + var globalAction = action; + var pluralAction = action + '_fields'; // ready_fields + var singleAction = action + '_field'; // ready_field + + // global action + var globalCallback = function( $el /*, arg1, arg2, etc*/ ){ + //console.log( action, arguments ); + + // get args [$el, ...] + var args = acf.arrayArgs( arguments ); + var extraArgs = args.slice(1); + + // find fields + var fields = acf.getFields({ parent: $el }); + + // check + if( fields.length ) { + + // pluralAction + var pluralArgs = [ pluralAction, fields ].concat( extraArgs ); + acf.doAction.apply(null, pluralArgs); + } + }; + + // plural action + var pluralCallback = function( fields /*, arg1, arg2, etc*/ ){ + //console.log( pluralAction, arguments ); + + // get args [fields, ...] + var args = acf.arrayArgs( arguments ); + var extraArgs = args.slice(1); + + // loop + fields.map(function( field, i ){ + //setTimeout(function(){ + // singleAction + var singleArgs = [ singleAction, field ].concat( extraArgs ); + acf.doAction.apply(null, singleArgs); + //}, i * 100); + }); + }; + + // add actions + acf.addAction(globalAction, globalCallback); + acf.addAction(pluralAction, pluralCallback); + + // also add single action + addSingleFieldAction( action ); + } + + /** + * addSingleFieldAction + * + * Sets up callback logic for single field actions + * + * @date 15/6/18 + * @since 5.6.9 + * + * @param string action + * @return void + */ + + var addSingleFieldAction = function( action ){ + + // vars + var singleAction = action + '_field'; // ready_field + var singleEvent = action + 'Field'; // readyField + + // single action + var singleCallback = function( field /*, arg1, arg2, etc*/ ){ + //console.log( singleAction, arguments ); + + // get args [field, ...] + var args = acf.arrayArgs( arguments ); + var extraArgs = args.slice(1); + + // action variations (ready_field/type=image) + var variations = ['type', 'name', 'key']; + variations.map(function( variation ){ + + // vars + var prefix = '/' + variation + '=' + field.get(variation); + + // singleAction + args = [ singleAction + prefix , field ].concat( extraArgs ); + acf.doAction.apply(null, args); + }); + + // event + if( singleFieldEvents.indexOf(action) > -1 ) { + field.trigger(singleEvent, extraArgs); + } + }; + + // add actions + acf.addAction(singleAction, singleCallback); + } + + // vars + var globalFieldActions = [ 'prepare', 'ready', 'load', 'append', 'remove', 'unmount', 'remount', 'sortstart', 'sortstop', 'show', 'hide', 'unload' ]; + var singleFieldActions = [ 'valid', 'invalid', 'enable', 'disable', 'new', 'duplicate' ]; + var singleFieldEvents = [ 'remove', 'unmount', 'remount', 'sortstart', 'sortstop', 'show', 'hide', 'unload', 'valid', 'invalid', 'enable', 'disable', 'duplicate' ]; + + // add + globalFieldActions.map( addGlobalFieldAction ); + singleFieldActions.map( addSingleFieldAction ); + + /** + * fieldsEventManager + * + * Manages field actions and events + * + * @date 15/12/17 + * @since 5.6.5 + * + * @param void + * @param void + */ + + var fieldsEventManager = new acf.Model({ + id: 'fieldsEventManager', + events: { + 'click .acf-field a[href="#"]': 'onClick', + 'change .acf-field': 'onChange' + }, + onClick: function( e ){ + + // prevent default of any link with an href of # + e.preventDefault(); + }, + onChange: function(){ + + // preview hack allows post to save with no title or content + $('#_acf_changed').val(1); + } + }); + + var duplicateFieldsManager = new acf.Model({ + id: 'duplicateFieldsManager', + actions: { + 'duplicate': 'onDuplicate', + 'duplicate_fields': 'onDuplicateFields', + }, + onDuplicate: function( $el, $el2 ){ + var fields = acf.getFields({ parent: $el }); + if( fields.length ) { + var $fields = acf.findFields({ parent: $el2 }); + acf.doAction( 'duplicate_fields', fields, $fields ); + } + }, + onDuplicateFields: function( fields, duplicates ){ + fields.map(function( field, i ){ + acf.doAction( 'duplicate_field', field, $(duplicates[i]) ); + }); + } + }); + })(jQuery); - -(function ($, undefined) { - var i = 0; - var Field = acf.Field.extend({ - type: 'accordion', - wait: '', - $control: function () { - return this.$('.acf-fields:first'); - }, - initialize: function () { - // Bail early if this is a duplicate of an existing initialized accordion. - if (this.$el.hasClass('acf-accordion')) { - return; - } // bail early if is cell +(function($, undefined){ + + var i = 0; + + var Field = acf.Field.extend({ + + type: 'accordion', + + wait: '', + + $control: function(){ + return this.$('.acf-fields:first'); + }, + + initialize: function(){ + + // Bail early if this is a duplicate of an existing initialized accordion. + if( this.$el.hasClass('acf-accordion') ) { + return; + } + + // bail early if is cell + if( this.$el.is('td') ) return; + + // enpoint + if( this.get('endpoint') ) { + return this.remove(); + } + + // vars + var $field = this.$el; + var $label = this.$labelWrap() + var $input = this.$inputWrap(); + var $wrap = this.$control(); + var $instructions = $input.children('.description'); + + // force description into label + if( $instructions.length ) { + $label.append( $instructions ); + } + + // table + if( this.$el.is('tr') ) { + + // vars + var $table = this.$el.closest('table'); + var $newLabel = $('
    '); + var $newInput = $('
    '); + var $newTable = $(''); + var $newWrap = $(''); + + // dom + $newLabel.append( $label.html() ); + $newTable.append( $newWrap ); + $newInput.append( $newTable ); + $input.append( $newLabel ); + $input.append( $newInput ); + + // modify + $label.remove(); + $wrap.remove(); + $input.attr('colspan', 2); + + // update vars + $label = $newLabel; + $input = $newInput; + $wrap = $newWrap; + } + + // add classes + $field.addClass('acf-accordion'); + $label.addClass('acf-accordion-title'); + $input.addClass('acf-accordion-content'); + + // index + i++; + + // multi-expand + if( this.get('multi_expand') ) { + $field.attr('multi-expand', 1); + } + + // open + var order = acf.getPreference('this.accordions') || []; + if( order[i-1] !== undefined ) { + this.set('open', order[i-1]); + } + + if( this.get('open') ) { + $field.addClass('-open'); + $input.css('display', 'block'); // needed for accordion to close smoothly + } + + // add icon + $label.prepend( accordionManager.iconHtml({ open: this.get('open') }) ); + + // classes + // - remove 'inside' which is a #poststuff WP class + var $parent = $field.parent(); + $wrap.addClass( $parent.hasClass('-left') ? '-left' : '' ); + $wrap.addClass( $parent.hasClass('-clear') ? '-clear' : '' ); + + // append + $wrap.append( $field.nextUntil('.acf-field-accordion', '.acf-field') ); + + // clean up + $wrap.removeAttr('data-open data-multi_expand data-endpoint'); + }, + + }); + + acf.registerFieldType( Field ); - if (this.$el.is('td')) return; // enpoint + /** + * accordionManager + * + * Events manager for the acf accordion + * + * @date 14/2/18 + * @since 5.6.9 + * + * @param void + * @return void + */ + + var accordionManager = new acf.Model({ + + actions: { + 'unload': 'onUnload' + }, + + events: { + 'click .acf-accordion-title': 'onClick', + 'invalidField .acf-accordion': 'onInvalidField' + }, + + isOpen: function( $el ) { + return $el.hasClass('-open'); + }, + + toggle: function( $el ){ + if( this.isOpen($el) ) { + this.close( $el ); + } else { + this.open( $el ); + } + }, + + iconHtml: function( props ){ + + // Use SVG inside Gutenberg editor. + if( acf.isGutenberg() ) { + if( props.open ) { + return ''; + } else { + return ''; + } + } else { + if( props.open ) { + return ''; + } else { + return ''; + } + } + }, + + open: function( $el ){ + var duration = acf.isGutenberg() ? 0 : 300; + + // open + $el.find('.acf-accordion-content:first').slideDown( duration ).css('display', 'block'); + $el.find('.acf-accordion-icon:first').replaceWith( this.iconHtml({ open: true }) ); + $el.addClass('-open'); + + // action + acf.doAction('show', $el); + + // close siblings + if( !$el.attr('multi-expand') ) { + $el.siblings('.acf-accordion.-open').each(function(){ + accordionManager.close( $(this) ); + }); + } + }, + + close: function( $el ){ + var duration = acf.isGutenberg() ? 0 : 300; + + // close + $el.find('.acf-accordion-content:first').slideUp( duration ); + $el.find('.acf-accordion-icon:first').replaceWith( this.iconHtml({ open: false }) ); + $el.removeClass('-open'); + + // action + acf.doAction('hide', $el); + }, + + onClick: function( e, $el ){ + + // prevent Defailt + e.preventDefault(); + + // open close + this.toggle( $el.parent() ); + + }, + + onInvalidField: function( e, $el ){ + + // bail early if already focused + if( this.busy ) { + return; + } + + // disable functionality for 1sec (allow next validation to work) + this.busy = true; + this.setTimeout(function(){ + this.busy = false; + }, 1000); + + // open accordion + this.open( $el ); + }, + + onUnload: function( e ){ + + // vars + var order = []; + + // loop + $('.acf-accordion').each(function(){ + var open = $(this).hasClass('-open') ? 1 : 0; + order.push(open); + }); + + // set + if( order.length ) { + acf.setPreference('this.accordions', order); + } + } + }); - if (this.get('endpoint')) { - return this.remove(); - } // vars - - - var $field = this.$el; - var $label = this.$labelWrap(); - var $input = this.$inputWrap(); - var $wrap = this.$control(); - var $instructions = $input.children('.description'); // force description into label - - if ($instructions.length) { - $label.append($instructions); - } // table - - - if (this.$el.is('tr')) { - // vars - var $table = this.$el.closest('table'); - var $newLabel = $('
    '); - var $newInput = $('
    '); - var $newTable = $('
    '); - var $newWrap = $(''); // dom - - $newLabel.append($label.html()); - $newTable.append($newWrap); - $newInput.append($newTable); - $input.append($newLabel); - $input.append($newInput); // modify - - $label.remove(); - $wrap.remove(); - $input.attr('colspan', 2); // update vars - - $label = $newLabel; - $input = $newInput; - $wrap = $newWrap; - } // add classes - - - $field.addClass('acf-accordion'); - $label.addClass('acf-accordion-title'); - $input.addClass('acf-accordion-content'); // index - - i++; // multi-expand - - if (this.get('multi_expand')) { - $field.attr('multi-expand', 1); - } // open - - - var order = acf.getPreference('this.accordions') || []; - - if (order[i - 1] !== undefined) { - this.set('open', order[i - 1]); - } - - if (this.get('open')) { - $field.addClass('-open'); - $input.css('display', 'block'); // needed for accordion to close smoothly - } // add icon - - - $label.prepend(accordionManager.iconHtml({ - open: this.get('open') - })); // classes - // - remove 'inside' which is a #poststuff WP class - - var $parent = $field.parent(); - $wrap.addClass($parent.hasClass('-left') ? '-left' : ''); - $wrap.addClass($parent.hasClass('-clear') ? '-clear' : ''); // append - - $wrap.append($field.nextUntil('.acf-field-accordion', '.acf-field')); // clean up - - $wrap.removeAttr('data-open data-multi_expand data-endpoint'); - } - }); - acf.registerFieldType(Field); - /** - * accordionManager - * - * Events manager for the acf accordion - * - * @date 14/2/18 - * @since 5.6.9 - * - * @param void - * @return void - */ - - var accordionManager = new acf.Model({ - actions: { - 'unload': 'onUnload' - }, - events: { - 'click .acf-accordion-title': 'onClick', - 'invalidField .acf-accordion': 'onInvalidField' - }, - isOpen: function ($el) { - return $el.hasClass('-open'); - }, - toggle: function ($el) { - if (this.isOpen($el)) { - this.close($el); - } else { - this.open($el); - } - }, - iconHtml: function (props) { - // Use SVG inside Gutenberg editor. - if (acf.isGutenberg()) { - if (props.open) { - return ''; - } else { - return ''; - } - } else { - if (props.open) { - return ''; - } else { - return ''; - } - } - }, - open: function ($el) { - var duration = acf.isGutenberg() ? 0 : 300; // open - - $el.find('.acf-accordion-content:first').slideDown(duration).css('display', 'block'); - $el.find('.acf-accordion-icon:first').replaceWith(this.iconHtml({ - open: true - })); - $el.addClass('-open'); // action - - acf.doAction('show', $el); // close siblings - - if (!$el.attr('multi-expand')) { - $el.siblings('.acf-accordion.-open').each(function () { - accordionManager.close($(this)); - }); - } - }, - close: function ($el) { - var duration = acf.isGutenberg() ? 0 : 300; // close - - $el.find('.acf-accordion-content:first').slideUp(duration); - $el.find('.acf-accordion-icon:first').replaceWith(this.iconHtml({ - open: false - })); - $el.removeClass('-open'); // action - - acf.doAction('hide', $el); - }, - onClick: function (e, $el) { - // prevent Defailt - e.preventDefault(); // open close - - this.toggle($el.parent()); - }, - onInvalidField: function (e, $el) { - // bail early if already focused - if (this.busy) { - return; - } // disable functionality for 1sec (allow next validation to work) - - - this.busy = true; - this.setTimeout(function () { - this.busy = false; - }, 1000); // open accordion - - this.open($el); - }, - onUnload: function (e) { - // vars - var order = []; // loop - - $('.acf-accordion').each(function () { - var open = $(this).hasClass('-open') ? 1 : 0; - order.push(open); - }); // set - - if (order.length) { - acf.setPreference('this.accordions', order); - } - } - }); })(jQuery); +(function($, undefined){ + + var Field = acf.Field.extend({ + + type: 'button_group', + + events: { + 'click input[type="radio"]': 'onClick' + }, + + $control: function(){ + return this.$('.acf-button-group'); + }, + + $input: function(){ + return this.$('input:checked'); + }, + + setValue: function( val ){ + this.$('input[value="' + val + '"]').prop('checked', true).trigger('change'); + }, + + onClick: function( e, $el ){ + + // vars + var $label = $el.parent('label'); + var selected = $label.hasClass('selected'); + + // remove previous selected + this.$('.selected').removeClass('selected'); + + // add active class + $label.addClass('selected'); + + // allow null + if( this.get('allow_null') && selected ) { + $label.removeClass('selected'); + $el.prop('checked', false).trigger('change'); + } + } + }); + + acf.registerFieldType( Field ); -(function ($, undefined) { - var Field = acf.Field.extend({ - type: 'button_group', - events: { - 'click input[type="radio"]': 'onClick' - }, - $control: function () { - return this.$('.acf-button-group'); - }, - $input: function () { - return this.$('input:checked'); - }, - setValue: function (val) { - this.$('input[value="' + val + '"]').prop('checked', true).trigger('change'); - }, - onClick: function (e, $el) { - // vars - var $label = $el.parent('label'); - var selected = $label.hasClass('selected'); // remove previous selected - - this.$('.selected').removeClass('selected'); // add active class - - $label.addClass('selected'); // allow null - - if (this.get('allow_null') && selected) { - $label.removeClass('selected'); - $el.prop('checked', false).trigger('change'); - } - } - }); - acf.registerFieldType(Field); })(jQuery); - -(function ($, undefined) { - var Field = acf.Field.extend({ - type: 'checkbox', - events: { - 'change input': 'onChange', - 'click .acf-add-checkbox': 'onClickAdd', - 'click .acf-checkbox-toggle': 'onClickToggle', - 'click .acf-checkbox-custom': 'onClickCustom' - }, - $control: function () { - return this.$('.acf-checkbox-list'); - }, - $toggle: function () { - return this.$('.acf-checkbox-toggle'); - }, - $input: function () { - return this.$('input[type="hidden"]'); - }, - $inputs: function () { - return this.$('input[type="checkbox"]').not('.acf-checkbox-toggle'); - }, - getValue: function () { - var val = []; - this.$(':checked').each(function () { - val.push($(this).val()); - }); - return val.length ? val : false; - }, - onChange: function (e, $el) { - // Vars. - var checked = $el.prop('checked'); - var $label = $el.parent('label'); - var $toggle = this.$toggle(); // Add or remove "selected" class. - - if (checked) { - $label.addClass('selected'); - } else { - $label.removeClass('selected'); - } // Update toggle state if all inputs are checked. - - - if ($toggle.length) { - var $inputs = this.$inputs(); // all checked - - if ($inputs.not(':checked').length == 0) { - $toggle.prop('checked', true); - } else { - $toggle.prop('checked', false); - } - } - }, - onClickAdd: function (e, $el) { - var html = '
  • '; - $el.parent('li').before(html); - }, - onClickToggle: function (e, $el) { - // Vars. - var checked = $el.prop('checked'); - var $inputs = this.$('input[type="checkbox"]'); - var $labels = this.$('label'); // Update "checked" state. - - $inputs.prop('checked', checked); // Add or remove "selected" class. - - if (checked) { - $labels.addClass('selected'); - } else { - $labels.removeClass('selected'); - } - }, - onClickCustom: function (e, $el) { - var checked = $el.prop('checked'); - var $text = $el.next('input[type="text"]'); // checked - - if (checked) { - $text.prop('disabled', false); // not checked - } else { - $text.prop('disabled', true); // remove - - if ($text.val() == '') { - $el.parent('li').remove(); - } - } - } - }); - acf.registerFieldType(Field); +(function($, undefined){ + + var Field = acf.Field.extend({ + + type: 'checkbox', + + events: { + 'change input': 'onChange', + 'click .acf-add-checkbox': 'onClickAdd', + 'click .acf-checkbox-toggle': 'onClickToggle', + 'click .acf-checkbox-custom': 'onClickCustom' + }, + + $control: function(){ + return this.$('.acf-checkbox-list'); + }, + + $toggle: function(){ + return this.$('.acf-checkbox-toggle'); + }, + + $input: function(){ + return this.$('input[type="hidden"]'); + }, + + $inputs: function(){ + return this.$('input[type="checkbox"]').not('.acf-checkbox-toggle'); + }, + + getValue: function(){ + var val = []; + this.$(':checked').each(function(){ + val.push( $(this).val() ); + }); + return val.length ? val : false; + }, + + onChange: function( e, $el ){ + + // Vars. + var checked = $el.prop('checked'); + var $label = $el.parent('label'); + var $toggle = this.$toggle(); + + // Add or remove "selected" class. + if( checked ) { + $label.addClass('selected'); + } else { + $label.removeClass('selected'); + } + + // Update toggle state if all inputs are checked. + if( $toggle.length ) { + var $inputs = this.$inputs(); + + // all checked + if( $inputs.not(':checked').length == 0 ) { + $toggle.prop('checked', true); + } else { + $toggle.prop('checked', false); + } + } + }, + + onClickAdd: function( e, $el ){ + var html = '
  • '; + $el.parent('li').before( html ); + }, + + onClickToggle: function( e, $el ){ + + // Vars. + var checked = $el.prop('checked'); + var $inputs = this.$('input[type="checkbox"]'); + var $labels = this.$('label'); + + // Update "checked" state. + $inputs.prop('checked', checked); + + // Add or remove "selected" class. + if( checked ) { + $labels.addClass('selected'); + } else { + $labels.removeClass('selected'); + } + }, + + onClickCustom: function( e, $el ){ + var checked = $el.prop('checked'); + var $text = $el.next('input[type="text"]'); + + // checked + if( checked ) { + $text.prop('disabled', false); + + // not checked + } else { + $text.prop('disabled', true); + + // remove + if( $text.val() == '' ) { + $el.parent('li').remove(); + } + } + } + }); + + acf.registerFieldType( Field ); + })(jQuery); +(function($, undefined){ + + var Field = acf.Field.extend({ + + type: 'color_picker', + + wait: 'load', + + events: { + 'duplicateField': 'onDuplicate' + }, -(function ($, undefined) { - var Field = acf.Field.extend({ - type: 'color_picker', - wait: 'load', - events: { - 'duplicateField': 'onDuplicate' - }, - $control: function () { - return this.$('.acf-color-picker'); - }, - $input: function () { - return this.$('input[type="hidden"]'); - }, - $inputText: function () { - return this.$('input[type="text"]'); - }, - setValue: function (val) { - // update input (with change) - acf.val(this.$input(), val); // update iris + $control: function(){ + return this.$('.acf-color-picker'); + }, + + $input: function(){ + return this.$('input[type="hidden"]'); + }, + + $inputText: function(){ + return this.$('input[type="text"]'); + }, + + setValue: function( val ){ + + // update input (with change) + acf.val( this.$input(), val ); + + // update iris + this.$inputText().iris('color', val); + }, + + initialize: function(){ + + // vars + var $input = this.$input(); + var $inputText = this.$inputText(); + + // event + var onChange = function( e ){ + + // timeout is required to ensure the $input val is correct + setTimeout(function(){ + acf.val( $input, $inputText.val() ); + }, 1); + } + + // args + var args = { + defaultColor: false, + palettes: true, + hide: true, + change: onChange, + clear: onChange + }; + + // filter + var args = acf.applyFilters('color_picker_args', args, this); + + // initialize + $inputText.wpColorPicker( args ); + }, - this.$inputText().iris('color', val); - }, - initialize: function () { - // vars - var $input = this.$input(); - var $inputText = this.$inputText(); // event - - var onChange = function (e) { - // timeout is required to ensure the $input val is correct - setTimeout(function () { - acf.val($input, $inputText.val()); - }, 1); - }; // args - - - var args = { - defaultColor: false, - palettes: true, - hide: true, - change: onChange, - clear: onChange - }; // filter - - var args = acf.applyFilters('color_picker_args', args, this); // initialize - - $inputText.wpColorPicker(args); - }, - onDuplicate: function (e, $el, $duplicate) { - // The wpColorPicker library does not provide a destroy method. - // Manually reset DOM by replacing elements back to their original state. - $colorPicker = $duplicate.find('.wp-picker-container'); - $inputText = $duplicate.find('input[type="text"]'); - $colorPicker.replaceWith($inputText); - } - }); - acf.registerFieldType(Field); + onDuplicate: function( e, $el, $duplicate ){ + + // The wpColorPicker library does not provide a destroy method. + // Manually reset DOM by replacing elements back to their original state. + $colorPicker = $duplicate.find('.wp-picker-container'); + $inputText = $duplicate.find('input[type="text"]'); + $colorPicker.replaceWith( $inputText ); + } + }); + + acf.registerFieldType( Field ); + })(jQuery); - -(function ($, undefined) { - var Field = acf.Field.extend({ - type: 'date_picker', - events: { - 'blur input[type="text"]': 'onBlur', - 'duplicateField': 'onDuplicate' - }, - $control: function () { - return this.$('.acf-date-picker'); - }, - $input: function () { - return this.$('input[type="hidden"]'); - }, - $inputText: function () { - return this.$('input[type="text"]'); - }, - initialize: function () { - // save_format: compatibility with ACF < 5.0.0 - if (this.has('save_format')) { - return this.initializeCompatibility(); - } // vars - - - var $input = this.$input(); - var $inputText = this.$inputText(); // args - - var args = { - dateFormat: this.get('date_format'), - altField: $input, - altFormat: 'yymmdd', - changeYear: true, - yearRange: "-100:+100", - changeMonth: true, - showButtonPanel: true, - firstDay: this.get('first_day') - }; // filter - - args = acf.applyFilters('date_picker_args', args, this); // add date picker - - acf.newDatePicker($inputText, args); // action - - acf.doAction('date_picker_init', $inputText, args, this); - }, - initializeCompatibility: function () { - // vars - var $input = this.$input(); - var $inputText = this.$inputText(); // get and set value from alt field - - $inputText.val($input.val()); // args - - var args = { - dateFormat: this.get('date_format'), - altField: $input, - altFormat: this.get('save_format'), - changeYear: true, - yearRange: "-100:+100", - changeMonth: true, - showButtonPanel: true, - firstDay: this.get('first_day') - }; // filter for 3rd party customization - - args = acf.applyFilters('date_picker_args', args, this); // backup - - var dateFormat = args.dateFormat; // change args.dateFormat - - args.dateFormat = this.get('save_format'); // add date picker - - acf.newDatePicker($inputText, args); // now change the format back to how it should be. - - $inputText.datepicker('option', 'dateFormat', dateFormat); // action for 3rd party customization - - acf.doAction('date_picker_init', $inputText, args, this); - }, - onBlur: function () { - if (!this.$inputText().val()) { - acf.val(this.$input(), ''); - } - }, - onDuplicate: function (e, $el, $duplicate) { - $duplicate.find('input[type="text"]').removeClass('hasDatepicker').removeAttr('id'); - } - }); - acf.registerFieldType(Field); // manager - - var datePickerManager = new acf.Model({ - priority: 5, - wait: 'ready', - initialize: function () { - // vars - var locale = acf.get('locale'); - var rtl = acf.get('rtl'); - var l10n = acf.get('datePickerL10n'); // bail ealry if no l10n - - if (!l10n) { - return false; - } // bail ealry if no datepicker library - - - if (typeof $.datepicker === 'undefined') { - return false; - } // rtl - - - l10n.isRTL = rtl; // append - - $.datepicker.regional[locale] = l10n; - $.datepicker.setDefaults(l10n); - } - }); // add - - acf.newDatePicker = function ($input, args) { - // bail ealry if no datepicker library - if (typeof $.datepicker === 'undefined') { - return false; - } // defaults - - - args = args || {}; // initialize - - $input.datepicker(args); // wrap the datepicker (only if it hasn't already been wrapped) - - if ($('body > #ui-datepicker-div').exists()) { - $('body > #ui-datepicker-div').wrap('
    '); - } - }; +(function($, undefined){ + + var Field = acf.Field.extend({ + + type: 'date_picker', + + events: { + 'blur input[type="text"]': 'onBlur', + 'duplicateField': 'onDuplicate' + }, + + $control: function(){ + return this.$('.acf-date-picker'); + }, + + $input: function(){ + return this.$('input[type="hidden"]'); + }, + + $inputText: function(){ + return this.$('input[type="text"]'); + }, + + initialize: function(){ + + // save_format: compatibility with ACF < 5.0.0 + if( this.has('save_format') ) { + return this.initializeCompatibility(); + } + + // vars + var $input = this.$input(); + var $inputText = this.$inputText(); + + // args + var args = { + dateFormat: this.get('date_format'), + altField: $input, + altFormat: 'yymmdd', + changeYear: true, + yearRange: "-100:+100", + changeMonth: true, + showButtonPanel: true, + firstDay: this.get('first_day') + }; + + // filter + args = acf.applyFilters('date_picker_args', args, this); + + // add date picker + acf.newDatePicker( $inputText, args ); + + // action + acf.doAction('date_picker_init', $inputText, args, this); + + }, + + initializeCompatibility: function(){ + + // vars + var $input = this.$input(); + var $inputText = this.$inputText(); + + // get and set value from alt field + $inputText.val( $input.val() ); + + // args + var args = { + dateFormat: this.get('date_format'), + altField: $input, + altFormat: this.get('save_format'), + changeYear: true, + yearRange: "-100:+100", + changeMonth: true, + showButtonPanel: true, + firstDay: this.get('first_day') + }; + + // filter for 3rd party customization + args = acf.applyFilters('date_picker_args', args, this); + + // backup + var dateFormat = args.dateFormat; + + // change args.dateFormat + args.dateFormat = this.get('save_format'); + + // add date picker + acf.newDatePicker( $inputText, args ); + + // now change the format back to how it should be. + $inputText.datepicker( 'option', 'dateFormat', dateFormat ); + + // action for 3rd party customization + acf.doAction('date_picker_init', $inputText, args, this); + }, + + onBlur: function(){ + if( !this.$inputText().val() ) { + acf.val( this.$input(), '' ); + } + }, + + onDuplicate: function( e, $el, $duplicate ){ + $duplicate.find('input[type="text"]').removeClass('hasDatepicker').removeAttr('id'); + } + }); + + acf.registerFieldType( Field ); + + + // manager + var datePickerManager = new acf.Model({ + priority: 5, + wait: 'ready', + initialize: function(){ + + // vars + var locale = acf.get('locale'); + var rtl = acf.get('rtl'); + var l10n = acf.get('datePickerL10n'); + + // bail ealry if no l10n + if( !l10n ) { + return false; + } + + // bail ealry if no datepicker library + if( typeof $.datepicker === 'undefined' ) { + return false; + } + + // rtl + l10n.isRTL = rtl; + + // append + $.datepicker.regional[ locale ] = l10n; + $.datepicker.setDefaults(l10n); + } + }); + + // add + acf.newDatePicker = function( $input, args ){ + + // bail ealry if no datepicker library + if( typeof $.datepicker === 'undefined' ) { + return false; + } + + // defaults + args = args || {}; + + // initialize + $input.datepicker( args ); + + // wrap the datepicker (only if it hasn't already been wrapped) + if( $('body > #ui-datepicker-div').exists() ) { + $('body > #ui-datepicker-div').wrap('
    '); + } + }; + })(jQuery); - -(function ($, undefined) { - var Field = acf.models.DatePickerField.extend({ - type: 'date_time_picker', - $control: function () { - return this.$('.acf-date-time-picker'); - }, - initialize: function () { - // vars - var $input = this.$input(); - var $inputText = this.$inputText(); // args - - var args = { - dateFormat: this.get('date_format'), - timeFormat: this.get('time_format'), - altField: $input, - altFieldTimeOnly: false, - altFormat: 'yy-mm-dd', - altTimeFormat: 'HH:mm:ss', - changeYear: true, - yearRange: "-100:+100", - changeMonth: true, - showButtonPanel: true, - firstDay: this.get('first_day'), - controlType: 'select', - oneLine: true - }; // filter - - args = acf.applyFilters('date_time_picker_args', args, this); // add date time picker - - acf.newDateTimePicker($inputText, args); // action - - acf.doAction('date_time_picker_init', $inputText, args, this); - } - }); - acf.registerFieldType(Field); // manager - - var dateTimePickerManager = new acf.Model({ - priority: 5, - wait: 'ready', - initialize: function () { - // vars - var locale = acf.get('locale'); - var rtl = acf.get('rtl'); - var l10n = acf.get('dateTimePickerL10n'); // bail ealry if no l10n - - if (!l10n) { - return false; - } // bail ealry if no datepicker library - - - if (typeof $.timepicker === 'undefined') { - return false; - } // rtl - - - l10n.isRTL = rtl; // append - - $.timepicker.regional[locale] = l10n; - $.timepicker.setDefaults(l10n); - } - }); // add - - acf.newDateTimePicker = function ($input, args) { - // bail ealry if no datepicker library - if (typeof $.timepicker === 'undefined') { - return false; - } // defaults - - - args = args || {}; // initialize - - $input.datetimepicker(args); // wrap the datepicker (only if it hasn't already been wrapped) - - if ($('body > #ui-datepicker-div').exists()) { - $('body > #ui-datepicker-div').wrap('
    '); - } - }; +(function($, undefined){ + + var Field = acf.models.DatePickerField.extend({ + + type: 'date_time_picker', + + $control: function(){ + return this.$('.acf-date-time-picker'); + }, + + initialize: function(){ + + // vars + var $input = this.$input(); + var $inputText = this.$inputText(); + + // args + var args = { + dateFormat: this.get('date_format'), + timeFormat: this.get('time_format'), + altField: $input, + altFieldTimeOnly: false, + altFormat: 'yy-mm-dd', + altTimeFormat: 'HH:mm:ss', + changeYear: true, + yearRange: "-100:+100", + changeMonth: true, + showButtonPanel: true, + firstDay: this.get('first_day'), + controlType: 'select', + oneLine: true + }; + + // filter + args = acf.applyFilters('date_time_picker_args', args, this); + + // add date time picker + acf.newDateTimePicker( $inputText, args ); + + // action + acf.doAction('date_time_picker_init', $inputText, args, this); + } + }); + + acf.registerFieldType( Field ); + + + // manager + var dateTimePickerManager = new acf.Model({ + priority: 5, + wait: 'ready', + initialize: function(){ + + // vars + var locale = acf.get('locale'); + var rtl = acf.get('rtl'); + var l10n = acf.get('dateTimePickerL10n'); + + // bail ealry if no l10n + if( !l10n ) { + return false; + } + + // bail ealry if no datepicker library + if( typeof $.timepicker === 'undefined' ) { + return false; + } + + // rtl + l10n.isRTL = rtl; + + // append + $.timepicker.regional[ locale ] = l10n; + $.timepicker.setDefaults(l10n); + } + }); + + + // add + acf.newDateTimePicker = function( $input, args ){ + + // bail ealry if no datepicker library + if( typeof $.timepicker === 'undefined' ) { + return false; + } + + // defaults + args = args || {}; + + // initialize + $input.datetimepicker( args ); + + // wrap the datepicker (only if it hasn't already been wrapped) + if( $('body > #ui-datepicker-div').exists() ) { + $('body > #ui-datepicker-div').wrap('
    '); + } + }; + })(jQuery); - -(function ($, undefined) { - var Field = acf.Field.extend({ - type: 'google_map', - map: false, - wait: 'load', - events: { - 'click a[data-name="clear"]': 'onClickClear', - 'click a[data-name="locate"]': 'onClickLocate', - 'click a[data-name="search"]': 'onClickSearch', - 'keydown .search': 'onKeydownSearch', - 'keyup .search': 'onKeyupSearch', - 'focus .search': 'onFocusSearch', - 'blur .search': 'onBlurSearch', - 'showField': 'onShow' - }, - $control: function () { - return this.$('.acf-google-map'); - }, - $search: function () { - return this.$('.search'); - }, - $canvas: function () { - return this.$('.canvas'); - }, - setState: function (state) { - // Remove previous state classes. - this.$control().removeClass('-value -loading -searching'); // Determine auto state based of current value. - - if (state === 'default') { - state = this.val() ? 'value' : ''; - } // Update state class. - - - if (state) { - this.$control().addClass('-' + state); - } - }, - getValue: function () { - var val = this.$input().val(); - - if (val) { - return JSON.parse(val); - } else { - return false; - } - }, - setValue: function (val, silent) { - // Convert input value. - var valAttr = ''; - - if (val) { - valAttr = JSON.stringify(val); - } // Update input (with change). - - - acf.val(this.$input(), valAttr); // Bail early if silent update. - - if (silent) { - return; - } // Render. - - - this.renderVal(val); - /** - * Fires immediately after the value has changed. - * - * @date 12/02/2014 - * @since 5.0.0 - * - * @param object|string val The new value. - * @param object map The Google Map isntance. - * @param object field The field instance. - */ - - acf.doAction('google_map_change', val, this.map, this); - }, - renderVal: function (val) { - // Value. - if (val) { - this.setState('value'); - this.$search().val(val.address); - this.setPosition(val.lat, val.lng); // No value. - } else { - this.setState(''); - this.$search().val(''); - this.map.marker.setVisible(false); - } - }, - newLatLng: function (lat, lng) { - return new google.maps.LatLng(parseFloat(lat), parseFloat(lng)); - }, - setPosition: function (lat, lng) { - // Update marker position. - this.map.marker.setPosition({ - lat: parseFloat(lat), - lng: parseFloat(lng) - }); // Show marker. - - this.map.marker.setVisible(true); // Center map. - - this.center(); - }, - center: function () { - // Find marker position. - var position = this.map.marker.getPosition(); - - if (position) { - var lat = position.lat(); - var lng = position.lng(); // Or find default settings. - } else { - var lat = this.get('lat'); - var lng = this.get('lng'); - } // Center map. - - - this.map.setCenter({ - lat: parseFloat(lat), - lng: parseFloat(lng) - }); - }, - initialize: function () { - // Ensure Google API is loaded and then initialize map. - withAPI(this.initializeMap.bind(this)); - }, - initializeMap: function () { - // Get value ignoring conditional logic status. - var val = this.getValue(); // Construct default args. - - var args = acf.parseArgs(val, { - zoom: this.get('zoom'), - lat: this.get('lat'), - lng: this.get('lng') - }); // Create Map. - - var mapArgs = { - scrollwheel: false, - zoom: parseInt(args.zoom), - center: { - lat: parseFloat(args.lat), - lng: parseFloat(args.lng) - }, - mapTypeId: google.maps.MapTypeId.ROADMAP, - marker: { - draggable: true, - raiseOnDrag: true - }, - autocomplete: {} - }; - mapArgs = acf.applyFilters('google_map_args', mapArgs, this); - var map = new google.maps.Map(this.$canvas()[0], mapArgs); // Create Marker. - - var markerArgs = acf.parseArgs(mapArgs.marker, { - draggable: true, - raiseOnDrag: true, - map: map - }); - markerArgs = acf.applyFilters('google_map_marker_args', markerArgs, this); - var marker = new google.maps.Marker(markerArgs); // Maybe Create Autocomplete. - - var autocomplete = false; - - if (acf.isset(google, 'maps', 'places', 'Autocomplete')) { - var autocompleteArgs = mapArgs.autocomplete || {}; - autocompleteArgs = acf.applyFilters('google_map_autocomplete_args', autocompleteArgs, this); - autocomplete = new google.maps.places.Autocomplete(this.$search()[0], autocompleteArgs); - autocomplete.bindTo('bounds', map); - } // Add map events. - - - this.addMapEvents(this, map, marker, autocomplete); // Append references. - - map.acf = this; - map.marker = marker; - map.autocomplete = autocomplete; - this.map = map; // Set position. - - if (val) { - this.setPosition(val.lat, val.lng); - } - /** - * Fires immediately after the Google Map has been initialized. - * - * @date 12/02/2014 - * @since 5.0.0 - * - * @param object map The Google Map isntance. - * @param object marker The Google Map marker isntance. - * @param object field The field instance. - */ - - - acf.doAction('google_map_init', map, marker, this); - }, - addMapEvents: function (field, map, marker, autocomplete) { - // Click map. - google.maps.event.addListener(map, 'click', function (e) { - var lat = e.latLng.lat(); - var lng = e.latLng.lng(); - field.searchPosition(lat, lng); - }); // Drag marker. - - google.maps.event.addListener(marker, 'dragend', function () { - var lat = this.getPosition().lat(); - var lng = this.getPosition().lng(); - field.searchPosition(lat, lng); - }); // Autocomplete search. - - if (autocomplete) { - google.maps.event.addListener(autocomplete, 'place_changed', function () { - var place = this.getPlace(); - field.searchPlace(place); - }); - } // Detect zoom change. - - - google.maps.event.addListener(map, 'zoom_changed', function () { - var val = field.val(); - - if (val) { - val.zoom = map.getZoom(); - field.setValue(val, true); - } - }); - }, - searchPosition: function (lat, lng) { - //console.log('searchPosition', lat, lng ); - // Start Loading. - this.setState('loading'); // Query Geocoder. - - var latLng = { - lat: lat, - lng: lng - }; - geocoder.geocode({ - location: latLng - }, function (results, status) { - //console.log('searchPosition', arguments ); - // End Loading. - this.setState(''); // Status failure. - - if (status !== 'OK') { - this.showNotice({ - text: acf.__('Location not found: %s').replace('%s', status), - type: 'warning' - }); // Success. - } else { - var val = this.parseResult(results[0]); // Override lat/lng to match user defined marker location. - // Avoids issue where marker "snaps" to nearest result. - - val.lat = lat; - val.lng = lng; - this.val(val); - } - }.bind(this)); - }, - searchPlace: function (place) { - //console.log('searchPlace', place ); - // Bail early if no place. - if (!place) { - return; - } // Selecting from the autocomplete dropdown will return a rich PlaceResult object. - // Be sure to over-write the "formatted_address" value with the one displayed to the user for best UX. - - - if (place.geometry) { - place.formatted_address = this.$search().val(); - var val = this.parseResult(place); - this.val(val); // Searching a custom address will return an empty PlaceResult object. - } else if (place.name) { - this.searchAddress(place.name); - } - }, - searchAddress: function (address) { - //console.log('searchAddress', address ); - // Bail early if no address. - if (!address) { - return; - } // Allow "lat,lng" search. - - - var latLng = address.split(','); - - if (latLng.length == 2) { - var lat = parseFloat(latLng[0]); - var lng = parseFloat(latLng[1]); - - if (lat && lng) { - return this.searchPosition(lat, lng); - } - } // Start Loading. - - - this.setState('loading'); // Query Geocoder. - - geocoder.geocode({ - address: address - }, function (results, status) { - //console.log('searchPosition', arguments ); - // End Loading. - this.setState(''); // Status failure. - - if (status !== 'OK') { - this.showNotice({ - text: acf.__('Location not found: %s').replace('%s', status), - type: 'warning' - }); // Success. - } else { - var val = this.parseResult(results[0]); // Override address data with parameter allowing custom address to be defined in search. - - val.address = address; // Update value. - - this.val(val); - } - }.bind(this)); - }, - searchLocation: function () { - //console.log('searchLocation' ); - // Check HTML5 geolocation. - if (!navigator.geolocation) { - return alert(acf.__('Sorry, this browser does not support geolocation')); - } // Start Loading. - - - this.setState('loading'); // Query Geolocation. - - navigator.geolocation.getCurrentPosition( // Success. - function (results) { - // End Loading. - this.setState(''); // Search position. - - var lat = results.coords.latitude; - var lng = results.coords.longitude; - this.searchPosition(lat, lng); - }.bind(this), // Failure. - function (error) { - this.setState(''); - }.bind(this)); - }, - - /** - * parseResult - * - * Returns location data for the given GeocoderResult object. - * - * @date 15/10/19 - * @since 5.8.6 - * - * @param object obj A GeocoderResult object. - * @return object - */ - parseResult: function (obj) { - // Construct basic data. - var result = { - address: obj.formatted_address, - lat: obj.geometry.location.lat(), - lng: obj.geometry.location.lng() - }; // Add zoom level. - - result.zoom = this.map.getZoom(); // Add place ID. - - if (obj.place_id) { - result.place_id = obj.place_id; - } // Add place name. - - - if (obj.name) { - result.name = obj.name; - } // Create search map for address component data. - - - var map = { - street_number: ['street_number'], - street_name: ['street_address', 'route'], - city: ['locality'], - state: ['administrative_area_level_1', 'administrative_area_level_2', 'administrative_area_level_3', 'administrative_area_level_4', 'administrative_area_level_5'], - post_code: ['postal_code'], - country: ['country'] - }; // Loop over map. - - for (var k in map) { - var keywords = map[k]; // Loop over address components. - - for (var i = 0; i < obj.address_components.length; i++) { - var component = obj.address_components[i]; - var component_type = component.types[0]; // Look for matching component type. - - if (keywords.indexOf(component_type) !== -1) { - // Append to result. - result[k] = component.long_name; // Append short version. - - if (component.long_name !== component.short_name) { - result[k + '_short'] = component.short_name; - } - } - } - } - /** - * Filters the parsed result. - * - * @date 18/10/19 - * @since 5.8.6 - * - * @param object result The parsed result value. - * @param object obj The GeocoderResult object. - */ - - - return acf.applyFilters('google_map_result', result, obj, this.map, this); - }, - onClickClear: function () { - this.val(false); - }, - onClickLocate: function () { - this.searchLocation(); - }, - onClickSearch: function () { - this.searchAddress(this.$search().val()); - }, - onFocusSearch: function (e, $el) { - this.setState('searching'); - }, - onBlurSearch: function (e, $el) { - // Get saved address value. - var val = this.val(); - var address = val ? val.address : ''; // Remove 'is-searching' if value has not changed. - - if ($el.val() === address) { - this.setState('default'); - } - }, - onKeyupSearch: function (e, $el) { - // Clear empty value. - if (!$el.val()) { - this.val(false); - } - }, - // Prevent form from submitting. - onKeydownSearch: function (e, $el) { - if (e.which == 13) { - e.preventDefault(); - $el.blur(); - } - }, - // Center map once made visible. - onShow: function () { - if (this.map) { - this.setTimeout(this.center); - } - } - }); - acf.registerFieldType(Field); // Vars. - - var loading = false; - var geocoder = false; - /** - * withAPI - * - * Loads the Google Maps API library and troggers callback. - * - * @date 28/3/19 - * @since 5.7.14 - * - * @param function callback The callback to excecute. - * @return void - */ - - function withAPI(callback) { - // Check if geocoder exists. - if (geocoder) { - return callback(); - } // Check if geocoder API exists. - - - if (acf.isset(window, 'google', 'maps', 'Geocoder')) { - geocoder = new google.maps.Geocoder(); - return callback(); - } // Geocoder will need to be loaded. Hook callback to action. - - - acf.addAction('google_map_api_loaded', callback); // Bail early if already loading API. - - if (loading) { - return; - } // load api - - - var url = acf.get('google_map_api'); - - if (url) { - // Set loading status. - loading = true; // Load API - - $.ajax({ - url: url, - dataType: 'script', - cache: true, - success: function () { - geocoder = new google.maps.Geocoder(); - acf.doAction('google_map_api_loaded'); - } - }); - } - } +(function($, undefined){ + + var Field = acf.Field.extend({ + + type: 'google_map', + + map: false, + + wait: 'load', + + events: { + 'click a[data-name="clear"]': 'onClickClear', + 'click a[data-name="locate"]': 'onClickLocate', + 'click a[data-name="search"]': 'onClickSearch', + 'keydown .search': 'onKeydownSearch', + 'keyup .search': 'onKeyupSearch', + 'focus .search': 'onFocusSearch', + 'blur .search': 'onBlurSearch', + 'showField': 'onShow', + }, + + $control: function(){ + return this.$('.acf-google-map'); + }, + + $search: function(){ + return this.$('.search'); + }, + + $canvas: function(){ + return this.$('.canvas'); + }, + + setState: function( state ){ + + // Remove previous state classes. + this.$control().removeClass( '-value -loading -searching' ); + + // Determine auto state based of current value. + if( state === 'default' ) { + state = this.val() ? 'value' : ''; + } + + // Update state class. + if( state ) { + this.$control().addClass( '-' + state ); + } + }, + + getValue: function(){ + var val = this.$input().val(); + if( val ) { + return JSON.parse( val ) + } else { + return false; + } + }, + + setValue: function( val, silent ){ + + // Convert input value. + var valAttr = ''; + if( val ) { + valAttr = JSON.stringify( val ); + } + + // Update input (with change). + acf.val( this.$input(), valAttr ); + + // Bail early if silent update. + if( silent ) { + return; + } + + // Render. + this.renderVal( val ); + + /** + * Fires immediately after the value has changed. + * + * @date 12/02/2014 + * @since 5.0.0 + * + * @param object|string val The new value. + * @param object map The Google Map isntance. + * @param object field The field instance. + */ + acf.doAction('google_map_change', val, this.map, this); + }, + + renderVal: function( val ){ + + // Value. + if( val ) { + this.setState( 'value' ); + this.$search().val( val.address ); + this.setPosition( val.lat, val.lng ); + + // No value. + } else { + this.setState( '' ); + this.$search().val( '' ); + this.map.marker.setVisible( false ); + } + }, + + newLatLng: function( lat, lng ){ + return new google.maps.LatLng( parseFloat(lat), parseFloat(lng) ); + }, + + setPosition: function( lat, lng ){ + + // Update marker position. + this.map.marker.setPosition({ + lat: parseFloat(lat), + lng: parseFloat(lng) + }); + + // Show marker. + this.map.marker.setVisible( true ); + + // Center map. + this.center(); + }, + + center: function(){ + + // Find marker position. + var position = this.map.marker.getPosition(); + if( position ) { + var lat = position.lat(); + var lng = position.lng(); + + // Or find default settings. + } else { + var lat = this.get('lat'); + var lng = this.get('lng'); + } + + // Center map. + this.map.setCenter({ + lat: parseFloat(lat), + lng: parseFloat(lng) + }); + }, + + initialize: function(){ + + // Ensure Google API is loaded and then initialize map. + withAPI( this.initializeMap.bind(this) ); + }, + + initializeMap: function(){ + + // Get value ignoring conditional logic status. + var val = this.getValue(); + + // Construct default args. + var args = acf.parseArgs(val, { + zoom: this.get('zoom'), + lat: this.get('lat'), + lng: this.get('lng') + }); + + // Create Map. + var mapArgs = { + scrollwheel: false, + zoom: parseInt( args.zoom ), + center: { + lat: parseFloat( args.lat ), + lng: parseFloat( args.lng ) + }, + mapTypeId: google.maps.MapTypeId.ROADMAP, + marker: { + draggable: true, + raiseOnDrag: true + }, + autocomplete: {} + }; + mapArgs = acf.applyFilters('google_map_args', mapArgs, this); + var map = new google.maps.Map( this.$canvas()[0], mapArgs ); + + // Create Marker. + var markerArgs = acf.parseArgs(mapArgs.marker, { + draggable: true, + raiseOnDrag: true, + map: map + }); + markerArgs = acf.applyFilters('google_map_marker_args', markerArgs, this); + var marker = new google.maps.Marker( markerArgs ); + + // Maybe Create Autocomplete. + var autocomplete = false; + if( acf.isset(google, 'maps', 'places', 'Autocomplete') ) { + var autocompleteArgs = mapArgs.autocomplete || {}; + autocompleteArgs = acf.applyFilters('google_map_autocomplete_args', autocompleteArgs, this); + autocomplete = new google.maps.places.Autocomplete( this.$search()[0], autocompleteArgs ); + autocomplete.bindTo('bounds', map); + } + + // Add map events. + this.addMapEvents( this, map, marker, autocomplete ); + + // Append references. + map.acf = this; + map.marker = marker; + map.autocomplete = autocomplete; + this.map = map; + + // Set position. + if( val ) { + this.setPosition( val.lat, val.lng ); + } + + /** + * Fires immediately after the Google Map has been initialized. + * + * @date 12/02/2014 + * @since 5.0.0 + * + * @param object map The Google Map isntance. + * @param object marker The Google Map marker isntance. + * @param object field The field instance. + */ + acf.doAction('google_map_init', map, marker, this); + }, + + addMapEvents: function( field, map, marker, autocomplete ){ + + // Click map. + google.maps.event.addListener( map, 'click', function( e ) { + var lat = e.latLng.lat(); + var lng = e.latLng.lng(); + field.searchPosition( lat, lng ); + }); + + // Drag marker. + google.maps.event.addListener( marker, 'dragend', function(){ + var lat = this.getPosition().lat(); + var lng = this.getPosition().lng(); + field.searchPosition( lat, lng ); + }); + + // Autocomplete search. + if( autocomplete ) { + google.maps.event.addListener(autocomplete, 'place_changed', function() { + var place = this.getPlace(); + field.searchPlace( place ); + }); + } + + // Detect zoom change. + google.maps.event.addListener( map, 'zoom_changed', function(){ + var val = field.val(); + if( val ) { + val.zoom = map.getZoom(); + field.setValue( val, true ); + } + }); + }, + + searchPosition: function( lat, lng ){ + //console.log('searchPosition', lat, lng ); + + // Start Loading. + this.setState( 'loading' ); + + // Query Geocoder. + var latLng = { lat: lat, lng: lng }; + geocoder.geocode({ location: latLng }, function( results, status ){ + //console.log('searchPosition', arguments ); + + // End Loading. + this.setState( '' ); + + // Status failure. + if( status !== 'OK' ) { + this.showNotice({ + text: acf.__('Location not found: %s').replace('%s', status), + type: 'warning' + }); + + // Success. + } else { + var val = this.parseResult( results[0] ); + + // Override lat/lng to match user defined marker location. + // Avoids issue where marker "snaps" to nearest result. + val.lat = lat; + val.lng = lng; + this.val( val ); + } + + }.bind( this )); + }, + + searchPlace: function( place ){ + //console.log('searchPlace', place ); + + // Bail early if no place. + if( !place ) { + return; + } + + // Selecting from the autocomplete dropdown will return a rich PlaceResult object. + // Be sure to over-write the "formatted_address" value with the one displayed to the user for best UX. + if( place.geometry ) { + place.formatted_address = this.$search().val(); + var val = this.parseResult( place ); + this.val( val ); + + // Searching a custom address will return an empty PlaceResult object. + } else if( place.name ) { + this.searchAddress( place.name ); + } + }, + + searchAddress: function( address ){ + //console.log('searchAddress', address ); + + // Bail early if no address. + if( !address ) { + return; + } + + // Allow "lat,lng" search. + var latLng = address.split(','); + if( latLng.length == 2 ) { + var lat = parseFloat(latLng[0]); + var lng = parseFloat(latLng[1]); + if( lat && lng ) { + return this.searchPosition( lat, lng ); + } + } + + // Start Loading. + this.setState( 'loading' ); + + // Query Geocoder. + geocoder.geocode({ address: address }, function( results, status ){ + //console.log('searchPosition', arguments ); + + // End Loading. + this.setState( '' ); + + // Status failure. + if( status !== 'OK' ) { + this.showNotice({ + text: acf.__('Location not found: %s').replace('%s', status), + type: 'warning' + }); + + // Success. + } else { + var val = this.parseResult( results[0] ); + + // Override address data with parameter allowing custom address to be defined in search. + val.address = address; + + // Update value. + this.val( val ); + } + + }.bind( this )); + }, + + searchLocation: function(){ + //console.log('searchLocation' ); + + // Check HTML5 geolocation. + if( !navigator.geolocation ) { + return alert( acf.__('Sorry, this browser does not support geolocation') ); + } + + // Start Loading. + this.setState( 'loading' ); + + // Query Geolocation. + navigator.geolocation.getCurrentPosition( + + // Success. + function( results ){ + + // End Loading. + this.setState( '' ); + + // Search position. + var lat = results.coords.latitude; + var lng = results.coords.longitude; + this.searchPosition( lat, lng ); + + }.bind(this), + + // Failure. + function( error ){ + this.setState( '' ); + }.bind(this) + ); + }, + + /** + * parseResult + * + * Returns location data for the given GeocoderResult object. + * + * @date 15/10/19 + * @since 5.8.6 + * + * @param object obj A GeocoderResult object. + * @return object + */ + parseResult: function( obj ) { + + // Construct basic data. + var result = { + address: obj.formatted_address, + lat: obj.geometry.location.lat(), + lng: obj.geometry.location.lng(), + }; + + // Add zoom level. + result.zoom = this.map.getZoom(); + + // Add place ID. + if( obj.place_id ) { + result.place_id = obj.place_id; + } + + // Add place name. + if( obj.name ) { + result.name = obj.name; + } + + // Create search map for address component data. + var map = { + street_number: [ 'street_number' ], + street_name: [ 'street_address', 'route' ], + city: [ 'locality' ], + state: [ + 'administrative_area_level_1', + 'administrative_area_level_2', + 'administrative_area_level_3', + 'administrative_area_level_4', + 'administrative_area_level_5' + ], + post_code: [ 'postal_code' ], + country: [ 'country' ] + }; + + // Loop over map. + for( var k in map ) { + var keywords = map[ k ]; + + // Loop over address components. + for( var i = 0; i < obj.address_components.length; i++ ) { + var component = obj.address_components[ i ]; + var component_type = component.types[0]; + + // Look for matching component type. + if( keywords.indexOf(component_type) !== -1 ) { + + // Append to result. + result[ k ] = component.long_name; + + // Append short version. + if( component.long_name !== component.short_name ) { + result[ k + '_short' ] = component.short_name; + } + } + } + } + + /** + * Filters the parsed result. + * + * @date 18/10/19 + * @since 5.8.6 + * + * @param object result The parsed result value. + * @param object obj The GeocoderResult object. + */ + return acf.applyFilters('google_map_result', result, obj, this.map, this); + }, + + onClickClear: function(){ + this.val( false ); + }, + + onClickLocate: function(){ + this.searchLocation(); + }, + + onClickSearch: function(){ + this.searchAddress( this.$search().val() ); + }, + + onFocusSearch: function( e, $el ){ + this.setState( 'searching' ); + }, + + onBlurSearch: function( e, $el ){ + + // Get saved address value. + var val = this.val(); + var address = val ? val.address : ''; + + // Remove 'is-searching' if value has not changed. + if( $el.val() === address ) { + this.setState( 'default' ); + } + }, + + onKeyupSearch: function( e, $el ){ + + // Clear empty value. + if( !$el.val() ) { + this.val( false ); + } + }, + + // Prevent form from submitting. + onKeydownSearch: function( e, $el ){ + if( e.which == 13 ) { + e.preventDefault(); + $el.blur(); + } + }, + + // Center map once made visible. + onShow: function(){ + if( this.map ) { + this.setTimeout( this.center ); + } + }, + }); + + acf.registerFieldType( Field ); + + // Vars. + var loading = false; + var geocoder = false; + + /** + * withAPI + * + * Loads the Google Maps API library and troggers callback. + * + * @date 28/3/19 + * @since 5.7.14 + * + * @param function callback The callback to excecute. + * @return void + */ + + function withAPI( callback ) { + + // Check if geocoder exists. + if( geocoder ) { + return callback(); + } + + // Check if geocoder API exists. + if( acf.isset(window, 'google', 'maps', 'Geocoder') ) { + geocoder = new google.maps.Geocoder(); + return callback(); + } + + // Geocoder will need to be loaded. Hook callback to action. + acf.addAction( 'google_map_api_loaded', callback ); + + // Bail early if already loading API. + if( loading ) { + return; + } + + // load api + var url = acf.get('google_map_api'); + if( url ) { + + // Set loading status. + loading = true; + + // Load API + $.ajax({ + url: url, + dataType: 'script', + cache: true, + success: function(){ + geocoder = new google.maps.Geocoder(); + acf.doAction('google_map_api_loaded'); + } + }); + } + } + })(jQuery); +(function($, undefined){ + + var Field = acf.Field.extend({ + + type: 'image', + + $control: function(){ + return this.$('.acf-image-uploader'); + }, + + $input: function(){ + return this.$('input[type="hidden"]'); + }, + + events: { + 'click a[data-name="add"]': 'onClickAdd', + 'click a[data-name="edit"]': 'onClickEdit', + 'click a[data-name="remove"]': 'onClickRemove', + 'change input[type="file"]': 'onChange' + }, + + initialize: function(){ + + // add attribute to form + if( this.get('uploader') === 'basic' ) { + this.$el.closest('form').attr('enctype', 'multipart/form-data'); + } + }, + + validateAttachment: function( attachment ){ + + // Use WP attachment attributes when available. + if( attachment && attachment.attributes ) { + attachment = attachment.attributes; + } + + // Apply defaults. + attachment = acf.parseArgs(attachment, { + id: 0, + url: '', + alt: '', + title: '', + caption: '', + description: '', + width: 0, + height: 0 + }); + + // Override with "preview size". + var size = acf.isget( attachment, 'sizes', this.get('preview_size') ); + if( size ) { + attachment.url = size.url; + attachment.width = size.width; + attachment.height = size.height; + } + + // Return. + return attachment; + }, + + render: function( attachment ){ + attachment = this.validateAttachment( attachment ); + + // Update DOM. + this.$('img').attr({ + src: attachment.url, + alt: attachment.alt + }); + if( attachment.id ) { + this.val( attachment.id ); + this.$control().addClass('has-value'); + } else { + this.val( '' ); + this.$control().removeClass('has-value'); + } + }, + + // create a new repeater row and render value + append: function( attachment, parent ){ + + // create function to find next available field within parent + var getNext = function( field, parent ){ + + // find existing file fields within parent + var fields = acf.getFields({ + key: field.get('key'), + parent: parent.$el + }); + + // find the first field with no value + for( var i = 0; i < fields.length; i++ ) { + if( !fields[i].val() ) { + return fields[i]; + } + } + + // return + return false; + } + + // find existing file fields within parent + var field = getNext( this, parent ); + + // add new row if no available field + if( !field ) { + parent.$('.acf-button:last').trigger('click'); + field = getNext( this, parent ); + } + + // render + if( field ) { + field.render( attachment ); + } + }, + + selectAttachment: function(){ + + // vars + var parent = this.parent(); + var multiple = (parent && parent.get('type') === 'repeater'); + + // new frame + var frame = acf.newMediaPopup({ + mode: 'select', + type: 'image', + title: acf.__('Select Image'), + field: this.get('key'), + multiple: multiple, + library: this.get('library'), + allowedTypes: this.get('mime_types'), + select: $.proxy(function( attachment, i ) { + if( i > 0 ) { + this.append( attachment, parent ); + } else { + this.render( attachment ); + } + }, this) + }); + }, + + editAttachment: function(){ + + // vars + var val = this.val(); + + // bail early if no val + if( !val ) return; + + // popup + var frame = acf.newMediaPopup({ + mode: 'edit', + title: acf.__('Edit Image'), + button: acf.__('Update Image'), + attachment: val, + field: this.get('key'), + select: $.proxy(function( attachment, i ) { + this.render( attachment ); + }, this) + }); + }, + + removeAttachment: function(){ + this.render( false ); + }, + + onClickAdd: function( e, $el ){ + this.selectAttachment(); + }, + + onClickEdit: function( e, $el ){ + this.editAttachment(); + }, + + onClickRemove: function( e, $el ){ + this.removeAttachment(); + }, + + onChange: function( e, $el ){ + var $hiddenInput = this.$input(); + + acf.getFileInputData($el, function( data ){ + $hiddenInput.val( $.param(data) ); + }); + } + }); + + acf.registerFieldType( Field ); -(function ($, undefined) { - var Field = acf.Field.extend({ - type: 'image', - $control: function () { - return this.$('.acf-image-uploader'); - }, - $input: function () { - return this.$('input[type="hidden"]'); - }, - events: { - 'click a[data-name="add"]': 'onClickAdd', - 'click a[data-name="edit"]': 'onClickEdit', - 'click a[data-name="remove"]': 'onClickRemove', - 'change input[type="file"]': 'onChange' - }, - initialize: function () { - // add attribute to form - if (this.get('uploader') === 'basic') { - this.$el.closest('form').attr('enctype', 'multipart/form-data'); - } - }, - validateAttachment: function (attachment) { - // Use WP attachment attributes when available. - if (attachment && attachment.attributes) { - attachment = attachment.attributes; - } // Apply defaults. - - - attachment = acf.parseArgs(attachment, { - id: 0, - url: '', - alt: '', - title: '', - caption: '', - description: '', - width: 0, - height: 0 - }); // Override with "preview size". - - var size = acf.isget(attachment, 'sizes', this.get('preview_size')); - - if (size) { - attachment.url = size.url; - attachment.width = size.width; - attachment.height = size.height; - } // Return. - - - return attachment; - }, - render: function (attachment) { - attachment = this.validateAttachment(attachment); // Update DOM. - - this.$('img').attr({ - src: attachment.url, - alt: attachment.alt - }); - - if (attachment.id) { - this.val(attachment.id); - this.$control().addClass('has-value'); - } else { - this.val(''); - this.$control().removeClass('has-value'); - } - }, - // create a new repeater row and render value - append: function (attachment, parent) { - // create function to find next available field within parent - var getNext = function (field, parent) { - // find existing file fields within parent - var fields = acf.getFields({ - key: field.get('key'), - parent: parent.$el - }); // find the first field with no value - - for (var i = 0; i < fields.length; i++) { - if (!fields[i].val()) { - return fields[i]; - } - } // return - - - return false; - }; // find existing file fields within parent - - - var field = getNext(this, parent); // add new row if no available field - - if (!field) { - parent.$('.acf-button:last').trigger('click'); - field = getNext(this, parent); - } // render - - - if (field) { - field.render(attachment); - } - }, - selectAttachment: function () { - // vars - var parent = this.parent(); - var multiple = parent && parent.get('type') === 'repeater'; // new frame - - var frame = acf.newMediaPopup({ - mode: 'select', - type: 'image', - title: acf.__('Select Image'), - field: this.get('key'), - multiple: multiple, - library: this.get('library'), - allowedTypes: this.get('mime_types'), - select: $.proxy(function (attachment, i) { - if (i > 0) { - this.append(attachment, parent); - } else { - this.render(attachment); - } - }, this) - }); - }, - editAttachment: function () { - // vars - var val = this.val(); // bail early if no val - - if (!val) return; // popup - - var frame = acf.newMediaPopup({ - mode: 'edit', - title: acf.__('Edit Image'), - button: acf.__('Update Image'), - attachment: val, - field: this.get('key'), - select: $.proxy(function (attachment, i) { - this.render(attachment); - }, this) - }); - }, - removeAttachment: function () { - this.render(false); - }, - onClickAdd: function (e, $el) { - this.selectAttachment(); - }, - onClickEdit: function (e, $el) { - this.editAttachment(); - }, - onClickRemove: function (e, $el) { - this.removeAttachment(); - }, - onChange: function (e, $el) { - var $hiddenInput = this.$input(); - acf.getFileInputData($el, function (data) { - $hiddenInput.val($.param(data)); - }); - } - }); - acf.registerFieldType(Field); })(jQuery); - -(function ($, undefined) { - var Field = acf.models.ImageField.extend({ - type: 'file', - $control: function () { - return this.$('.acf-file-uploader'); - }, - $input: function () { - return this.$('input[type="hidden"]'); - }, - validateAttachment: function (attachment) { - // defaults - attachment = attachment || {}; // WP attachment - - if (attachment.id !== undefined) { - attachment = attachment.attributes; - } // args - - - attachment = acf.parseArgs(attachment, { - url: '', - alt: '', - title: '', - filename: '', - filesizeHumanReadable: '', - icon: '/wp-includes/images/media/default.png' - }); // return - - return attachment; - }, - render: function (attachment) { - // vars - attachment = this.validateAttachment(attachment); // update image - - this.$('img').attr({ - src: attachment.icon, - alt: attachment.alt, - title: attachment.title - }); // update elements - - this.$('[data-name="title"]').text(attachment.title); - this.$('[data-name="filename"]').text(attachment.filename).attr('href', attachment.url); - this.$('[data-name="filesize"]').text(attachment.filesizeHumanReadable); // vars - - var val = attachment.id || ''; // update val - - acf.val(this.$input(), val); // update class - - if (val) { - this.$control().addClass('has-value'); - } else { - this.$control().removeClass('has-value'); - } - }, - selectAttachment: function () { - // vars - var parent = this.parent(); - var multiple = parent && parent.get('type') === 'repeater'; // new frame - - var frame = acf.newMediaPopup({ - mode: 'select', - title: acf.__('Select File'), - field: this.get('key'), - multiple: multiple, - library: this.get('library'), - allowedTypes: this.get('mime_types'), - select: $.proxy(function (attachment, i) { - if (i > 0) { - this.append(attachment, parent); - } else { - this.render(attachment); - } - }, this) - }); - }, - editAttachment: function () { - // vars - var val = this.val(); // bail early if no val - - if (!val) { - return false; - } // popup - - - var frame = acf.newMediaPopup({ - mode: 'edit', - title: acf.__('Edit File'), - button: acf.__('Update File'), - attachment: val, - field: this.get('key'), - select: $.proxy(function (attachment, i) { - this.render(attachment); - }, this) - }); - } - }); - acf.registerFieldType(Field); +(function($, undefined){ + + var Field = acf.models.ImageField.extend({ + + type: 'file', + + $control: function(){ + return this.$('.acf-file-uploader'); + }, + + $input: function(){ + return this.$('input[type="hidden"]'); + }, + + validateAttachment: function( attachment ){ + + // defaults + attachment = attachment || {}; + + // WP attachment + if( attachment.id !== undefined ) { + attachment = attachment.attributes; + } + + // args + attachment = acf.parseArgs(attachment, { + url: '', + alt: '', + title: '', + filename: '', + filesizeHumanReadable: '', + icon: '/wp-includes/images/media/default.png' + }); + + // return + return attachment; + }, + + render: function( attachment ){ + + // vars + attachment = this.validateAttachment( attachment ); + + // update image + this.$('img').attr({ + src: attachment.icon, + alt: attachment.alt, + title: attachment.title + }); + + // update elements + this.$('[data-name="title"]').text( attachment.title ); + this.$('[data-name="filename"]').text( attachment.filename ).attr( 'href', attachment.url ); + this.$('[data-name="filesize"]').text( attachment.filesizeHumanReadable ); + + // vars + var val = attachment.id || ''; + + // update val + acf.val( this.$input(), val ); + + // update class + if( val ) { + this.$control().addClass('has-value'); + } else { + this.$control().removeClass('has-value'); + } + }, + + selectAttachment: function(){ + + // vars + var parent = this.parent(); + var multiple = (parent && parent.get('type') === 'repeater'); + + // new frame + var frame = acf.newMediaPopup({ + mode: 'select', + title: acf.__('Select File'), + field: this.get('key'), + multiple: multiple, + library: this.get('library'), + allowedTypes: this.get('mime_types'), + select: $.proxy(function( attachment, i ) { + if( i > 0 ) { + this.append( attachment, parent ); + } else { + this.render( attachment ); + } + }, this) + }); + }, + + editAttachment: function(){ + + // vars + var val = this.val(); + + // bail early if no val + if( !val ) { + return false; + } + + // popup + var frame = acf.newMediaPopup({ + mode: 'edit', + title: acf.__('Edit File'), + button: acf.__('Update File'), + attachment: val, + field: this.get('key'), + select: $.proxy(function( attachment, i ) { + this.render( attachment ); + }, this) + }); + } + }); + + acf.registerFieldType( Field ); + })(jQuery); +(function($, undefined){ + + var Field = acf.Field.extend({ + + type: 'link', + + events: { + 'click a[data-name="add"]': 'onClickEdit', + 'click a[data-name="edit"]': 'onClickEdit', + 'click a[data-name="remove"]': 'onClickRemove', + 'change .link-node': 'onChange', + }, + + $control: function(){ + return this.$('.acf-link'); + }, + + $node: function(){ + return this.$('.link-node'); + }, + + getValue: function(){ + + // vars + var $node = this.$node(); + + // return false if empty + if( !$node.attr('href') ) { + return false; + } + + // return + return { + title: $node.html(), + url: $node.attr('href'), + target: $node.attr('target') + }; + }, + + setValue: function( val ){ + + // default + val = acf.parseArgs(val, { + title: '', + url: '', + target: '' + }); + + // vars + var $div = this.$control(); + var $node = this.$node(); + + // remove class + $div.removeClass('-value -external'); + + // add class + if( val.url ) $div.addClass('-value'); + if( val.target === '_blank' ) $div.addClass('-external'); + + // update text + this.$('.link-title').html( val.title ); + this.$('.link-url').attr('href', val.url).html( val.url ); + + // update node + $node.html(val.title); + $node.attr('href', val.url); + $node.attr('target', val.target); + + // update inputs + this.$('.input-title').val( val.title ); + this.$('.input-target').val( val.target ); + this.$('.input-url').val( val.url ).trigger('change'); + }, + + onClickEdit: function( e, $el ){ + acf.wpLink.open( this.$node() ); + }, + + onClickRemove: function( e, $el ){ + this.setValue( false ); + }, + + onChange: function( e, $el ){ + + // get the changed value + var val = this.getValue(); + + // update inputs + this.setValue(val); + } + + }); + + acf.registerFieldType( Field ); + + + // manager + acf.wpLink = new acf.Model({ + + getNodeValue: function(){ + var $node = this.get('node'); + return { + title: acf.decode( $node.html() ), + url: $node.attr('href'), + target: $node.attr('target') + }; + }, + + setNodeValue: function( val ){ + var $node = this.get('node'); + $node.text( val.title ); + $node.attr('href', val.url); + $node.attr('target', val.target); + $node.trigger('change'); + }, + + getInputValue: function(){ + return { + title: $('#wp-link-text').val(), + url: $('#wp-link-url').val(), + target: $('#wp-link-target').prop('checked') ? '_blank' : '' + }; + }, + + setInputValue: function( val ){ + $('#wp-link-text').val( val.title ); + $('#wp-link-url').val( val.url ); + $('#wp-link-target').prop('checked', val.target === '_blank' ); + }, + + open: function( $node ){ -(function ($, undefined) { - var Field = acf.Field.extend({ - type: 'link', - events: { - 'click a[data-name="add"]': 'onClickEdit', - 'click a[data-name="edit"]': 'onClickEdit', - 'click a[data-name="remove"]': 'onClickRemove', - 'change .link-node': 'onChange' - }, - $control: function () { - return this.$('.acf-link'); - }, - $node: function () { - return this.$('.link-node'); - }, - getValue: function () { - // vars - var $node = this.$node(); // return false if empty + // add events + this.on('wplink-open', 'onOpen'); + this.on('wplink-close', 'onClose'); + + // set node + this.set('node', $node); + + // create textarea + var $textarea = $(''); + $('body').append( $textarea ); + + // vars + var val = this.getNodeValue(); + + // open popup + wpLink.open( 'acf-link-textarea', val.url, val.title, null ); + }, + + onOpen: function(){ - if (!$node.attr('href')) { - return false; - } // return + // always show title (WP will hide title if empty) + $('#wp-link-wrap').addClass('has-text-field'); + + // set inputs + var val = this.getNodeValue(); + this.setInputValue( val ); + // Update button text. + if( val.url && wpLinkL10n ) { + $('#wp-link-submit').val( wpLinkL10n.update ); + } + }, + + close: function(){ + wpLink.close(); + }, + + onClose: function(){ + + // Bail early if no node. + // Needed due to WP triggering this event twice. + if( !this.has('node') ) { + return false; + } - return { - title: $node.html(), - url: $node.attr('href'), - target: $node.attr('target') - }; - }, - setValue: function (val) { - // default - val = acf.parseArgs(val, { - title: '', - url: '', - target: '' - }); // vars + // Determine context. + var $submit = $('#wp-link-submit'); + var isSubmit = ( $submit.is(':hover') || $submit.is(':focus') ); + + // Set value + if( isSubmit ) { + var val = this.getInputValue(); + this.setNodeValue( val ); + } + + // Cleanup. + this.off('wplink-open'); + this.off('wplink-close'); + $('#acf-link-textarea').remove(); + this.set('node', null); + } + }); - var $div = this.$control(); - var $node = this.$node(); // remove class - - $div.removeClass('-value -external'); // add class - - if (val.url) $div.addClass('-value'); - if (val.target === '_blank') $div.addClass('-external'); // update text - - this.$('.link-title').html(val.title); - this.$('.link-url').attr('href', val.url).html(val.url); // update node - - $node.html(val.title); - $node.attr('href', val.url); - $node.attr('target', val.target); // update inputs - - this.$('.input-title').val(val.title); - this.$('.input-target').val(val.target); - this.$('.input-url').val(val.url).trigger('change'); - }, - onClickEdit: function (e, $el) { - acf.wpLink.open(this.$node()); - }, - onClickRemove: function (e, $el) { - this.setValue(false); - }, - onChange: function (e, $el) { - // get the changed value - var val = this.getValue(); // update inputs - - this.setValue(val); - } - }); - acf.registerFieldType(Field); // manager - - acf.wpLink = new acf.Model({ - getNodeValue: function () { - var $node = this.get('node'); - return { - title: acf.decode($node.html()), - url: $node.attr('href'), - target: $node.attr('target') - }; - }, - setNodeValue: function (val) { - var $node = this.get('node'); - $node.text(val.title); - $node.attr('href', val.url); - $node.attr('target', val.target); - $node.trigger('change'); - }, - getInputValue: function () { - return { - title: $('#wp-link-text').val(), - url: $('#wp-link-url').val(), - target: $('#wp-link-target').prop('checked') ? '_blank' : '' - }; - }, - setInputValue: function (val) { - $('#wp-link-text').val(val.title); - $('#wp-link-url').val(val.url); - $('#wp-link-target').prop('checked', val.target === '_blank'); - }, - open: function ($node) { - // add events - this.on('wplink-open', 'onOpen'); - this.on('wplink-close', 'onClose'); // set node - - this.set('node', $node); // create textarea - - var $textarea = $(''); - $('body').append($textarea); // vars - - var val = this.getNodeValue(); // open popup - - wpLink.open('acf-link-textarea', val.url, val.title, null); - }, - onOpen: function () { - // always show title (WP will hide title if empty) - $('#wp-link-wrap').addClass('has-text-field'); // set inputs - - var val = this.getNodeValue(); - this.setInputValue(val); // Update button text. - - if (val.url && wpLinkL10n) { - $('#wp-link-submit').val(wpLinkL10n.update); - } - }, - close: function () { - wpLink.close(); - }, - onClose: function () { - // Bail early if no node. - // Needed due to WP triggering this event twice. - if (!this.has('node')) { - return false; - } // Determine context. - - - var $submit = $('#wp-link-submit'); - var isSubmit = $submit.is(':hover') || $submit.is(':focus'); // Set value - - if (isSubmit) { - var val = this.getInputValue(); - this.setNodeValue(val); - } // Cleanup. - - - this.off('wplink-open'); - this.off('wplink-close'); - $('#acf-link-textarea').remove(); - this.set('node', null); - } - }); })(jQuery); +(function($, undefined){ + + var Field = acf.Field.extend({ + + type: 'oembed', + + events: { + 'click [data-name="clear-button"]': 'onClickClear', + 'keypress .input-search': 'onKeypressSearch', + 'keyup .input-search': 'onKeyupSearch', + 'change .input-search': 'onChangeSearch' + }, + + $control: function(){ + return this.$('.acf-oembed'); + }, + + $input: function(){ + return this.$('.input-value'); + }, + + $search: function(){ + return this.$('.input-search'); + }, + + getValue: function(){ + return this.$input().val(); + }, + + getSearchVal: function(){ + return this.$search().val(); + }, + + setValue: function( val ){ + + // class + if( val ) { + this.$control().addClass('has-value'); + } else { + this.$control().removeClass('has-value'); + } + + acf.val( this.$input(), val ); + }, + + showLoading: function( show ){ + acf.showLoading( this.$('.canvas') ); + }, + + hideLoading: function(){ + acf.hideLoading( this.$('.canvas') ); + }, + + maybeSearch: function(){ + + // vars + var prevUrl = this.val(); + var url = this.getSearchVal(); + + // no value + if( !url ) { + return this.clear(); + } + + // fix missing 'http://' - causes the oembed code to error and fail + if( url.substr(0, 4) != 'http' ) { + url = 'http://' + url; + } + + // bail early if no change + if( url === prevUrl ) return; + + // clear existing timeout + var timeout = this.get('timeout'); + if( timeout ) { + clearTimeout( timeout ); + } + + // set new timeout + var callback = $.proxy(this.search, this, url); + this.set('timeout', setTimeout(callback, 300)); + + }, + + search: function( url ){ + + // ajax + var ajaxData = { + action: 'acf/fields/oembed/search', + s: url, + field_key: this.get('key') + }; + + // clear existing timeout + var xhr = this.get('xhr'); + if( xhr ) { + xhr.abort(); + } + + // loading + this.showLoading(); + + // query + var xhr = $.ajax({ + url: acf.get('ajaxurl'), + data: acf.prepareForAjax(ajaxData), + type: 'post', + dataType: 'json', + context: this, + success: function( json ){ + + // error + if( !json || !json.html ) { + json = { + url: false, + html: '' + } + } + + // update vars + this.val( json.url ); + this.$('.canvas-media').html( json.html ); + }, + complete: function(){ + this.hideLoading(); + } + }); + + this.set('xhr', xhr); + }, + + clear: function(){ + this.val(''); + this.$search().val(''); + this.$('.canvas-media').html(''); + }, + + onClickClear: function( e, $el ){ + this.clear(); + }, + + onKeypressSearch: function( e, $el ){ + if( e.which == 13 ) { + e.preventDefault(); + this.maybeSearch(); + } + }, + + onKeyupSearch: function( e, $el ){ + if( $el.val() ) { + this.maybeSearch(); + } + }, + + onChangeSearch: function( e, $el ){ + this.maybeSearch(); + } + + }); + + acf.registerFieldType( Field ); -(function ($, undefined) { - var Field = acf.Field.extend({ - type: 'oembed', - events: { - 'click [data-name="clear-button"]': 'onClickClear', - 'keypress .input-search': 'onKeypressSearch', - 'keyup .input-search': 'onKeyupSearch', - 'change .input-search': 'onChangeSearch' - }, - $control: function () { - return this.$('.acf-oembed'); - }, - $input: function () { - return this.$('.input-value'); - }, - $search: function () { - return this.$('.input-search'); - }, - getValue: function () { - return this.$input().val(); - }, - getSearchVal: function () { - return this.$search().val(); - }, - setValue: function (val) { - // class - if (val) { - this.$control().addClass('has-value'); - } else { - this.$control().removeClass('has-value'); - } - - acf.val(this.$input(), val); - }, - showLoading: function (show) { - acf.showLoading(this.$('.canvas')); - }, - hideLoading: function () { - acf.hideLoading(this.$('.canvas')); - }, - maybeSearch: function () { - // vars - var prevUrl = this.val(); - var url = this.getSearchVal(); // no value - - if (!url) { - return this.clear(); - } // fix missing 'http://' - causes the oembed code to error and fail - - - if (url.substr(0, 4) != 'http') { - url = 'http://' + url; - } // bail early if no change - - - if (url === prevUrl) return; // clear existing timeout - - var timeout = this.get('timeout'); - - if (timeout) { - clearTimeout(timeout); - } // set new timeout - - - var callback = $.proxy(this.search, this, url); - this.set('timeout', setTimeout(callback, 300)); - }, - search: function (url) { - // ajax - var ajaxData = { - action: 'acf/fields/oembed/search', - s: url, - field_key: this.get('key') - }; // clear existing timeout - - var xhr = this.get('xhr'); - - if (xhr) { - xhr.abort(); - } // loading - - - this.showLoading(); // query - - var xhr = $.ajax({ - url: acf.get('ajaxurl'), - data: acf.prepareForAjax(ajaxData), - type: 'post', - dataType: 'json', - context: this, - success: function (json) { - // error - if (!json || !json.html) { - json = { - url: false, - html: '' - }; - } // update vars - - - this.val(json.url); - this.$('.canvas-media').html(json.html); - }, - complete: function () { - this.hideLoading(); - } - }); - this.set('xhr', xhr); - }, - clear: function () { - this.val(''); - this.$search().val(''); - this.$('.canvas-media').html(''); - }, - onClickClear: function (e, $el) { - this.clear(); - }, - onKeypressSearch: function (e, $el) { - if (e.which == 13) { - e.preventDefault(); - this.maybeSearch(); - } - }, - onKeyupSearch: function (e, $el) { - if ($el.val()) { - this.maybeSearch(); - } - }, - onChangeSearch: function (e, $el) { - this.maybeSearch(); - } - }); - acf.registerFieldType(Field); })(jQuery); +(function($, undefined){ + + var Field = acf.Field.extend({ + + type: 'radio', + + events: { + 'click input[type="radio"]': 'onClick', + }, + + $control: function(){ + return this.$('.acf-radio-list'); + }, + + $input: function(){ + return this.$('input:checked'); + }, + + $inputText: function(){ + return this.$('input[type="text"]'); + }, + + getValue: function(){ + var val = this.$input().val(); + if( val === 'other' && this.get('other_choice') ) { + val = this.$inputText().val(); + } + return val; + }, + + onClick: function( e, $el ){ + + // vars + var $label = $el.parent('label'); + var selected = $label.hasClass('selected'); + var val = $el.val(); + + // remove previous selected + this.$('.selected').removeClass('selected'); + + // add active class + $label.addClass('selected'); + + // allow null + if( this.get('allow_null') && selected ) { + $label.removeClass('selected'); + $el.prop('checked', false).trigger('change'); + val = false; + } + + // other + if( this.get('other_choice') ) { + + // enable + if( val === 'other' ) { + this.$inputText().prop('disabled', false); + + // disable + } else { + this.$inputText().prop('disabled', true); + } + } + } + }); + + acf.registerFieldType( Field ); -(function ($, undefined) { - var Field = acf.Field.extend({ - type: 'radio', - events: { - 'click input[type="radio"]': 'onClick' - }, - $control: function () { - return this.$('.acf-radio-list'); - }, - $input: function () { - return this.$('input:checked'); - }, - $inputText: function () { - return this.$('input[type="text"]'); - }, - getValue: function () { - var val = this.$input().val(); - - if (val === 'other' && this.get('other_choice')) { - val = this.$inputText().val(); - } - - return val; - }, - onClick: function (e, $el) { - // vars - var $label = $el.parent('label'); - var selected = $label.hasClass('selected'); - var val = $el.val(); // remove previous selected - - this.$('.selected').removeClass('selected'); // add active class - - $label.addClass('selected'); // allow null - - if (this.get('allow_null') && selected) { - $label.removeClass('selected'); - $el.prop('checked', false).trigger('change'); - val = false; - } // other - - - if (this.get('other_choice')) { - // enable - if (val === 'other') { - this.$inputText().prop('disabled', false); // disable - } else { - this.$inputText().prop('disabled', true); - } - } - } - }); - acf.registerFieldType(Field); })(jQuery); - -(function ($, undefined) { - var Field = acf.Field.extend({ - type: 'range', - events: { - 'input input[type="range"]': 'onChange', - 'change input': 'onChange' - }, - $input: function () { - return this.$('input[type="range"]'); - }, - $inputAlt: function () { - return this.$('input[type="number"]'); - }, - setValue: function (val) { - this.busy = true; // Update range input (with change). - - acf.val(this.$input(), val); // Update alt input (without change). - // Read in input value to inherit min/max validation. - - acf.val(this.$inputAlt(), this.$input().val(), true); - this.busy = false; - }, - onChange: function (e, $el) { - if (!this.busy) { - this.setValue($el.val()); - } - } - }); - acf.registerFieldType(Field); +(function($, undefined){ + + var Field = acf.Field.extend({ + + type: 'range', + + events: { + 'input input[type="range"]': 'onChange', + 'change input': 'onChange' + }, + + $input: function(){ + return this.$('input[type="range"]'); + }, + + $inputAlt: function(){ + return this.$('input[type="number"]'); + }, + + setValue: function( val ){ + this.busy = true; + + // Update range input (with change). + acf.val( this.$input(), val ); + + // Update alt input (without change). + // Read in input value to inherit min/max validation. + acf.val( this.$inputAlt(), this.$input().val(), true ); + + this.busy = false; + }, + + onChange: function( e, $el ){ + if( !this.busy ) { + this.setValue( $el.val() ); + } + } + }); + + acf.registerFieldType( Field ); + })(jQuery); - -(function ($, undefined) { - var Field = acf.Field.extend({ - type: 'relationship', - events: { - 'keypress [data-filter]': 'onKeypressFilter', - 'change [data-filter]': 'onChangeFilter', - 'keyup [data-filter]': 'onChangeFilter', - 'click .choices-list .acf-rel-item': 'onClickAdd', - 'click [data-name="remove_item"]': 'onClickRemove' - }, - $control: function () { - return this.$('.acf-relationship'); - }, - $list: function (list) { - return this.$('.' + list + '-list'); - }, - $listItems: function (list) { - return this.$list(list).find('.acf-rel-item'); - }, - $listItem: function (list, id) { - return this.$list(list).find('.acf-rel-item[data-id="' + id + '"]'); - }, - getValue: function () { - var val = []; - this.$listItems('values').each(function () { - val.push($(this).data('id')); - }); - return val.length ? val : false; - }, - newChoice: function (props) { - return ['
  • ', '' + props.text + '', '
  • '].join(''); - }, - newValue: function (props) { - return ['
  • ', '', '' + props.text, '', '', '
  • '].join(''); - }, - initialize: function () { - // Delay initialization until "interacted with" or "in view". - var delayed = this.proxy(acf.once(function () { - // Add sortable. - this.$list('values').sortable({ - items: 'li', - forceHelperSize: true, - forcePlaceholderSize: true, - scroll: true, - update: this.proxy(function () { - this.$input().trigger('change'); - }) - }); // Avoid browser remembering old scroll position and add event. - - this.$list('choices').scrollTop(0).on('scroll', this.proxy(this.onScrollChoices)); // Fetch choices. - - this.fetch(); - })); // Bind "interacted with". - - this.$el.one('mouseover', delayed); - this.$el.one('focus', 'input', delayed); // Bind "in view". - - acf.onceInView(this.$el, delayed); - }, - onScrollChoices: function (e) { - // bail early if no more results - if (this.get('loading') || !this.get('more')) { - return; - } // Scrolled to bottom - - - var $list = this.$list('choices'); - var scrollTop = Math.ceil($list.scrollTop()); - var scrollHeight = Math.ceil($list[0].scrollHeight); - var innerHeight = Math.ceil($list.innerHeight()); - var paged = this.get('paged') || 1; - - if (scrollTop + innerHeight >= scrollHeight) { - // update paged - this.set('paged', paged + 1); // fetch - - this.fetch(); - } - }, - onKeypressFilter: function (e, $el) { - // don't submit form - if (e.which == 13) { - e.preventDefault(); - } - }, - onChangeFilter: function (e, $el) { - // vars - var val = $el.val(); - var filter = $el.data('filter'); // Bail early if filter has not changed - - if (this.get(filter) === val) { - return; - } // update attr - - - this.set(filter, val); // reset paged - - this.set('paged', 1); // fetch - - if ($el.is('select')) { - this.fetch(); // search must go through timeout - } else { - this.maybeFetch(); - } - }, - onClickAdd: function (e, $el) { - // vars - var val = this.val(); - var max = parseInt(this.get('max')); // can be added? - - if ($el.hasClass('disabled')) { - return false; - } // validate - - - if (max > 0 && val && val.length >= max) { - // add notice - this.showNotice({ - text: acf.__('Maximum values reached ( {max} values )').replace('{max}', max), - type: 'warning' - }); - return false; - } // disable - - - $el.addClass('disabled'); // add - - var html = this.newValue({ - id: $el.data('id'), - text: $el.html() - }); - this.$list('values').append(html); // trigger change - - this.$input().trigger('change'); - }, - onClickRemove: function (e, $el) { - // Prevent default here because generic handler wont be triggered. - e.preventDefault(); // vars - - var $span = $el.parent(); - var $li = $span.parent(); - var id = $span.data('id'); // remove value - - $li.remove(); // show choice - - this.$listItem('choices', id).removeClass('disabled'); // trigger change - - this.$input().trigger('change'); - }, - maybeFetch: function () { - // vars - var timeout = this.get('timeout'); // abort timeout - - if (timeout) { - clearTimeout(timeout); - } // fetch - - - timeout = this.setTimeout(this.fetch, 300); - this.set('timeout', timeout); - }, - getAjaxData: function () { - // load data based on element attributes - var ajaxData = this.$control().data(); - - for (var name in ajaxData) { - ajaxData[name] = this.get(name); - } // extra - - - ajaxData.action = 'acf/fields/relationship/query'; - ajaxData.field_key = this.get('key'); // Filter. - - ajaxData = acf.applyFilters('relationship_ajax_data', ajaxData, this); // return - - return ajaxData; - }, - fetch: function () { - // abort XHR if this field is already loading AJAX data - var xhr = this.get('xhr'); - - if (xhr) { - xhr.abort(); - } // add to this.o - - - var ajaxData = this.getAjaxData(); // clear html if is new query - - var $choiceslist = this.$list('choices'); - - if (ajaxData.paged == 1) { - $choiceslist.html(''); - } // loading - - - var $loading = $('
  • ' + acf.__('Loading') + '
  • '); - $choiceslist.append($loading); - this.set('loading', true); // callback - - var onComplete = function () { - this.set('loading', false); - $loading.remove(); - }; - - var onSuccess = function (json) { - // no results - if (!json || !json.results || !json.results.length) { - // prevent pagination - this.set('more', false); // add message - - if (this.get('paged') == 1) { - this.$list('choices').append('
  • ' + acf.__('No matches found') + '
  • '); - } // return - - - return; - } // set more (allows pagination scroll) - - - this.set('more', json.more); // get new results - - var html = this.walkChoices(json.results); - var $html = $(html); // apply .disabled to left li's - - var val = this.val(); - - if (val && val.length) { - val.map(function (id) { - $html.find('.acf-rel-item[data-id="' + id + '"]').addClass('disabled'); - }); - } // append - - - $choiceslist.append($html); // merge together groups - - var $prevLabel = false; - var $prevList = false; - $choiceslist.find('.acf-rel-label').each(function () { - var $label = $(this); - var $list = $label.siblings('ul'); - - if ($prevLabel && $prevLabel.text() == $label.text()) { - $prevList.append($list.children()); - $(this).parent().remove(); - return; - } // update vars - - - $prevLabel = $label; - $prevList = $list; - }); - }; // get results - - - var xhr = $.ajax({ - url: acf.get('ajaxurl'), - dataType: 'json', - type: 'post', - data: acf.prepareForAjax(ajaxData), - context: this, - success: onSuccess, - complete: onComplete - }); // set - - this.set('xhr', xhr); - }, - walkChoices: function (data) { - // walker - var walk = function (data) { - // vars - var html = ''; // is array - - if ($.isArray(data)) { - data.map(function (item) { - html += walk(item); - }); // is item - } else if ($.isPlainObject(data)) { - // group - if (data.children !== undefined) { - html += '
  • ' + acf.escHtml(data.text) + '
      '; - html += walk(data.children); - html += '
  • '; // single - } else { - html += '
  • ' + acf.escHtml(data.text) + '
  • '; - } - } // return - - - return html; - }; - - return walk(data); - } - }); - acf.registerFieldType(Field); +(function($, undefined){ + + var Field = acf.Field.extend({ + + type: 'relationship', + + events: { + 'keypress [data-filter]': 'onKeypressFilter', + 'change [data-filter]': 'onChangeFilter', + 'keyup [data-filter]': 'onChangeFilter', + 'click .choices-list .acf-rel-item': 'onClickAdd', + 'click [data-name="remove_item"]': 'onClickRemove', + }, + + $control: function(){ + return this.$('.acf-relationship'); + }, + + $list: function( list ) { + return this.$('.' + list + '-list'); + }, + + $listItems: function( list ) { + return this.$list( list ).find('.acf-rel-item'); + }, + + $listItem: function( list, id ) { + return this.$list( list ).find('.acf-rel-item[data-id="' + id + '"]'); + }, + + getValue: function(){ + var val = []; + this.$listItems('values').each(function(){ + val.push( $(this).data('id') ); + }); + return val.length ? val : false; + }, + + newChoice: function( props ){ + return [ + '
  • ', + '' + props.text + '', + '
  • ' + ].join(''); + }, + + newValue: function( props ){ + return [ + '
  • ', + '', + '' + props.text, + '', + '', + '
  • ' + ].join(''); + }, + + initialize: function(){ + + // Delay initialization until "interacted with" or "in view". + var delayed = this.proxy(acf.once(function(){ + + // Add sortable. + this.$list('values').sortable({ + items: 'li', + forceHelperSize: true, + forcePlaceholderSize: true, + scroll: true, + update: this.proxy(function(){ + this.$input().trigger('change'); + }) + }); + + // Avoid browser remembering old scroll position and add event. + this.$list('choices').scrollTop(0).on('scroll', this.proxy(this.onScrollChoices)); + + // Fetch choices. + this.fetch(); + + })); + + // Bind "interacted with". + this.$el.one( 'mouseover', delayed ); + this.$el.one( 'focus', 'input', delayed ); + + // Bind "in view". + acf.onceInView( this.$el, delayed ); + }, + + onScrollChoices: function(e){ + + // bail early if no more results + if( this.get('loading') || !this.get('more') ) { + return; + } + + // Scrolled to bottom + var $list = this.$list('choices'); + var scrollTop = Math.ceil( $list.scrollTop() ); + var scrollHeight = Math.ceil( $list[0].scrollHeight ); + var innerHeight = Math.ceil( $list.innerHeight() ); + var paged = this.get('paged') || 1; + if( (scrollTop + innerHeight) >= scrollHeight ) { + + // update paged + this.set('paged', (paged+1)); + + // fetch + this.fetch(); + } + }, + + onKeypressFilter: function( e, $el ){ + + // don't submit form + if( e.which == 13 ) { + e.preventDefault(); + } + }, + + onChangeFilter: function( e, $el ){ + + // vars + var val = $el.val(); + var filter = $el.data('filter'); + + // Bail early if filter has not changed + if( this.get(filter) === val ) { + return; + } + + // update attr + this.set(filter, val); + + // reset paged + this.set('paged', 1); + + // fetch + if( $el.is('select') ) { + this.fetch(); + + // search must go through timeout + } else { + this.maybeFetch(); + } + }, + + onClickAdd: function( e, $el ){ + + // vars + var val = this.val(); + var max = parseInt( this.get('max') ); + + // can be added? + if( $el.hasClass('disabled') ) { + return false; + } + + // validate + if( max > 0 && val && val.length >= max ) { + + // add notice + this.showNotice({ + text: acf.__('Maximum values reached ( {max} values )').replace('{max}', max), + type: 'warning' + }); + return false; + } + + // disable + $el.addClass('disabled'); + + // add + var html = this.newValue({ + id: $el.data('id'), + text: $el.html() + }); + this.$list('values').append( html ) + + // trigger change + this.$input().trigger('change'); + }, + + onClickRemove: function( e, $el ){ + + // Prevent default here because generic handler wont be triggered. + e.preventDefault(); + + // vars + var $span = $el.parent(); + var $li = $span.parent(); + var id = $span.data('id'); + + // remove value + $li.remove(); + + // show choice + this.$listItem('choices', id).removeClass('disabled'); + + // trigger change + this.$input().trigger('change'); + }, + + maybeFetch: function(){ + + // vars + var timeout = this.get('timeout'); + + // abort timeout + if( timeout ) { + clearTimeout( timeout ); + } + + // fetch + timeout = this.setTimeout(this.fetch, 300); + this.set('timeout', timeout); + }, + + getAjaxData: function(){ + + // load data based on element attributes + var ajaxData = this.$control().data(); + for( var name in ajaxData ) { + ajaxData[ name ] = this.get( name ); + } + + // extra + ajaxData.action = 'acf/fields/relationship/query'; + ajaxData.field_key = this.get('key'); + + // Filter. + ajaxData = acf.applyFilters( 'relationship_ajax_data', ajaxData, this ); + + // return + return ajaxData; + }, + + fetch: function(){ + + // abort XHR if this field is already loading AJAX data + var xhr = this.get('xhr'); + if( xhr ) { + xhr.abort(); + } + + // add to this.o + var ajaxData = this.getAjaxData(); + + // clear html if is new query + var $choiceslist = this.$list( 'choices' ); + if( ajaxData.paged == 1 ) { + $choiceslist.html(''); + } + + // loading + var $loading = $('
  • ' + acf.__('Loading') + '
  • '); + $choiceslist.append($loading); + this.set('loading', true); + + // callback + var onComplete = function(){ + this.set('loading', false); + $loading.remove(); + }; + + var onSuccess = function( json ){ + + // no results + if( !json || !json.results || !json.results.length ) { + + // prevent pagination + this.set('more', false); + + // add message + if( this.get('paged') == 1 ) { + this.$list('choices').append('
  • ' + acf.__('No matches found') + '
  • '); + } + + // return + return; + } + + // set more (allows pagination scroll) + this.set('more', json.more ); + + // get new results + var html = this.walkChoices(json.results); + var $html = $( html ); + + // apply .disabled to left li's + var val = this.val(); + if( val && val.length ) { + val.map(function( id ){ + $html.find('.acf-rel-item[data-id="' + id + '"]').addClass('disabled'); + }); + } + + // append + $choiceslist.append( $html ); + + // merge together groups + var $prevLabel = false; + var $prevList = false; + + $choiceslist.find('.acf-rel-label').each(function(){ + + var $label = $(this); + var $list = $label.siblings('ul'); + + if( $prevLabel && $prevLabel.text() == $label.text() ) { + $prevList.append( $list.children() ); + $(this).parent().remove(); + return; + } + + // update vars + $prevLabel = $label; + $prevList = $list; + }); + }; + + // get results + var xhr = $.ajax({ + url: acf.get('ajaxurl'), + dataType: 'json', + type: 'post', + data: acf.prepareForAjax(ajaxData), + context: this, + success: onSuccess, + complete: onComplete + }); + + // set + this.set('xhr', xhr); + }, + + walkChoices: function( data ){ + + // walker + var walk = function( data ){ + + // vars + var html = ''; + + // is array + if( $.isArray(data) ) { + data.map(function(item){ + html += walk( item ); + }); + + // is item + } else if( $.isPlainObject(data) ) { + + // group + if( data.children !== undefined ) { + + html += '
  • ' + acf.escHtml( data.text ) + '
      '; + html += walk( data.children ); + html += '
  • '; + + // single + } else { + html += '
  • ' + acf.escHtml( data.text ) + '
  • '; + } + } + + // return + return html; + }; + + return walk( data ); + } + + }); + + acf.registerFieldType( Field ); + })(jQuery); - -(function ($, undefined) { - var Field = acf.Field.extend({ - type: 'select', - select2: false, - wait: 'load', - events: { - 'removeField': 'onRemove', - 'duplicateField': 'onDuplicate' - }, - $input: function () { - return this.$('select'); - }, - initialize: function () { - // vars - var $select = this.$input(); // inherit data - - this.inherit($select); // select2 - - if (this.get('ui')) { - // populate ajax_data (allowing custom attribute to already exist) - var ajaxAction = this.get('ajax_action'); - - if (!ajaxAction) { - ajaxAction = 'acf/fields/' + this.get('type') + '/query'; - } // select2 - - - this.select2 = acf.newSelect2($select, { - field: this, - ajax: this.get('ajax'), - multiple: this.get('multiple'), - placeholder: this.get('placeholder'), - allowNull: this.get('allow_null'), - ajaxAction: ajaxAction - }); - } - }, - onRemove: function () { - if (this.select2) { - this.select2.destroy(); - } - }, - onDuplicate: function (e, $el, $duplicate) { - if (this.select2) { - $duplicate.find('.select2-container').remove(); - $duplicate.find('select').removeClass('select2-hidden-accessible'); - } - } - }); - acf.registerFieldType(Field); +(function($, undefined){ + + var Field = acf.Field.extend({ + + type: 'select', + + select2: false, + + wait: 'load', + + events: { + 'removeField': 'onRemove', + 'duplicateField': 'onDuplicate' + }, + + $input: function(){ + return this.$('select'); + }, + + initialize: function(){ + + // vars + var $select = this.$input(); + + // inherit data + this.inherit( $select ); + + // select2 + if( this.get('ui') ) { + + // populate ajax_data (allowing custom attribute to already exist) + var ajaxAction = this.get('ajax_action'); + if( !ajaxAction ) { + ajaxAction = 'acf/fields/' + this.get('type') + '/query'; + } + + // select2 + this.select2 = acf.newSelect2($select, { + field: this, + ajax: this.get('ajax'), + multiple: this.get('multiple'), + placeholder: this.get('placeholder'), + allowNull: this.get('allow_null'), + ajaxAction: ajaxAction, + }); + } + }, + + onRemove: function(){ + if( this.select2 ) { + this.select2.destroy(); + } + }, + + onDuplicate: function( e, $el, $duplicate ){ + if( this.select2 ) { + $duplicate.find('.select2-container').remove(); + $duplicate.find('select').removeClass('select2-hidden-accessible'); + } + } + }); + + acf.registerFieldType( Field ); + })(jQuery); +(function($, undefined){ + + // vars + var CONTEXT = 'tab'; + + var Field = acf.Field.extend({ + + type: 'tab', + + wait: '', + + tabs: false, + + tab: false, + + events: { + 'duplicateField': 'onDuplicate' + }, -(function ($, undefined) { - // vars - var CONTEXT = 'tab'; - var Field = acf.Field.extend({ - type: 'tab', - wait: '', - tabs: false, - tab: false, - events: { - 'duplicateField': 'onDuplicate' - }, - findFields: function () { - return this.$el.nextUntil('.acf-field-tab', '.acf-field'); - }, - getFields: function () { - return acf.getFields(this.findFields()); - }, - findTabs: function () { - return this.$el.prevAll('.acf-tab-wrap:first'); - }, - findTab: function () { - return this.$('.acf-tab-button'); - }, - initialize: function () { - // bail early if is td - if (this.$el.is('td')) { - this.events = {}; - return false; - } // vars + findFields: function(){ + return this.$el.nextUntil('.acf-field-tab', '.acf-field'); + }, + + getFields: function(){ + return acf.getFields( this.findFields() ); + }, + + findTabs: function(){ + return this.$el.prevAll('.acf-tab-wrap:first'); + }, + + findTab: function(){ + return this.$('.acf-tab-button'); + }, + + initialize: function(){ + + // bail early if is td + if( this.$el.is('td') ) { + this.events = {}; + return false; + } + + // vars + var $tabs = this.findTabs(); + var $tab = this.findTab(); + var settings = acf.parseArgs($tab.data(), { + endpoint: false, + placement: '', + before: this.$el + }); + + // create wrap + if( !$tabs.length || settings.endpoint ) { + this.tabs = new Tabs( settings ); + } else { + this.tabs = $tabs.data('acf'); + } + + // add tab + this.tab = this.tabs.addTab($tab, this); + }, + + isActive: function(){ + return this.tab.isActive(); + }, + + showFields: function(){ + + // show fields + this.getFields().map(function( field ){ + field.show( this.cid, CONTEXT ); + field.hiddenByTab = false; + }, this); + + }, + + hideFields: function(){ + + // hide fields + this.getFields().map(function( field ){ + field.hide( this.cid, CONTEXT ); + field.hiddenByTab = this.tab; + }, this); + + }, + + show: function( lockKey ){ + // show field and store result + var visible = acf.Field.prototype.show.apply(this, arguments); + + // check if now visible + if( visible ) { + + // show tab + this.tab.show(); + + // check active tabs + this.tabs.refresh(); + } + + // return + return visible; + }, + + hide: function( lockKey ){ - var $tabs = this.findTabs(); - var $tab = this.findTab(); - var settings = acf.parseArgs($tab.data(), { - endpoint: false, - placement: '', - before: this.$el - }); // create wrap + // hide field and store result + var hidden = acf.Field.prototype.hide.apply(this, arguments); + + // check if now hidden + if( hidden ) { + + // hide tab + this.tab.hide(); + + // reset tabs if this was active + if( this.isActive() ) { + this.tabs.reset(); + } + } + + // return + return hidden; + }, + + enable: function( lockKey ){ - if (!$tabs.length || settings.endpoint) { - this.tabs = new Tabs(settings); - } else { - this.tabs = $tabs.data('acf'); - } // add tab + // enable fields + this.getFields().map(function( field ){ + field.enable( CONTEXT ); + }); + }, + + disable: function( lockKey ){ + + // disable fields + this.getFields().map(function( field ){ + field.disable( CONTEXT ); + }); + }, - - this.tab = this.tabs.addTab($tab, this); - }, - isActive: function () { - return this.tab.isActive(); - }, - showFields: function () { - // show fields - this.getFields().map(function (field) { - field.show(this.cid, CONTEXT); - field.hiddenByTab = false; - }, this); - }, - hideFields: function () { - // hide fields - this.getFields().map(function (field) { - field.hide(this.cid, CONTEXT); - field.hiddenByTab = this.tab; - }, this); - }, - show: function (lockKey) { - // show field and store result - var visible = acf.Field.prototype.show.apply(this, arguments); // check if now visible - - if (visible) { - // show tab - this.tab.show(); // check active tabs - - this.tabs.refresh(); - } // return - - - return visible; - }, - hide: function (lockKey) { - // hide field and store result - var hidden = acf.Field.prototype.hide.apply(this, arguments); // check if now hidden - - if (hidden) { - // hide tab - this.tab.hide(); // reset tabs if this was active - - if (this.isActive()) { - this.tabs.reset(); - } - } // return - - - return hidden; - }, - enable: function (lockKey) { - // enable fields - this.getFields().map(function (field) { - field.enable(CONTEXT); - }); - }, - disable: function (lockKey) { - // disable fields - this.getFields().map(function (field) { - field.disable(CONTEXT); - }); - }, - onDuplicate: function (e, $el, $duplicate) { - if (this.isActive()) { - $duplicate.prevAll('.acf-tab-wrap:first').remove(); - } - } - }); - acf.registerFieldType(Field); - /** - * tabs - * - * description - * - * @date 8/2/18 - * @since 5.6.5 - * - * @param type $var Description. Default. - * @return type Description. - */ - - var i = 0; - var Tabs = acf.Model.extend({ - tabs: [], - active: false, - actions: { - 'refresh': 'onRefresh' - }, - data: { - before: false, - placement: 'top', - index: 0, - initialized: false - }, - setup: function (settings) { - // data - $.extend(this.data, settings); // define this prop to avoid scope issues - - this.tabs = []; - this.active = false; // vars - - var placement = this.get('placement'); - var $before = this.get('before'); - var $parent = $before.parent(); // add sidebar for left placement - - if (placement == 'left' && $parent.hasClass('acf-fields')) { - $parent.addClass('-sidebar'); - } // create wrap - - - if ($before.is('tr')) { - this.$el = $('
    '); - } else { - this.$el = $('
      '); - } // append - - - $before.before(this.$el); // set index - - this.set('index', i, true); - i++; - }, - initializeTabs: function () { - // find first visible tab - var tab = this.getVisible().shift(); // remember previous tab state - - var order = acf.getPreference('this.tabs') || []; - var groupIndex = this.get('index'); - var tabIndex = order[groupIndex]; - - if (this.tabs[tabIndex] && this.tabs[tabIndex].isVisible()) { - tab = this.tabs[tabIndex]; - } // select - - - if (tab) { - this.selectTab(tab); - } else { - this.closeTabs(); - } // set local variable used by tabsManager - - - this.set('initialized', true); - }, - getVisible: function () { - return this.tabs.filter(function (tab) { - return tab.isVisible(); - }); - }, - getActive: function () { - return this.active; - }, - setActive: function (tab) { - return this.active = tab; - }, - hasActive: function () { - return this.active !== false; - }, - isActive: function (tab) { - var active = this.getActive(); - return active && active.cid === tab.cid; - }, - closeActive: function () { - if (this.hasActive()) { - this.closeTab(this.getActive()); - } - }, - openTab: function (tab) { - // close existing tab - this.closeActive(); // open - - tab.open(); // set active - - this.setActive(tab); - }, - closeTab: function (tab) { - // close - tab.close(); // set active - - this.setActive(false); - }, - closeTabs: function () { - this.tabs.map(this.closeTab, this); - }, - selectTab: function (tab) { - // close other tabs - this.tabs.map(function (t) { - if (tab.cid !== t.cid) { - this.closeTab(t); - } - }, this); // open - - this.openTab(tab); - }, - addTab: function ($a, field) { - // create
    • - var $li = $('
    • ' + $a.outerHTML() + '
    • '); // append - - this.$('ul').append($li); // initialize - - var tab = new Tab({ - $el: $li, - field: field, - group: this - }); // store - - this.tabs.push(tab); // return - - return tab; - }, - reset: function () { - // close existing tab - this.closeActive(); // find and active a tab - - return this.refresh(); - }, - refresh: function () { - // bail early if active already exists - if (this.hasActive()) { - return false; - } // find next active tab - - - var tab = this.getVisible().shift(); // open tab - - if (tab) { - this.openTab(tab); - } // return - - - return tab; - }, - onRefresh: function () { - // only for left placements - if (this.get('placement') !== 'left') { - return; - } // vars - - - var $parent = this.$el.parent(); - var $list = this.$el.children('ul'); - var attribute = $parent.is('td') ? 'height' : 'min-height'; // find height (minus 1 for border-bottom) - - var height = $list.position().top + $list.outerHeight(true) - 1; // add css - - $parent.css(attribute, height); - } - }); - var Tab = acf.Model.extend({ - group: false, - field: false, - events: { - 'click a': 'onClick' - }, - index: function () { - return this.$el.index(); - }, - isVisible: function () { - return acf.isVisible(this.$el); - }, - isActive: function () { - return this.$el.hasClass('active'); - }, - open: function () { - // add class - this.$el.addClass('active'); // show field - - this.field.showFields(); - }, - close: function () { - // remove class - this.$el.removeClass('active'); // hide field - - this.field.hideFields(); - }, - onClick: function (e, $el) { - // prevent default - e.preventDefault(); // toggle - - this.toggle(); - }, - toggle: function () { - // bail early if already active - if (this.isActive()) { - return; - } // toggle this tab - - - this.group.openTab(this); - } - }); - var tabsManager = new acf.Model({ - priority: 50, - actions: { - 'prepare': 'render', - 'append': 'render', - 'unload': 'onUnload', - 'invalid_field': 'onInvalidField' - }, - findTabs: function () { - return $('.acf-tab-wrap'); - }, - getTabs: function () { - return acf.getInstances(this.findTabs()); - }, - render: function ($el) { - this.getTabs().map(function (tabs) { - if (!tabs.get('initialized')) { - tabs.initializeTabs(); - } - }); - }, - onInvalidField: function (field) { - // bail early if busy - if (this.busy) { - return; - } // ignore if not hidden by tab - - - if (!field.hiddenByTab) { - return; - } // toggle tab - - - field.hiddenByTab.toggle(); // ignore other invalid fields - - this.busy = true; - this.setTimeout(function () { - this.busy = false; - }, 100); - }, - onUnload: function () { - // vars - var order = []; // loop - - this.getTabs().map(function (group) { - var active = group.hasActive() ? group.getActive().index() : 0; - order.push(active); - }); // bail if no tabs - - if (!order.length) { - return; - } // update - - - acf.setPreference('this.tabs', order); - } - }); + onDuplicate: function( e, $el, $duplicate ){ + if( this.isActive() ) { + $duplicate.prevAll('.acf-tab-wrap:first').remove(); + } + } + }); + + acf.registerFieldType( Field ); + + + /** + * tabs + * + * description + * + * @date 8/2/18 + * @since 5.6.5 + * + * @param type $var Description. Default. + * @return type Description. + */ + + var i = 0; + var Tabs = acf.Model.extend({ + + tabs: [], + + active: false, + + actions: { + 'refresh': 'onRefresh' + }, + + data: { + before: false, + placement: 'top', + index: 0, + initialized: false, + }, + + setup: function( settings ){ + + // data + $.extend(this.data, settings); + + // define this prop to avoid scope issues + this.tabs = []; + this.active = false; + + // vars + var placement = this.get('placement'); + var $before = this.get('before'); + var $parent = $before.parent(); + + // add sidebar for left placement + if( placement == 'left' && $parent.hasClass('acf-fields') ) { + $parent.addClass('-sidebar'); + } + + // create wrap + if( $before.is('tr') ) { + this.$el = $(''); + } else { + this.$el = $('
        '); + } + + // append + $before.before( this.$el ); + + // set index + this.set('index', i, true); + i++; + }, + + initializeTabs: function(){ + + // find first visible tab + var tab = this.getVisible().shift(); + + // remember previous tab state + var order = acf.getPreference('this.tabs') || []; + var groupIndex = this.get('index'); + var tabIndex = order[ groupIndex ]; + + if( this.tabs[ tabIndex ] && this.tabs[ tabIndex ].isVisible() ) { + tab = this.tabs[ tabIndex ]; + } + + // select + if( tab ) { + this.selectTab( tab ); + } else { + this.closeTabs(); + } + + // set local variable used by tabsManager + this.set('initialized', true); + }, + + getVisible: function(){ + return this.tabs.filter(function( tab ){ + return tab.isVisible(); + }); + }, + + getActive: function(){ + return this.active; + }, + + setActive: function( tab ){ + return this.active = tab; + }, + + hasActive: function(){ + return (this.active !== false); + }, + + isActive: function( tab ){ + var active = this.getActive(); + return (active && active.cid === tab.cid); + }, + + closeActive: function(){ + if( this.hasActive() ) { + this.closeTab( this.getActive() ); + } + }, + + openTab: function( tab ){ + + // close existing tab + this.closeActive(); + + // open + tab.open(); + + // set active + this.setActive( tab ); + }, + + closeTab: function( tab ){ + + // close + tab.close(); + + // set active + this.setActive( false ); + }, + + closeTabs: function(){ + this.tabs.map( this.closeTab, this ); + }, + + selectTab: function( tab ){ + + // close other tabs + this.tabs.map(function( t ){ + if( tab.cid !== t.cid ) { + this.closeTab( t ); + } + }, this); + + // open + this.openTab( tab ); + + }, + + addTab: function( $a, field ){ + + // create
      • + var $li = $('
      • ' + $a.outerHTML() + '
      • '); + + // append + this.$('ul').append( $li ); + + // initialize + var tab = new Tab({ + $el: $li, + field: field, + group: this, + }); + + // store + this.tabs.push( tab ); + + // return + return tab; + }, + + reset: function(){ + + // close existing tab + this.closeActive(); + + // find and active a tab + return this.refresh(); + }, + + refresh: function(){ + + // bail early if active already exists + if( this.hasActive() ) { + return false; + } + + // find next active tab + var tab = this.getVisible().shift(); + + // open tab + if( tab ) { + this.openTab( tab ); + } + + // return + return tab; + }, + + onRefresh: function(){ + + // only for left placements + if( this.get('placement') !== 'left' ) { + return; + } + + // vars + var $parent = this.$el.parent(); + var $list = this.$el.children('ul'); + var attribute = $parent.is('td') ? 'height' : 'min-height'; + + // find height (minus 1 for border-bottom) + var height = $list.position().top + $list.outerHeight(true) - 1; + + // add css + $parent.css(attribute, height); + } + }); + + var Tab = acf.Model.extend({ + + group: false, + + field: false, + + events: { + 'click a': 'onClick' + }, + + index: function(){ + return this.$el.index(); + }, + + isVisible: function(){ + return acf.isVisible( this.$el ); + }, + + isActive: function(){ + return this.$el.hasClass('active'); + }, + + open: function(){ + + // add class + this.$el.addClass('active'); + + // show field + this.field.showFields(); + }, + + close: function(){ + + // remove class + this.$el.removeClass('active'); + + // hide field + this.field.hideFields(); + }, + + onClick: function( e, $el ){ + + // prevent default + e.preventDefault(); + + // toggle + this.toggle(); + }, + + toggle: function(){ + + // bail early if already active + if( this.isActive() ) { + return; + } + + // toggle this tab + this.group.openTab( this ); + } + }); + + var tabsManager = new acf.Model({ + + priority: 50, + + actions: { + 'prepare': 'render', + 'append': 'render', + 'unload': 'onUnload', + 'invalid_field': 'onInvalidField' + }, + + findTabs: function(){ + return $('.acf-tab-wrap'); + }, + + getTabs: function(){ + return acf.getInstances( this.findTabs() ); + }, + + render: function( $el ){ + this.getTabs().map(function( tabs ){ + if( !tabs.get('initialized') ) { + tabs.initializeTabs(); + } + }); + }, + + onInvalidField: function( field ){ + + // bail early if busy + if( this.busy ) { + return; + } + + // ignore if not hidden by tab + if( !field.hiddenByTab ) { + return; + } + + // toggle tab + field.hiddenByTab.toggle(); + + // ignore other invalid fields + this.busy = true; + this.setTimeout(function(){ + this.busy = false; + }, 100); + }, + + onUnload: function(){ + + // vars + var order = []; + + // loop + this.getTabs().map(function( group ){ + var active = group.hasActive() ? group.getActive().index() : 0; + order.push(active); + }); + + // bail if no tabs + if( !order.length ) { + return; + } + + // update + acf.setPreference('this.tabs', order); + } + }); + })(jQuery); - -(function ($, undefined) { - var Field = acf.models.SelectField.extend({ - type: 'post_object' - }); - acf.registerFieldType(Field); +(function($, undefined){ + + var Field = acf.models.SelectField.extend({ + type: 'post_object', + }); + + acf.registerFieldType( Field ); + })(jQuery); - -(function ($, undefined) { - var Field = acf.models.SelectField.extend({ - type: 'page_link' - }); - acf.registerFieldType(Field); +(function($, undefined){ + + var Field = acf.models.SelectField.extend({ + type: 'page_link', + }); + + acf.registerFieldType( Field ); + })(jQuery); - -(function ($, undefined) { - var Field = acf.models.SelectField.extend({ - type: 'user' - }); - acf.registerFieldType(Field); +(function($, undefined){ + + var Field = acf.models.SelectField.extend({ + type: 'user', + }); + + acf.registerFieldType( Field ); + })(jQuery); +(function($, undefined){ + + var Field = acf.Field.extend({ + + type: 'taxonomy', + + data: { + 'ftype': 'select' + }, + + select2: false, + + wait: 'load', + + events: { + 'click a[data-name="add"]': 'onClickAdd', + 'click input[type="radio"]': 'onClickRadio', + 'removeField': 'onRemove' + }, + + $control: function(){ + return this.$('.acf-taxonomy-field'); + }, + + $input: function(){ + return this.getRelatedPrototype().$input.apply(this, arguments); + }, + + getRelatedType: function(){ + + // vars + var fieldType = this.get('ftype'); + + // normalize + if( fieldType == 'multi_select' ) { + fieldType = 'select'; + } -(function ($, undefined) { - var Field = acf.Field.extend({ - type: 'taxonomy', - data: { - 'ftype': 'select' - }, - select2: false, - wait: 'load', - events: { - 'click a[data-name="add"]': 'onClickAdd', - 'click input[type="radio"]': 'onClickRadio', - 'removeField': 'onRemove' - }, - $control: function () { - return this.$('.acf-taxonomy-field'); - }, - $input: function () { - return this.getRelatedPrototype().$input.apply(this, arguments); - }, - getRelatedType: function () { - // vars - var fieldType = this.get('ftype'); // normalize - - if (fieldType == 'multi_select') { - fieldType = 'select'; - } // return - - - return fieldType; - }, - getRelatedPrototype: function () { - return acf.getFieldType(this.getRelatedType()).prototype; - }, - getValue: function () { - return this.getRelatedPrototype().getValue.apply(this, arguments); - }, - setValue: function () { - return this.getRelatedPrototype().setValue.apply(this, arguments); - }, - initialize: function () { - this.getRelatedPrototype().initialize.apply(this, arguments); - }, - onRemove: function () { - var proto = this.getRelatedPrototype(); - - if (proto.onRemove) { - proto.onRemove.apply(this, arguments); - } - }, - onClickAdd: function (e, $el) { - // vars - var field = this; - var popup = false; - var $form = false; - var $name = false; - var $parent = false; - var $button = false; - var $message = false; - var notice = false; // step 1. - - var step1 = function () { - // popup - popup = acf.newPopup({ - title: $el.attr('title'), - loading: true, - width: '300px' - }); // ajax - - var ajaxData = { - action: 'acf/fields/taxonomy/add_term', - field_key: field.get('key') - }; // get HTML - - $.ajax({ - url: acf.get('ajaxurl'), - data: acf.prepareForAjax(ajaxData), - type: 'post', - dataType: 'html', - success: step2 - }); - }; // step 2. - - - var step2 = function (html) { - // update popup - popup.loading(false); - popup.content(html); // vars - - $form = popup.$('form'); - $name = popup.$('input[name="term_name"]'); - $parent = popup.$('select[name="term_parent"]'); - $button = popup.$('.acf-submit-button'); // focus - - $name.trigger('focus'); // submit form - - popup.on('submit', 'form', step3); - }; // step 3. - - - var step3 = function (e, $el) { - // prevent - e.preventDefault(); - e.stopImmediatePropagation(); // basic validation - - if ($name.val() === '') { - $name.trigger('focus'); - return false; - } // disable - - - acf.startButtonLoading($button); // ajax - - var ajaxData = { - action: 'acf/fields/taxonomy/add_term', - field_key: field.get('key'), - term_name: $name.val(), - term_parent: $parent.length ? $parent.val() : 0 - }; - $.ajax({ - url: acf.get('ajaxurl'), - data: acf.prepareForAjax(ajaxData), - type: 'post', - dataType: 'json', - success: step4 - }); - }; // step 4. - - - var step4 = function (json) { - // enable - acf.stopButtonLoading($button); // remove prev notice - - if (notice) { - notice.remove(); - } // success - - - if (acf.isAjaxSuccess(json)) { - // clear name - $name.val(''); // update term lists - - step5(json.data); // notice - - notice = acf.newNotice({ - type: 'success', - text: acf.getAjaxMessage(json), - target: $form, - timeout: 2000, - dismiss: false - }); - } else { - // notice - notice = acf.newNotice({ - type: 'error', - text: acf.getAjaxError(json), - target: $form, - timeout: 2000, - dismiss: false - }); - } // focus - - - $name.trigger('focus'); - }; // step 5. - - - var step5 = function (term) { - // update parent dropdown - var $option = $(''); - - if (term.term_parent) { - $parent.children('option[value="' + term.term_parent + '"]').after($option); - } else { - $parent.append($option); - } // add this new term to all taxonomy field - - - var fields = acf.getFields({ - type: 'taxonomy' - }); - fields.map(function (otherField) { - if (otherField.get('taxonomy') == field.get('taxonomy')) { - otherField.appendTerm(term); - } - }); // select - - field.selectTerm(term.term_id); - }; // run - - - step1(); - }, - appendTerm: function (term) { - if (this.getRelatedType() == 'select') { - this.appendTermSelect(term); - } else { - this.appendTermCheckbox(term); - } - }, - appendTermSelect: function (term) { - this.select2.addOption({ - id: term.term_id, - text: term.term_label - }); - }, - appendTermCheckbox: function (term) { - // vars - var name = this.$('[name]:first').attr('name'); - var $ul = this.$('ul:first'); // allow multiple selection - - if (this.getRelatedType() == 'checkbox') { - name += '[]'; - } // create new li - - - var $li = $(['
      • ', '', '
      • '].join('')); // find parent - - if (term.term_parent) { - // vars - var $parent = $ul.find('li[data-id="' + term.term_parent + '"]'); // update vars - - $ul = $parent.children('ul'); // create ul - - if (!$ul.exists()) { - $ul = $('
          '); - $parent.append($ul); - } - } // append - - - $ul.append($li); - }, - selectTerm: function (id) { - if (this.getRelatedType() == 'select') { - this.select2.selectOption(id); - } else { - var $input = this.$('input[value="' + id + '"]'); - $input.prop('checked', true).trigger('change'); - } - }, - onClickRadio: function (e, $el) { - // vars - var $label = $el.parent('label'); - var selected = $label.hasClass('selected'); // remove previous selected - - this.$('.selected').removeClass('selected'); // add active class - - $label.addClass('selected'); // allow null - - if (this.get('allow_null') && selected) { - $label.removeClass('selected'); - $el.prop('checked', false).trigger('change'); - } - } - }); - acf.registerFieldType(Field); + // return + return fieldType; + + }, + + getRelatedPrototype: function(){ + return acf.getFieldType( this.getRelatedType() ).prototype; + }, + + getValue: function(){ + return this.getRelatedPrototype().getValue.apply(this, arguments); + }, + + setValue: function(){ + return this.getRelatedPrototype().setValue.apply(this, arguments); + }, + + initialize: function(){ + this.getRelatedPrototype().initialize.apply(this, arguments); + }, + + onRemove: function(){ + var proto = this.getRelatedPrototype(); + if( proto.onRemove ) { + proto.onRemove.apply(this, arguments); + } + }, + + onClickAdd: function( e, $el ){ + + // vars + var field = this; + var popup = false; + var $form = false; + var $name = false; + var $parent = false; + var $button = false; + var $message = false; + var notice = false; + + // step 1. + var step1 = function(){ + + // popup + popup = acf.newPopup({ + title: $el.attr('title'), + loading: true, + width: '300px' + }); + + // ajax + var ajaxData = { + action: 'acf/fields/taxonomy/add_term', + field_key: field.get('key') + }; + + // get HTML + $.ajax({ + url: acf.get('ajaxurl'), + data: acf.prepareForAjax(ajaxData), + type: 'post', + dataType: 'html', + success: step2 + }); + }; + + // step 2. + var step2 = function( html ){ + + // update popup + popup.loading(false); + popup.content(html); + + // vars + $form = popup.$('form'); + $name = popup.$('input[name="term_name"]'); + $parent = popup.$('select[name="term_parent"]'); + $button = popup.$('.acf-submit-button'); + + // focus + $name.trigger('focus'); + + // submit form + popup.on('submit', 'form', step3); + }; + + // step 3. + var step3 = function( e, $el ){ + + // prevent + e.preventDefault(); + e.stopImmediatePropagation(); + + // basic validation + if( $name.val() === '' ) { + $name.trigger('focus'); + return false; + } + + // disable + acf.startButtonLoading( $button ); + + // ajax + var ajaxData = { + action: 'acf/fields/taxonomy/add_term', + field_key: field.get('key'), + term_name: $name.val(), + term_parent: $parent.length ? $parent.val() : 0 + }; + + $.ajax({ + url: acf.get('ajaxurl'), + data: acf.prepareForAjax(ajaxData), + type: 'post', + dataType: 'json', + success: step4 + }); + }; + + // step 4. + var step4 = function( json ){ + + // enable + acf.stopButtonLoading( $button ); + + // remove prev notice + if( notice ) { + notice.remove(); + } + + // success + if( acf.isAjaxSuccess(json) ) { + + // clear name + $name.val(''); + + // update term lists + step5( json.data ); + + // notice + notice = acf.newNotice({ + type: 'success', + text: acf.getAjaxMessage(json), + target: $form, + timeout: 2000, + dismiss: false + }); + + } else { + + // notice + notice = acf.newNotice({ + type: 'error', + text: acf.getAjaxError(json), + target: $form, + timeout: 2000, + dismiss: false + }); + } + + // focus + $name.trigger('focus'); + }; + + // step 5. + var step5 = function( term ){ + + // update parent dropdown + var $option = $(''); + if( term.term_parent ) { + $parent.children('option[value="' + term.term_parent + '"]').after( $option ); + } else { + $parent.append( $option ); + } + + // add this new term to all taxonomy field + var fields = acf.getFields({ + type: 'taxonomy' + }); + + fields.map(function( otherField ){ + if( otherField.get('taxonomy') == field.get('taxonomy') ) { + otherField.appendTerm( term ); + } + }); + + // select + field.selectTerm( term.term_id ); + }; + + // run + step1(); + }, + + appendTerm: function( term ){ + + if( this.getRelatedType() == 'select' ) { + this.appendTermSelect( term ); + } else { + this.appendTermCheckbox( term ); + } + }, + + appendTermSelect: function( term ){ + + this.select2.addOption({ + id: term.term_id, + text: term.term_label + }); + + }, + + appendTermCheckbox: function( term ){ + + // vars + var name = this.$('[name]:first').attr('name'); + var $ul = this.$('ul:first'); + + // allow multiple selection + if( this.getRelatedType() == 'checkbox' ) { + name += '[]'; + } + + // create new li + var $li = $([ + '
        • ', + '', + '
        • ' + ].join('')); + + // find parent + if( term.term_parent ) { + + // vars + var $parent = $ul.find('li[data-id="' + term.term_parent + '"]'); + + // update vars + $ul = $parent.children('ul'); + + // create ul + if( !$ul.exists() ) { + $ul = $('
            '); + $parent.append( $ul ); + } + } + + // append + $ul.append( $li ); + }, + + selectTerm: function( id ){ + if( this.getRelatedType() == 'select' ) { + this.select2.selectOption( id ); + } else { + var $input = this.$('input[value="' + id + '"]'); + $input.prop('checked', true).trigger('change'); + } + }, + + onClickRadio: function( e, $el ){ + + // vars + var $label = $el.parent('label'); + var selected = $label.hasClass('selected'); + + // remove previous selected + this.$('.selected').removeClass('selected'); + + // add active class + $label.addClass('selected'); + + // allow null + if( this.get('allow_null') && selected ) { + $label.removeClass('selected'); + $el.prop('checked', false).trigger('change'); + } + } + }); + + acf.registerFieldType( Field ); + })(jQuery); +(function($, undefined){ + + var Field = acf.models.DatePickerField.extend({ + + type: 'time_picker', + + $control: function(){ + return this.$('.acf-time-picker'); + }, + + initialize: function(){ + + // vars + var $input = this.$input(); + var $inputText = this.$inputText(); + + // args + var args = { + timeFormat: this.get('time_format'), + altField: $input, + altFieldTimeOnly: false, + altTimeFormat: 'HH:mm:ss', + showButtonPanel: true, + controlType: 'select', + oneLine: true, + closeText: acf.get('dateTimePickerL10n').selectText, + timeOnly: true, + }; + + // add custom 'Close = Select' functionality + args.onClose = function( value, dp_instance, t_instance ){ + + // vars + var $close = dp_instance.dpDiv.find('.ui-datepicker-close'); + + // if clicking close button + if( !value && $close.is(':hover') ) { + t_instance._updateDateTime(); + } + }; -(function ($, undefined) { - var Field = acf.models.DatePickerField.extend({ - type: 'time_picker', - $control: function () { - return this.$('.acf-time-picker'); - }, - initialize: function () { - // vars - var $input = this.$input(); - var $inputText = this.$inputText(); // args - - var args = { - timeFormat: this.get('time_format'), - altField: $input, - altFieldTimeOnly: false, - altTimeFormat: 'HH:mm:ss', - showButtonPanel: true, - controlType: 'select', - oneLine: true, - closeText: acf.get('dateTimePickerL10n').selectText, - timeOnly: true - }; // add custom 'Close = Select' functionality - - args.onClose = function (value, dp_instance, t_instance) { - // vars - var $close = dp_instance.dpDiv.find('.ui-datepicker-close'); // if clicking close button - - if (!value && $close.is(':hover')) { - t_instance._updateDateTime(); - } - }; // filter - - - args = acf.applyFilters('time_picker_args', args, this); // add date time picker - - acf.newTimePicker($inputText, args); // action - - acf.doAction('time_picker_init', $inputText, args, this); - } - }); - acf.registerFieldType(Field); // add - - acf.newTimePicker = function ($input, args) { - // bail ealry if no datepicker library - if (typeof $.timepicker === 'undefined') { - return false; - } // defaults - - - args = args || {}; // initialize - - $input.timepicker(args); // wrap the datepicker (only if it hasn't already been wrapped) - - if ($('body > #ui-datepicker-div').exists()) { - $('body > #ui-datepicker-div').wrap('
            '); - } - }; + + // filter + args = acf.applyFilters('time_picker_args', args, this); + + // add date time picker + acf.newTimePicker( $inputText, args ); + + // action + acf.doAction('time_picker_init', $inputText, args, this); + } + }); + + acf.registerFieldType( Field ); + + + // add + acf.newTimePicker = function( $input, args ){ + + // bail ealry if no datepicker library + if( typeof $.timepicker === 'undefined' ) { + return false; + } + + // defaults + args = args || {}; + + // initialize + $input.timepicker( args ); + + // wrap the datepicker (only if it hasn't already been wrapped) + if( $('body > #ui-datepicker-div').exists() ) { + $('body > #ui-datepicker-div').wrap('
            '); + } + }; + })(jQuery); - -(function ($, undefined) { - var Field = acf.Field.extend({ - type: 'true_false', - events: { - 'change .acf-switch-input': 'onChange', - 'focus .acf-switch-input': 'onFocus', - 'blur .acf-switch-input': 'onBlur', - 'keypress .acf-switch-input': 'onKeypress' - }, - $input: function () { - return this.$('input[type="checkbox"]'); - }, - $switch: function () { - return this.$('.acf-switch'); - }, - getValue: function () { - return this.$input().prop('checked') ? 1 : 0; - }, - initialize: function () { - this.render(); - }, - render: function () { - // vars - var $switch = this.$switch(); // bail ealry if no $switch - - if (!$switch.length) return; // vars - - var $on = $switch.children('.acf-switch-on'); - var $off = $switch.children('.acf-switch-off'); - var width = Math.max($on.width(), $off.width()); // bail ealry if no width - - if (!width) return; // set widths - - $on.css('min-width', width); - $off.css('min-width', width); - }, - switchOn: function () { - this.$input().prop('checked', true); - this.$switch().addClass('-on'); - }, - switchOff: function () { - this.$input().prop('checked', false); - this.$switch().removeClass('-on'); - }, - onChange: function (e, $el) { - if ($el.prop('checked')) { - this.switchOn(); - } else { - this.switchOff(); - } - }, - onFocus: function (e, $el) { - this.$switch().addClass('-focus'); - }, - onBlur: function (e, $el) { - this.$switch().removeClass('-focus'); - }, - onKeypress: function (e, $el) { - // left - if (e.keyCode === 37) { - return this.switchOff(); - } // right - - - if (e.keyCode === 39) { - return this.switchOn(); - } - } - }); - acf.registerFieldType(Field); +(function($, undefined){ + + var Field = acf.Field.extend({ + + type: 'true_false', + + events: { + 'change .acf-switch-input': 'onChange', + 'focus .acf-switch-input': 'onFocus', + 'blur .acf-switch-input': 'onBlur', + 'keypress .acf-switch-input': 'onKeypress' + }, + + $input: function(){ + return this.$('input[type="checkbox"]'); + }, + + $switch: function(){ + return this.$('.acf-switch'); + }, + + getValue: function(){ + return this.$input().prop('checked') ? 1 : 0; + }, + + initialize: function(){ + this.render(); + }, + + render: function(){ + + // vars + var $switch = this.$switch(); + + // bail ealry if no $switch + if( !$switch.length ) return; + + // vars + var $on = $switch.children('.acf-switch-on'); + var $off = $switch.children('.acf-switch-off'); + var width = Math.max( $on.width(), $off.width() ); + + // bail ealry if no width + if( !width ) return; + + // set widths + $on.css( 'min-width', width ); + $off.css( 'min-width', width ); + + }, + + switchOn: function() { + this.$input().prop('checked', true); + this.$switch().addClass('-on'); + }, + + switchOff: function() { + this.$input().prop('checked', false); + this.$switch().removeClass('-on'); + }, + + onChange: function( e, $el ){ + if( $el.prop('checked') ) { + this.switchOn(); + } else { + this.switchOff(); + } + }, + + onFocus: function( e, $el ){ + this.$switch().addClass('-focus'); + }, + + onBlur: function( e, $el ){ + this.$switch().removeClass('-focus'); + }, + + onKeypress: function( e, $el ){ + + // left + if( e.keyCode === 37 ) { + return this.switchOff(); + } + + // right + if( e.keyCode === 39 ) { + return this.switchOn(); + } + + } + }); + + acf.registerFieldType( Field ); + })(jQuery); - -(function ($, undefined) { - var Field = acf.Field.extend({ - type: 'url', - events: { - 'keyup input[type="url"]': 'onkeyup' - }, - $control: function () { - return this.$('.acf-input-wrap'); - }, - $input: function () { - return this.$('input[type="url"]'); - }, - initialize: function () { - this.render(); - }, - isValid: function () { - // vars - var val = this.val(); // bail early if no val - - if (!val) { - return false; - } // url - - - if (val.indexOf('://') !== -1) { - return true; - } // protocol relative url - - - if (val.indexOf('//') === 0) { - return true; - } // return - - - return false; - }, - render: function () { - // add class - if (this.isValid()) { - this.$control().addClass('-valid'); - } else { - this.$control().removeClass('-valid'); - } - }, - onkeyup: function (e, $el) { - this.render(); - } - }); - acf.registerFieldType(Field); +(function($, undefined){ + + var Field = acf.Field.extend({ + + type: 'url', + + events: { + 'keyup input[type="url"]': 'onkeyup' + }, + + $control: function(){ + return this.$('.acf-input-wrap'); + }, + + $input: function(){ + return this.$('input[type="url"]'); + }, + + initialize: function(){ + this.render(); + }, + + isValid: function(){ + + // vars + var val = this.val(); + + // bail early if no val + if( !val ) { + return false; + } + + // url + if( val.indexOf('://') !== -1 ) { + return true; + } + + // protocol relative url + if( val.indexOf('//') === 0 ) { + return true; + } + + // return + return false; + }, + + render: function(){ + + // add class + if( this.isValid() ) { + this.$control().addClass('-valid'); + } else { + this.$control().removeClass('-valid'); + } + }, + + onkeyup: function( e, $el ){ + this.render(); + } + }); + + acf.registerFieldType( Field ); + })(jQuery); +(function($, undefined){ + + var Field = acf.Field.extend({ + + type: 'wysiwyg', + + wait: 'load', + + events: { + 'mousedown .acf-editor-wrap.delay': 'onMousedown', + 'unmountField': 'disableEditor', + 'remountField': 'enableEditor', + 'removeField': 'disableEditor' + }, + + $control: function(){ + return this.$('.acf-editor-wrap'); + }, + + $input: function(){ + return this.$('textarea'); + }, + + getMode: function(){ + return this.$control().hasClass('tmce-active') ? 'visual' : 'text'; + }, + + initialize: function(){ + + // initializeEditor if no delay + if( !this.$control().hasClass('delay') ) { + this.initializeEditor(); + } + }, + + initializeEditor: function(){ + + // vars + var $wrap = this.$control(); + var $textarea = this.$input(); + var args = { + tinymce: true, + quicktags: true, + toolbar: this.get('toolbar'), + mode: this.getMode(), + field: this + }; + + // generate new id + var oldId = $textarea.attr('id'); + var newId = acf.uniqueId('acf-editor-'); + + // Backup textarea data. + var inputData = $textarea.data(); + var inputVal = $textarea.val(); + + // rename + acf.rename({ + target: $wrap, + search: oldId, + replace: newId, + destructive: true + }); + + // update id + this.set('id', newId, true); + + // apply data to new textarea (acf.rename creates a new textarea element due to destructive mode) + // fixes bug where conditional logic "disabled" is lost during "screen_check" + this.$input().data( inputData ).val( inputVal ); -(function ($, undefined) { - var Field = acf.Field.extend({ - type: 'wysiwyg', - wait: 'load', - events: { - 'mousedown .acf-editor-wrap.delay': 'onMousedown', - 'unmountField': 'disableEditor', - 'remountField': 'enableEditor', - 'removeField': 'disableEditor' - }, - $control: function () { - return this.$('.acf-editor-wrap'); - }, - $input: function () { - return this.$('textarea'); - }, - getMode: function () { - return this.$control().hasClass('tmce-active') ? 'visual' : 'text'; - }, - initialize: function () { - // initializeEditor if no delay - if (!this.$control().hasClass('delay')) { - this.initializeEditor(); - } - }, - initializeEditor: function () { - // vars - var $wrap = this.$control(); - var $textarea = this.$input(); - var args = { - tinymce: true, - quicktags: true, - toolbar: this.get('toolbar'), - mode: this.getMode(), - field: this - }; // generate new id - - var oldId = $textarea.attr('id'); - var newId = acf.uniqueId('acf-editor-'); // Backup textarea data. - - var inputData = $textarea.data(); - var inputVal = $textarea.val(); // rename - - acf.rename({ - target: $wrap, - search: oldId, - replace: newId, - destructive: true - }); // update id - - this.set('id', newId, true); // apply data to new textarea (acf.rename creates a new textarea element due to destructive mode) - // fixes bug where conditional logic "disabled" is lost during "screen_check" - - this.$input().data(inputData).val(inputVal); // initialize - - acf.tinymce.initialize(newId, args); - }, - onMousedown: function (e) { - // prevent default - e.preventDefault(); // remove delay class - - var $wrap = this.$control(); - $wrap.removeClass('delay'); - $wrap.find('.acf-editor-toolbar').remove(); // initialize - - this.initializeEditor(); - }, - enableEditor: function () { - if (this.getMode() == 'visual') { - acf.tinymce.enable(this.get('id')); - } - }, - disableEditor: function () { - acf.tinymce.destroy(this.get('id')); - } - }); - acf.registerFieldType(Field); + // initialize + acf.tinymce.initialize( newId, args ); + }, + + onMousedown: function( e ){ + + // prevent default + e.preventDefault(); + + // remove delay class + var $wrap = this.$control(); + $wrap.removeClass('delay'); + $wrap.find('.acf-editor-toolbar').remove(); + + // initialize + this.initializeEditor(); + }, + + enableEditor: function(){ + if( this.getMode() == 'visual' ) { + acf.tinymce.enable( this.get('id') ); + } + }, + + disableEditor: function(){ + acf.tinymce.destroy( this.get('id') ); + } + }); + + acf.registerFieldType( Field ); + })(jQuery); +(function($, undefined){ + + // vars + var storage = []; + + /** + * acf.Condition + * + * description + * + * @date 23/3/18 + * @since 5.6.9 + * + * @param type $var Description. Default. + * @return type Description. + */ + + acf.Condition = acf.Model.extend({ + + type: '', // used for model name + operator: '==', // rule operator + label: '', // label shown when editing fields + choiceType: 'input', // input, select + fieldTypes: [], // auto connect this conditions with these field types + + data: { + conditions: false, // the parent instance + field: false, // the field which we query against + rule: {} // the rule [field, operator, value] + }, + + events: { + 'change': 'change', + 'keyup': 'change', + 'enableField': 'change', + 'disableField': 'change' + }, + + setup: function( props ){ + $.extend(this.data, props); + }, + + getEventTarget: function( $el, event ){ + return $el || this.get('field').$el; + }, + + change: function( e, $el ){ + this.get('conditions').change( e ); + }, + + match: function( rule, field ){ + return false; + }, + + calculate: function(){ + return this.match( this.get('rule'), this.get('field') ); + }, + + choices: function( field ){ + return ''; + } + }); + + /** + * acf.newCondition + * + * description + * + * @date 1/2/18 + * @since 5.6.5 + * + * @param type $var Description. Default. + * @return type Description. + */ + + acf.newCondition = function( rule, conditions ){ + + // currently setting up conditions for fieldX, this field is the 'target' + var target = conditions.get('field'); + + // use the 'target' to find the 'trigger' field. + // - this field is used to setup the conditional logic events + var field = target.getField( rule.field ); + + // bail ealry if no target or no field (possible if field doesn't exist due to HTML error) + if( !target || !field ) { + return false; + } + + // vars + var args = { + rule: rule, + target: target, + conditions: conditions, + field: field + }; + + // vars + var fieldType = field.get('type'); + var operator = rule.operator; + + // get avaibale conditions + var conditionTypes = acf.getConditionTypes({ + fieldType: fieldType, + operator: operator, + }); + + // instantiate + var model = conditionTypes[0] || acf.Condition; + + // instantiate + var condition = new model( args ); + + // return + return condition; + }; -(function ($, undefined) { - // vars - var storage = []; - /** - * acf.Condition - * - * description - * - * @date 23/3/18 - * @since 5.6.9 - * - * @param type $var Description. Default. - * @return type Description. - */ - - acf.Condition = acf.Model.extend({ - type: '', - // used for model name - operator: '==', - // rule operator - label: '', - // label shown when editing fields - choiceType: 'input', - // input, select - fieldTypes: [], - // auto connect this conditions with these field types - data: { - conditions: false, - // the parent instance - field: false, - // the field which we query against - rule: {} // the rule [field, operator, value] - - }, - events: { - 'change': 'change', - 'keyup': 'change', - 'enableField': 'change', - 'disableField': 'change' - }, - setup: function (props) { - $.extend(this.data, props); - }, - getEventTarget: function ($el, event) { - return $el || this.get('field').$el; - }, - change: function (e, $el) { - this.get('conditions').change(e); - }, - match: function (rule, field) { - return false; - }, - calculate: function () { - return this.match(this.get('rule'), this.get('field')); - }, - choices: function (field) { - return ''; - } - }); - /** - * acf.newCondition - * - * description - * - * @date 1/2/18 - * @since 5.6.5 - * - * @param type $var Description. Default. - * @return type Description. - */ - - acf.newCondition = function (rule, conditions) { - // currently setting up conditions for fieldX, this field is the 'target' - var target = conditions.get('field'); // use the 'target' to find the 'trigger' field. - // - this field is used to setup the conditional logic events - - var field = target.getField(rule.field); // bail ealry if no target or no field (possible if field doesn't exist due to HTML error) - - if (!target || !field) { - return false; - } // vars - - - var args = { - rule: rule, - target: target, - conditions: conditions, - field: field - }; // vars - - var fieldType = field.get('type'); - var operator = rule.operator; // get avaibale conditions - - var conditionTypes = acf.getConditionTypes({ - fieldType: fieldType, - operator: operator - }); // instantiate - - var model = conditionTypes[0] || acf.Condition; // instantiate - - var condition = new model(args); // return - - return condition; - }; - /** - * mid - * - * Calculates the model ID for a field type - * - * @date 15/12/17 - * @since 5.6.5 - * - * @param string type - * @return string - */ - - - var modelId = function (type) { - return acf.strPascalCase(type || '') + 'Condition'; - }; - /** - * acf.registerConditionType - * - * description - * - * @date 1/2/18 - * @since 5.6.5 - * - * @param type $var Description. Default. - * @return type Description. - */ - - - acf.registerConditionType = function (model) { - // vars - var proto = model.prototype; - var type = proto.type; - var mid = modelId(type); // store model - - acf.models[mid] = model; // store reference - - storage.push(type); - }; - /** - * acf.getConditionType - * - * description - * - * @date 1/2/18 - * @since 5.6.5 - * - * @param type $var Description. Default. - * @return type Description. - */ - - - acf.getConditionType = function (type) { - var mid = modelId(type); - return acf.models[mid] || false; - }; - /** - * acf.registerConditionForFieldType - * - * description - * - * @date 1/2/18 - * @since 5.6.5 - * - * @param type $var Description. Default. - * @return type Description. - */ - - - acf.registerConditionForFieldType = function (conditionType, fieldType) { - // get model - var model = acf.getConditionType(conditionType); // append - - if (model) { - model.prototype.fieldTypes.push(fieldType); - } - }; - /** - * acf.getConditionTypes - * - * description - * - * @date 1/2/18 - * @since 5.6.5 - * - * @param type $var Description. Default. - * @return type Description. - */ - - - acf.getConditionTypes = function (args) { - // defaults - args = acf.parseArgs(args, { - fieldType: '', - operator: '' - }); // clonse available types - - var types = []; // loop - - storage.map(function (type) { - // vars - var model = acf.getConditionType(type); - var ProtoFieldTypes = model.prototype.fieldTypes; - var ProtoOperator = model.prototype.operator; // check fieldType - - if (args.fieldType && ProtoFieldTypes.indexOf(args.fieldType) === -1) { - return; - } // check operator - - - if (args.operator && ProtoOperator !== args.operator) { - return; - } // append - - - types.push(model); - }); // return - - return types; - }; + /** + * mid + * + * Calculates the model ID for a field type + * + * @date 15/12/17 + * @since 5.6.5 + * + * @param string type + * @return string + */ + + var modelId = function( type ) { + return acf.strPascalCase( type || '' ) + 'Condition'; + }; + + /** + * acf.registerConditionType + * + * description + * + * @date 1/2/18 + * @since 5.6.5 + * + * @param type $var Description. Default. + * @return type Description. + */ + + acf.registerConditionType = function( model ){ + + // vars + var proto = model.prototype; + var type = proto.type; + var mid = modelId( type ); + + // store model + acf.models[ mid ] = model; + + // store reference + storage.push( type ); + }; + + /** + * acf.getConditionType + * + * description + * + * @date 1/2/18 + * @since 5.6.5 + * + * @param type $var Description. Default. + * @return type Description. + */ + + acf.getConditionType = function( type ){ + var mid = modelId( type ); + return acf.models[ mid ] || false; + } + + /** + * acf.registerConditionForFieldType + * + * description + * + * @date 1/2/18 + * @since 5.6.5 + * + * @param type $var Description. Default. + * @return type Description. + */ + + acf.registerConditionForFieldType = function( conditionType, fieldType ){ + + // get model + var model = acf.getConditionType( conditionType ); + + // append + if( model ) { + model.prototype.fieldTypes.push( fieldType ); + } + }; + + /** + * acf.getConditionTypes + * + * description + * + * @date 1/2/18 + * @since 5.6.5 + * + * @param type $var Description. Default. + * @return type Description. + */ + + acf.getConditionTypes = function( args ){ + + // defaults + args = acf.parseArgs(args, { + fieldType: '', + operator: '' + }); + + // clonse available types + var types = []; + + // loop + storage.map(function( type ){ + + // vars + var model = acf.getConditionType(type); + var ProtoFieldTypes = model.prototype.fieldTypes; + var ProtoOperator = model.prototype.operator; + + // check fieldType + if( args.fieldType && ProtoFieldTypes.indexOf( args.fieldType ) === -1 ) { + return; + } + + // check operator + if( args.operator && ProtoOperator !== args.operator ) { + return; + } + + // append + types.push( model ); + }); + + // return + return types; + }; + })(jQuery); +(function($, undefined){ + + // vars + var CONTEXT = 'conditional_logic'; + + /** + * conditionsManager + * + * description + * + * @date 1/2/18 + * @since 5.6.5 + * + * @param type $var Description. Default. + * @return type Description. + */ + + var conditionsManager = new acf.Model({ + + id: 'conditionsManager', + + priority: 20, // run actions later + + actions: { + 'new_field': 'onNewField', + }, + + onNewField: function( field ){ + if( field.has('conditions') ) { + field.getConditions().render(); + } + }, + }); + + /** + * acf.Field.prototype.getField + * + * Finds a field that is related to another field + * + * @date 1/2/18 + * @since 5.6.5 + * + * @param type $var Description. Default. + * @return type Description. + */ + + var getSiblingField = function( field, key ){ + + // find sibling (very fast) + var fields = acf.getFields({ + key: key, + sibling: field.$el, + suppressFilters: true, + }); + + // find sibling-children (fast) + // needed for group fields, accordions, etc + if( !fields.length ) { + fields = acf.getFields({ + key: key, + parent: field.$el.parent(), + suppressFilters: true, + }); + } + + // return + if( fields.length ) { + return fields[0]; + } + return false; + }; + + acf.Field.prototype.getField = function( key ){ + + // get sibling field + var field = getSiblingField( this, key ); + + // return early + if( field ) { + return field; + } + + // move up through each parent and try again + var parents = this.parents(); + for( var i = 0; i < parents.length; i++ ) { + + // get sibling field + field = getSiblingField( parents[i], key ); + + // return early + if( field ) { + return field; + } + } + + // return + return false; + }; + + + /** + * acf.Field.prototype.getConditions + * + * Returns the field's conditions instance + * + * @date 1/2/18 + * @since 5.6.5 + * + * @param type $var Description. Default. + * @return type Description. + */ + + acf.Field.prototype.getConditions = function(){ + + // instantiate + if( !this.conditions ) { + this.conditions = new Conditions( this ); + } + + // return + return this.conditions; + }; + + + /** + * Conditions + * + * description + * + * @date 1/2/18 + * @since 5.6.5 + * + * @param type $var Description. Default. + * @return type Description. + */ + var timeout = false; + var Conditions = acf.Model.extend({ + + id: 'Conditions', + + data: { + field: false, // The field with "data-conditions" (target). + timeStamp: false, // Reference used during "change" event. + groups: [], // The groups of condition instances. + }, + + setup: function( field ){ + + // data + this.data.field = field; + + // vars + var conditions = field.get('conditions'); + + // detect groups + if( conditions instanceof Array ) { + + // detect groups + if( conditions[0] instanceof Array ) { -(function ($, undefined) { - // vars - var CONTEXT = 'conditional_logic'; - /** - * conditionsManager - * - * description - * - * @date 1/2/18 - * @since 5.6.5 - * - * @param type $var Description. Default. - * @return type Description. - */ - - var conditionsManager = new acf.Model({ - id: 'conditionsManager', - priority: 20, - // run actions later - actions: { - 'new_field': 'onNewField' - }, - onNewField: function (field) { - if (field.has('conditions')) { - field.getConditions().render(); - } - } - }); - /** - * acf.Field.prototype.getField - * - * Finds a field that is related to another field - * - * @date 1/2/18 - * @since 5.6.5 - * - * @param type $var Description. Default. - * @return type Description. - */ - - var getSiblingField = function (field, key) { - // find sibling (very fast) - var fields = acf.getFields({ - key: key, - sibling: field.$el, - suppressFilters: true - }); // find sibling-children (fast) - // needed for group fields, accordions, etc - - if (!fields.length) { - fields = acf.getFields({ - key: key, - parent: field.$el.parent(), - suppressFilters: true - }); - } // return - - - if (fields.length) { - return fields[0]; - } - - return false; - }; - - acf.Field.prototype.getField = function (key) { - // get sibling field - var field = getSiblingField(this, key); // return early - - if (field) { - return field; - } // move up through each parent and try again - - - var parents = this.parents(); - - for (var i = 0; i < parents.length; i++) { - // get sibling field - field = getSiblingField(parents[i], key); // return early - - if (field) { - return field; - } - } // return - - - return false; - }; - /** - * acf.Field.prototype.getConditions - * - * Returns the field's conditions instance - * - * @date 1/2/18 - * @since 5.6.5 - * - * @param type $var Description. Default. - * @return type Description. - */ - - - acf.Field.prototype.getConditions = function () { - // instantiate - if (!this.conditions) { - this.conditions = new Conditions(this); - } // return - - - return this.conditions; - }; - /** - * Conditions - * - * description - * - * @date 1/2/18 - * @since 5.6.5 - * - * @param type $var Description. Default. - * @return type Description. - */ - - - var timeout = false; - var Conditions = acf.Model.extend({ - id: 'Conditions', - data: { - field: false, - // The field with "data-conditions" (target). - timeStamp: false, - // Reference used during "change" event. - groups: [] // The groups of condition instances. - - }, - setup: function (field) { - // data - this.data.field = field; // vars - - var conditions = field.get('conditions'); // detect groups - - if (conditions instanceof Array) { - // detect groups - if (conditions[0] instanceof Array) { - // loop - conditions.map(function (rules, i) { - this.addRules(rules, i); - }, this); // detect rules - } else { - this.addRules(conditions); - } // detect rule - - } else { - this.addRule(conditions); - } - }, - change: function (e) { - // this function may be triggered multiple times per event due to multiple condition classes - // compare timestamp to allow only 1 trigger per event - if (this.get('timeStamp') === e.timeStamp) { - return false; - } else { - this.set('timeStamp', e.timeStamp, true); - } // render condition and store result - - - var changed = this.render(); - }, - render: function () { - return this.calculate() ? this.show() : this.hide(); - }, - show: function () { - return this.get('field').showEnable(this.cid, CONTEXT); - }, - hide: function () { - return this.get('field').hideDisable(this.cid, CONTEXT); - }, - calculate: function () { - // vars - var pass = false; // loop - - this.getGroups().map(function (group) { - // igrnore this group if another group passed - if (pass) return; // find passed - - var passed = group.filter(function (condition) { - return condition.calculate(); - }); // if all conditions passed, update the global var - - if (passed.length == group.length) { - pass = true; - } - }); - return pass; - }, - hasGroups: function () { - return this.data.groups != null; - }, - getGroups: function () { - return this.data.groups; - }, - addGroup: function () { - var group = []; - this.data.groups.push(group); - return group; - }, - hasGroup: function (i) { - return this.data.groups[i] != null; - }, - getGroup: function (i) { - return this.data.groups[i]; - }, - removeGroup: function (i) { - this.data.groups[i].delete; - return this; - }, - addRules: function (rules, group) { - rules.map(function (rule) { - this.addRule(rule, group); - }, this); - }, - addRule: function (rule, group) { - // defaults - group = group || 0; // vars - - var groupArray; // get group - - if (this.hasGroup(group)) { - groupArray = this.getGroup(group); - } else { - groupArray = this.addGroup(); - } // instantiate - - - var condition = acf.newCondition(rule, this); // bail ealry if condition failed (field did not exist) - - if (!condition) { - return false; - } // add rule - - - groupArray.push(condition); - }, - hasRule: function () {}, - getRule: function (rule, group) { - // defaults - rule = rule || 0; - group = group || 0; - return this.data.groups[group][rule]; - }, - removeRule: function () {} - }); + // loop + conditions.map(function(rules, i){ + this.addRules( rules, i ); + }, this); + + // detect rules + } else { + this.addRules( conditions ); + } + + // detect rule + } else { + this.addRule( conditions ); + } + }, + + change: function( e ){ + + // this function may be triggered multiple times per event due to multiple condition classes + // compare timestamp to allow only 1 trigger per event + if( this.get('timeStamp') === e.timeStamp ) { + return false; + } else { + this.set('timeStamp', e.timeStamp, true); + } + + // render condition and store result + var changed = this.render(); + }, + + render: function(){ + return this.calculate() ? this.show() : this.hide(); + }, + + show: function(){ + return this.get('field').showEnable(this.cid, CONTEXT); + }, + + hide: function(){ + return this.get('field').hideDisable(this.cid, CONTEXT); + }, + + calculate: function(){ + + // vars + var pass = false; + + // loop + this.getGroups().map(function( group ){ + + // igrnore this group if another group passed + if( pass ) return; + + // find passed + var passed = group.filter(function(condition){ + return condition.calculate(); + }); + + // if all conditions passed, update the global var + if( passed.length == group.length ) { + pass = true; + } + }); + + return pass; + }, + + hasGroups: function(){ + return this.data.groups != null; + }, + + getGroups: function(){ + return this.data.groups; + }, + + addGroup: function(){ + var group = []; + this.data.groups.push( group ); + return group; + }, + + hasGroup: function( i ){ + return this.data.groups[i] != null; + }, + + getGroup: function( i ){ + return this.data.groups[i]; + }, + + removeGroup: function( i ){ + this.data.groups[i].delete; + return this; + }, + + addRules: function( rules, group ){ + rules.map(function( rule ){ + this.addRule( rule, group ); + }, this); + }, + + addRule: function( rule, group ){ + + // defaults + group = group || 0; + + // vars + var groupArray; + + // get group + if( this.hasGroup(group) ) { + groupArray = this.getGroup(group); + } else { + groupArray = this.addGroup(); + } + + // instantiate + var condition = acf.newCondition( rule, this ); + + // bail ealry if condition failed (field did not exist) + if( !condition ) { + return false; + } + + // add rule + groupArray.push(condition); + }, + + hasRule: function(){ + + }, + + getRule: function( rule, group ){ + + // defaults + rule = rule || 0; + group = group || 0; + + return this.data.groups[ group ][ rule ]; + }, + + removeRule: function(){ + + } + }); + })(jQuery); - -(function ($, undefined) { - var __ = acf.__; - - var parseString = function (val) { - return val ? '' + val : ''; - }; - - var isEqualTo = function (v1, v2) { - return parseString(v1).toLowerCase() === parseString(v2).toLowerCase(); - }; - - var isEqualToNumber = function (v1, v2) { - return parseFloat(v1) === parseFloat(v2); - }; - - var isGreaterThan = function (v1, v2) { - return parseFloat(v1) > parseFloat(v2); - }; - - var isLessThan = function (v1, v2) { - return parseFloat(v1) < parseFloat(v2); - }; - - var inArray = function (v1, array) { - // cast all values as string - array = array.map(function (v2) { - return parseString(v2); - }); - return array.indexOf(v1) > -1; - }; - - var containsString = function (haystack, needle) { - return parseString(haystack).indexOf(parseString(needle)) > -1; - }; - - var matchesPattern = function (v1, pattern) { - var regexp = new RegExp(parseString(pattern), 'gi'); - return parseString(v1).match(regexp); - }; - /** - * hasValue - * - * description - * - * @date 1/2/18 - * @since 5.6.5 - * - * @param void - * @return void - */ - - - var HasValue = acf.Condition.extend({ - type: 'hasValue', - operator: '!=empty', - label: __('Has any value'), - fieldTypes: ['text', 'textarea', 'number', 'range', 'email', 'url', 'password', 'image', 'file', 'wysiwyg', 'oembed', 'select', 'checkbox', 'radio', 'button_group', 'link', 'post_object', 'page_link', 'relationship', 'taxonomy', 'user', 'google_map', 'date_picker', 'date_time_picker', 'time_picker', 'color_picker'], - match: function (rule, field) { - return field.val() ? true : false; - }, - choices: function (fieldObject) { - return ''; - } - }); - acf.registerConditionType(HasValue); - /** - * hasValue - * - * description - * - * @date 1/2/18 - * @since 5.6.5 - * - * @param void - * @return void - */ - - var HasNoValue = HasValue.extend({ - type: 'hasNoValue', - operator: '==empty', - label: __('Has no value'), - match: function (rule, field) { - return !HasValue.prototype.match.apply(this, arguments); - } - }); - acf.registerConditionType(HasNoValue); - /** - * EqualTo - * - * description - * - * @date 1/2/18 - * @since 5.6.5 - * - * @param void - * @return void - */ - - var EqualTo = acf.Condition.extend({ - type: 'equalTo', - operator: '==', - label: __('Value is equal to'), - fieldTypes: ['text', 'textarea', 'number', 'range', 'email', 'url', 'password'], - match: function (rule, field) { - if (acf.isNumeric(rule.value)) { - return isEqualToNumber(rule.value, field.val()); - } else { - return isEqualTo(rule.value, field.val()); - } - }, - choices: function (fieldObject) { - return ''; - } - }); - acf.registerConditionType(EqualTo); - /** - * NotEqualTo - * - * description - * - * @date 1/2/18 - * @since 5.6.5 - * - * @param void - * @return void - */ - - var NotEqualTo = EqualTo.extend({ - type: 'notEqualTo', - operator: '!=', - label: __('Value is not equal to'), - match: function (rule, field) { - return !EqualTo.prototype.match.apply(this, arguments); - } - }); - acf.registerConditionType(NotEqualTo); - /** - * PatternMatch - * - * description - * - * @date 1/2/18 - * @since 5.6.5 - * - * @param void - * @return void - */ - - var PatternMatch = acf.Condition.extend({ - type: 'patternMatch', - operator: '==pattern', - label: __('Value matches pattern'), - fieldTypes: ['text', 'textarea', 'email', 'url', 'password', 'wysiwyg'], - match: function (rule, field) { - return matchesPattern(field.val(), rule.value); - }, - choices: function (fieldObject) { - return ''; - } - }); - acf.registerConditionType(PatternMatch); - /** - * Contains - * - * description - * - * @date 1/2/18 - * @since 5.6.5 - * - * @param void - * @return void - */ - - var Contains = acf.Condition.extend({ - type: 'contains', - operator: '==contains', - label: __('Value contains'), - fieldTypes: ['text', 'textarea', 'number', 'email', 'url', 'password', 'wysiwyg', 'oembed', 'select'], - match: function (rule, field) { - return containsString(field.val(), rule.value); - }, - choices: function (fieldObject) { - return ''; - } - }); - acf.registerConditionType(Contains); - /** - * TrueFalseEqualTo - * - * description - * - * @date 1/2/18 - * @since 5.6.5 - * - * @param void - * @return void - */ - - var TrueFalseEqualTo = EqualTo.extend({ - type: 'trueFalseEqualTo', - choiceType: 'select', - fieldTypes: ['true_false'], - choices: function (field) { - return [{ - id: 1, - text: __('Checked') - }]; - } - }); - acf.registerConditionType(TrueFalseEqualTo); - /** - * TrueFalseNotEqualTo - * - * description - * - * @date 1/2/18 - * @since 5.6.5 - * - * @param void - * @return void - */ - - var TrueFalseNotEqualTo = NotEqualTo.extend({ - type: 'trueFalseNotEqualTo', - choiceType: 'select', - fieldTypes: ['true_false'], - choices: function (field) { - return [{ - id: 1, - text: __('Checked') - }]; - } - }); - acf.registerConditionType(TrueFalseNotEqualTo); - /** - * SelectEqualTo - * - * description - * - * @date 1/2/18 - * @since 5.6.5 - * - * @param void - * @return void - */ - - var SelectEqualTo = acf.Condition.extend({ - type: 'selectEqualTo', - operator: '==', - label: __('Value is equal to'), - fieldTypes: ['select', 'checkbox', 'radio', 'button_group'], - match: function (rule, field) { - var val = field.val(); - - if (val instanceof Array) { - return inArray(rule.value, val); - } else { - return isEqualTo(rule.value, val); - } - }, - choices: function (fieldObject) { - // vars - var choices = []; - var lines = fieldObject.$setting('choices textarea').val().split("\n"); // allow null - - if (fieldObject.$input('allow_null').prop('checked')) { - choices.push({ - id: '', - text: __('Null') - }); - } // loop - - - lines.map(function (line) { - // split - line = line.split(':'); // default label to value - - line[1] = line[1] || line[0]; // append - - choices.push({ - id: line[0].trim(), - text: line[1].trim() - }); - }); // return - - return choices; - } - }); - acf.registerConditionType(SelectEqualTo); - /** - * SelectNotEqualTo - * - * description - * - * @date 1/2/18 - * @since 5.6.5 - * - * @param void - * @return void - */ - - var SelectNotEqualTo = SelectEqualTo.extend({ - type: 'selectNotEqualTo', - operator: '!=', - label: __('Value is not equal to'), - match: function (rule, field) { - return !SelectEqualTo.prototype.match.apply(this, arguments); - } - }); - acf.registerConditionType(SelectNotEqualTo); - /** - * GreaterThan - * - * description - * - * @date 1/2/18 - * @since 5.6.5 - * - * @param void - * @return void - */ - - var GreaterThan = acf.Condition.extend({ - type: 'greaterThan', - operator: '>', - label: __('Value is greater than'), - fieldTypes: ['number', 'range'], - match: function (rule, field) { - var val = field.val(); - - if (val instanceof Array) { - val = val.length; - } - - return isGreaterThan(val, rule.value); - }, - choices: function (fieldObject) { - return ''; - } - }); - acf.registerConditionType(GreaterThan); - /** - * LessThan - * - * description - * - * @date 1/2/18 - * @since 5.6.5 - * - * @param void - * @return void - */ - - var LessThan = GreaterThan.extend({ - type: 'lessThan', - operator: '<', - label: __('Value is less than'), - match: function (rule, field) { - var val = field.val(); - - if (val instanceof Array) { - val = val.length; - } - - return isLessThan(val, rule.value); - }, - choices: function (fieldObject) { - return ''; - } - }); - acf.registerConditionType(LessThan); - /** - * SelectedGreaterThan - * - * description - * - * @date 1/2/18 - * @since 5.6.5 - * - * @param void - * @return void - */ - - var SelectionGreaterThan = GreaterThan.extend({ - type: 'selectionGreaterThan', - label: __('Selection is greater than'), - fieldTypes: ['checkbox', 'select', 'post_object', 'page_link', 'relationship', 'taxonomy', 'user'] - }); - acf.registerConditionType(SelectionGreaterThan); - /** - * SelectedGreaterThan - * - * description - * - * @date 1/2/18 - * @since 5.6.5 - * - * @param void - * @return void - */ - - var SelectionLessThan = LessThan.extend({ - type: 'selectionLessThan', - label: __('Selection is less than'), - fieldTypes: ['checkbox', 'select', 'post_object', 'page_link', 'relationship', 'taxonomy', 'user'] - }); - acf.registerConditionType(SelectionLessThan); +(function($, undefined){ + + var __ = acf.__; + + var parseString = function( val ){ + return val ? '' + val : ''; + }; + + var isEqualTo = function( v1, v2 ){ + return ( parseString(v1).toLowerCase() === parseString(v2).toLowerCase() ); + }; + + var isEqualToNumber = function( v1, v2 ){ + return ( parseFloat(v1) === parseFloat(v2) ); + }; + + var isGreaterThan = function( v1, v2 ){ + return ( parseFloat(v1) > parseFloat(v2) ); + }; + + var isLessThan = function( v1, v2 ){ + return ( parseFloat(v1) < parseFloat(v2) ); + }; + + var inArray = function( v1, array ){ + + // cast all values as string + array = array.map(function(v2){ + return parseString(v2); + }); + + return (array.indexOf( v1 ) > -1); + } + + var containsString = function( haystack, needle ){ + return ( parseString(haystack).indexOf( parseString(needle) ) > -1 ); + }; + + var matchesPattern = function( v1, pattern ){ + var regexp = new RegExp(parseString(pattern), 'gi'); + return parseString(v1).match( regexp ); + }; + + /** + * hasValue + * + * description + * + * @date 1/2/18 + * @since 5.6.5 + * + * @param void + * @return void + */ + + var HasValue = acf.Condition.extend({ + type: 'hasValue', + operator: '!=empty', + label: __('Has any value'), + fieldTypes: [ 'text', 'textarea', 'number', 'range', 'email', 'url', 'password', 'image', 'file', 'wysiwyg', 'oembed', 'select', 'checkbox', 'radio', 'button_group', 'link', 'post_object', 'page_link', 'relationship', 'taxonomy', 'user', 'google_map', 'date_picker', 'date_time_picker', 'time_picker', 'color_picker' ], + match: function( rule, field ){ + return (field.val() ? true : false); + }, + choices: function( fieldObject ){ + return ''; + } + }); + + acf.registerConditionType( HasValue ); + + /** + * hasValue + * + * description + * + * @date 1/2/18 + * @since 5.6.5 + * + * @param void + * @return void + */ + + var HasNoValue = HasValue.extend({ + type: 'hasNoValue', + operator: '==empty', + label: __('Has no value'), + match: function( rule, field ){ + return !HasValue.prototype.match.apply(this, arguments); + } + }); + + acf.registerConditionType( HasNoValue ); + + + + /** + * EqualTo + * + * description + * + * @date 1/2/18 + * @since 5.6.5 + * + * @param void + * @return void + */ + + var EqualTo = acf.Condition.extend({ + type: 'equalTo', + operator: '==', + label: __('Value is equal to'), + fieldTypes: [ 'text', 'textarea', 'number', 'range', 'email', 'url', 'password' ], + match: function( rule, field ){ + if( acf.isNumeric(rule.value) ) { + return isEqualToNumber( rule.value, field.val() ); + } else { + return isEqualTo( rule.value, field.val() ); + } + }, + choices: function( fieldObject ){ + return ''; + } + }); + + acf.registerConditionType( EqualTo ); + + /** + * NotEqualTo + * + * description + * + * @date 1/2/18 + * @since 5.6.5 + * + * @param void + * @return void + */ + + var NotEqualTo = EqualTo.extend({ + type: 'notEqualTo', + operator: '!=', + label: __('Value is not equal to'), + match: function( rule, field ){ + return !EqualTo.prototype.match.apply(this, arguments); + } + }); + + acf.registerConditionType( NotEqualTo ); + + /** + * PatternMatch + * + * description + * + * @date 1/2/18 + * @since 5.6.5 + * + * @param void + * @return void + */ + + var PatternMatch = acf.Condition.extend({ + type: 'patternMatch', + operator: '==pattern', + label: __('Value matches pattern'), + fieldTypes: [ 'text', 'textarea', 'email', 'url', 'password', 'wysiwyg' ], + match: function( rule, field ){ + return matchesPattern( field.val(), rule.value ); + }, + choices: function( fieldObject ){ + return ''; + } + }); + + acf.registerConditionType( PatternMatch ); + + /** + * Contains + * + * description + * + * @date 1/2/18 + * @since 5.6.5 + * + * @param void + * @return void + */ + + var Contains = acf.Condition.extend({ + type: 'contains', + operator: '==contains', + label: __('Value contains'), + fieldTypes: [ 'text', 'textarea', 'number', 'email', 'url', 'password', 'wysiwyg', 'oembed', 'select' ], + match: function( rule, field ){ + return containsString( field.val(), rule.value ); + }, + choices: function( fieldObject ){ + return ''; + } + }); + + acf.registerConditionType( Contains ); + + /** + * TrueFalseEqualTo + * + * description + * + * @date 1/2/18 + * @since 5.6.5 + * + * @param void + * @return void + */ + + var TrueFalseEqualTo = EqualTo.extend({ + type: 'trueFalseEqualTo', + choiceType: 'select', + fieldTypes: [ 'true_false' ], + choices: function( field ){ + return [ + { + id: 1, + text: __('Checked') + } + ]; + }, + }); + + acf.registerConditionType( TrueFalseEqualTo ); + + /** + * TrueFalseNotEqualTo + * + * description + * + * @date 1/2/18 + * @since 5.6.5 + * + * @param void + * @return void + */ + + var TrueFalseNotEqualTo = NotEqualTo.extend({ + type: 'trueFalseNotEqualTo', + choiceType: 'select', + fieldTypes: [ 'true_false' ], + choices: function( field ){ + return [ + { + id: 1, + text: __('Checked') + } + ]; + }, + }); + + acf.registerConditionType( TrueFalseNotEqualTo ); + + /** + * SelectEqualTo + * + * description + * + * @date 1/2/18 + * @since 5.6.5 + * + * @param void + * @return void + */ + + var SelectEqualTo = acf.Condition.extend({ + type: 'selectEqualTo', + operator: '==', + label: __('Value is equal to'), + fieldTypes: [ 'select', 'checkbox', 'radio', 'button_group' ], + match: function( rule, field ){ + var val = field.val(); + if( val instanceof Array ) { + return inArray( rule.value, val ); + } else { + return isEqualTo( rule.value, val ); + } + }, + choices: function( fieldObject ){ + + // vars + var choices = []; + var lines = fieldObject.$setting('choices textarea').val().split("\n"); + + // allow null + if( fieldObject.$input('allow_null').prop('checked') ) { + choices.push({ + id: '', + text: __('Null') + }); + } + + // loop + lines.map(function( line ){ + + // split + line = line.split(':'); + + // default label to value + line[1] = line[1] || line[0]; + + // append + choices.push({ + id: line[0].trim(), + text: line[1].trim() + }); + }); + + // return + return choices; + }, + }); + + acf.registerConditionType( SelectEqualTo ); + + /** + * SelectNotEqualTo + * + * description + * + * @date 1/2/18 + * @since 5.6.5 + * + * @param void + * @return void + */ + + var SelectNotEqualTo = SelectEqualTo.extend({ + type: 'selectNotEqualTo', + operator: '!=', + label: __('Value is not equal to'), + match: function( rule, field ){ + return !SelectEqualTo.prototype.match.apply(this, arguments); + } + }); + + acf.registerConditionType( SelectNotEqualTo ); + + /** + * GreaterThan + * + * description + * + * @date 1/2/18 + * @since 5.6.5 + * + * @param void + * @return void + */ + + var GreaterThan = acf.Condition.extend({ + type: 'greaterThan', + operator: '>', + label: __('Value is greater than'), + fieldTypes: [ 'number', 'range' ], + match: function( rule, field ){ + var val = field.val(); + if( val instanceof Array ) { + val = val.length; + } + return isGreaterThan( val, rule.value ); + }, + choices: function( fieldObject ){ + return ''; + } + }); + + acf.registerConditionType( GreaterThan ); + + + /** + * LessThan + * + * description + * + * @date 1/2/18 + * @since 5.6.5 + * + * @param void + * @return void + */ + + var LessThan = GreaterThan.extend({ + type: 'lessThan', + operator: '<', + label: __('Value is less than'), + match: function( rule, field ){ + var val = field.val(); + if( val instanceof Array ) { + val = val.length; + } + return isLessThan( val, rule.value ); + }, + choices: function( fieldObject ){ + return ''; + } + }); + + acf.registerConditionType( LessThan ); + + /** + * SelectedGreaterThan + * + * description + * + * @date 1/2/18 + * @since 5.6.5 + * + * @param void + * @return void + */ + + var SelectionGreaterThan = GreaterThan.extend({ + type: 'selectionGreaterThan', + label: __('Selection is greater than'), + fieldTypes: [ 'checkbox', 'select', 'post_object', 'page_link', 'relationship', 'taxonomy', 'user' ], + }); + + acf.registerConditionType( SelectionGreaterThan ); + + /** + * SelectedGreaterThan + * + * description + * + * @date 1/2/18 + * @since 5.6.5 + * + * @param void + * @return void + */ + + var SelectionLessThan = LessThan.extend({ + type: 'selectionLessThan', + label: __('Selection is less than'), + fieldTypes: [ 'checkbox', 'select', 'post_object', 'page_link', 'relationship', 'taxonomy', 'user' ], + }); + + acf.registerConditionType( SelectionLessThan ); + })(jQuery); - -(function ($, undefined) { - acf.unload = new acf.Model({ - wait: 'load', - active: true, - changed: false, - actions: { - 'validation_failure': 'startListening', - 'validation_success': 'stopListening' - }, - events: { - 'change form .acf-field': 'startListening', - 'submit form': 'stopListening' - }, - enable: function () { - this.active = true; - }, - disable: function () { - this.active = false; - }, - reset: function () { - this.stopListening(); - }, - startListening: function () { - // bail ealry if already changed, not active - if (this.changed || !this.active) { - return; - } // update - - - this.changed = true; // add event - - $(window).on('beforeunload', this.onUnload); - }, - stopListening: function () { - // update - this.changed = false; // remove event - - $(window).off('beforeunload', this.onUnload); - }, - onUnload: function () { - return acf.__('The changes you made will be lost if you navigate away from this page'); - } - }); +(function($, undefined){ + + acf.unload = new acf.Model({ + + wait: 'load', + active: true, + changed: false, + + actions: { + 'validation_failure': 'startListening', + 'validation_success': 'stopListening' + }, + + events: { + 'change form .acf-field': 'startListening', + 'submit form': 'stopListening' + }, + + enable: function(){ + this.active = true; + }, + + disable: function(){ + this.active = false; + }, + + reset: function(){ + this.stopListening(); + }, + + startListening: function(){ + + // bail ealry if already changed, not active + if( this.changed || !this.active ) { + return; + } + + // update + this.changed = true; + + // add event + $(window).on('beforeunload', this.onUnload); + + }, + + stopListening: function(){ + + // update + this.changed = false; + + // remove event + $(window).off('beforeunload', this.onUnload); + + }, + + onUnload: function(){ + return acf.__('The changes you made will be lost if you navigate away from this page'); + } + + }); + })(jQuery); +(function($, undefined){ + + /** + * postboxManager + * + * Manages postboxes on the screen. + * + * @date 25/5/19 + * @since 5.8.1 + * + * @param void + * @return void + */ + var postboxManager = new acf.Model({ + wait: 'prepare', + priority: 1, + initialize: function(){ + (acf.get('postboxes') || []).map( acf.newPostbox ); + }, + }); + + /** + * acf.getPostbox + * + * Returns a postbox instance. + * + * @date 23/9/18 + * @since 5.7.7 + * + * @param mixed $el Either a jQuery element or the postbox id. + * @return object + */ + acf.getPostbox = function( $el ){ + + // allow string parameter + if( typeof arguments[0] == 'string' ) { + $el = $('#' + arguments[0]); + } + + // return instance + return acf.getInstance( $el ); + }; + + /** + * acf.getPostboxes + * + * Returns an array of postbox instances. + * + * @date 23/9/18 + * @since 5.7.7 + * + * @param void + * @return array + */ + acf.getPostboxes = function(){ + return acf.getInstances( $('.acf-postbox') ); + }; + + /** + * acf.newPostbox + * + * Returns a new postbox instance for the given props. + * + * @date 20/9/18 + * @since 5.7.6 + * + * @param object props The postbox properties. + * @return object + */ + acf.newPostbox = function( props ){ + return new acf.models.Postbox( props ); + }; + + /** + * acf.models.Postbox + * + * The postbox model. + * + * @date 20/9/18 + * @since 5.7.6 + * + * @param void + * @return void + */ + acf.models.Postbox = acf.Model.extend({ + + data: { + id: '', + key: '', + style: 'default', + label: 'top', + edit: '' + }, + + setup: function( props ){ + + // compatibilty + if( props.editLink ) { + props.edit = props.editLink; + } + + // extend data + $.extend(this.data, props); + + // set $el + this.$el = this.$postbox(); + }, + + $postbox: function(){ + return $('#' + this.get('id')); + }, + + $hide: function(){ + return $('#' + this.get('id') + '-hide'); + }, + + $hideLabel: function(){ + return this.$hide().parent(); + }, + + $hndle: function(){ + return this.$('> .hndle'); + }, -(function ($, undefined) { - /** - * postboxManager - * - * Manages postboxes on the screen. - * - * @date 25/5/19 - * @since 5.8.1 - * - * @param void - * @return void - */ - var postboxManager = new acf.Model({ - wait: 'prepare', - priority: 1, - initialize: function () { - (acf.get('postboxes') || []).map(acf.newPostbox); - } - }); - /** - * acf.getPostbox - * - * Returns a postbox instance. - * - * @date 23/9/18 - * @since 5.7.7 - * - * @param mixed $el Either a jQuery element or the postbox id. - * @return object - */ - - acf.getPostbox = function ($el) { - // allow string parameter - if (typeof arguments[0] == 'string') { - $el = $('#' + arguments[0]); - } // return instance - - - return acf.getInstance($el); - }; - /** - * acf.getPostboxes - * - * Returns an array of postbox instances. - * - * @date 23/9/18 - * @since 5.7.7 - * - * @param void - * @return array - */ - - - acf.getPostboxes = function () { - return acf.getInstances($('.acf-postbox')); - }; - /** - * acf.newPostbox - * - * Returns a new postbox instance for the given props. - * - * @date 20/9/18 - * @since 5.7.6 - * - * @param object props The postbox properties. - * @return object - */ - - - acf.newPostbox = function (props) { - return new acf.models.Postbox(props); - }; - /** - * acf.models.Postbox - * - * The postbox model. - * - * @date 20/9/18 - * @since 5.7.6 - * - * @param void - * @return void - */ - - - acf.models.Postbox = acf.Model.extend({ - data: { - id: '', - key: '', - style: 'default', - label: 'top', - edit: '' - }, - setup: function (props) { - // compatibilty - if (props.editLink) { - props.edit = props.editLink; - } // extend data - - - $.extend(this.data, props); // set $el - - this.$el = this.$postbox(); - }, - $postbox: function () { - return $('#' + this.get('id')); - }, - $hide: function () { - return $('#' + this.get('id') + '-hide'); - }, - $hideLabel: function () { - return this.$hide().parent(); - }, - $hndle: function () { - return this.$('> .hndle'); - }, - $handleActions: function () { - return this.$('> .postbox-header .handle-actions'); - }, - $inside: function () { - return this.$('> .inside'); - }, - isVisible: function () { - return this.$el.hasClass('acf-hidden'); - }, - initialize: function () { - // Add default class. - this.$el.addClass('acf-postbox'); // Remove 'hide-if-js class. - // This class is added by WP to postboxes that are hidden via the "Screen Options" tab. - - this.$el.removeClass('hide-if-js'); // Add field group style class (ignore in block editor). - - if (acf.get('editor') !== 'block') { - var style = this.get('style'); - - if (style !== 'default') { - this.$el.addClass(style); - } - } // Add .inside class. - - - this.$inside().addClass('acf-fields').addClass('-' + this.get('label')); // Append edit link. - - var edit = this.get('edit'); - - if (edit) { - var html = ''; - var $handleActions = this.$handleActions(); - - if ($handleActions.length) { - $handleActions.prepend(html); - } else { - this.$hndle().append(html); - } - } // Show postbox. - - - this.show(); - }, - show: function () { - // Show label. - this.$hideLabel().show(); // toggle on checkbox - - this.$hide().prop('checked', true); // Show postbox - - this.$el.show().removeClass('acf-hidden'); // Do action. - - acf.doAction('show_postbox', this); - }, - enable: function () { - acf.enable(this.$el, 'postbox'); - }, - showEnable: function () { - this.enable(); - this.show(); - }, - hide: function () { - // Hide label. - this.$hideLabel().hide(); // Hide postbox - - this.$el.hide().addClass('acf-hidden'); // Do action. - - acf.doAction('hide_postbox', this); - }, - disable: function () { - acf.disable(this.$el, 'postbox'); - }, - hideDisable: function () { - this.disable(); - this.hide(); - }, - html: function (html) { - // Update HTML. - this.$inside().html(html); // Do action. - - acf.doAction('append', this.$el); - } - }); + $handleActions: function(){ + return this.$('> .postbox-header .handle-actions'); + }, + + $inside: function(){ + return this.$('> .inside'); + }, + + isVisible: function(){ + return this.$el.hasClass('acf-hidden'); + }, + + initialize: function(){ + + // Add default class. + this.$el.addClass('acf-postbox'); + + // Remove 'hide-if-js class. + // This class is added by WP to postboxes that are hidden via the "Screen Options" tab. + this.$el.removeClass('hide-if-js'); + + // Add field group style class (ignore in block editor). + if( acf.get('editor') !== 'block' ) { + var style = this.get('style'); + if( style !== 'default' ) { + this.$el.addClass( style ); + } + } + + // Add .inside class. + this.$inside().addClass('acf-fields').addClass('-' + this.get('label')); + + // Append edit link. + var edit = this.get('edit'); + if( edit ) { + var html = ''; + var $handleActions = this.$handleActions(); + if( $handleActions.length ) { + $handleActions.prepend( html ); + } else { + this.$hndle().append( html ); + } + } + + // Show postbox. + this.show(); + }, + + show: function(){ + + // Show label. + this.$hideLabel().show(); + + // toggle on checkbox + this.$hide().prop('checked', true); + + // Show postbox + this.$el.show().removeClass('acf-hidden'); + + // Do action. + acf.doAction('show_postbox', this); + }, + + enable: function(){ + acf.enable( this.$el, 'postbox' ); + }, + + showEnable: function(){ + this.enable(); + this.show(); + }, + + hide: function(){ + + // Hide label. + this.$hideLabel().hide(); + + // Hide postbox + this.$el.hide().addClass('acf-hidden'); + + // Do action. + acf.doAction('hide_postbox', this); + }, + + disable: function(){ + acf.disable( this.$el, 'postbox' ); + }, + + hideDisable: function(){ + this.disable(); + this.hide(); + }, + + html: function( html ){ + + // Update HTML. + this.$inside().html( html ); + + // Do action. + acf.doAction('append', this.$el); + } + }); + })(jQuery); +(function($, undefined){ + + /** + * acf.newMediaPopup + * + * description + * + * @date 10/1/18 + * @since 5.6.5 + * + * @param type $var Description. Default. + * @return type Description. + */ + + acf.newMediaPopup = function( args ){ + + // args + var popup = null; + var args = acf.parseArgs(args, { + mode: 'select', // 'select', 'edit' + title: '', // 'Upload Image' + button: '', // 'Select Image' + type: '', // 'image', '' + field: false, // field instance + allowedTypes: '', // '.jpg, .png, etc' + library: 'all', // 'all', 'uploadedTo' + multiple: false, // false, true, 'add' + attachment: 0, // the attachment to edit + autoOpen: true, // open the popup automatically + open: function(){}, // callback after close + select: function(){}, // callback after select + close: function(){} // callback after close + }); + + // initialize + if( args.mode == 'edit' ) { + popup = new acf.models.EditMediaPopup( args ); + } else { + popup = new acf.models.SelectMediaPopup( args ); + } + + // open popup (allow frame customization before opening) + if( args.autoOpen ) { + setTimeout(function(){ + popup.open(); + }, 1); + } + + // action + acf.doAction('new_media_popup', popup); + + // return + return popup; + }; + + + /** + * getPostID + * + * description + * + * @date 10/1/18 + * @since 5.6.5 + * + * @param type $var Description. Default. + * @return type Description. + */ + + var getPostID = function() { + var postID = acf.get('post_id'); + return acf.isNumeric(postID) ? postID : 0; + } + + + /** + * acf.getMimeTypes + * + * description + * + * @date 11/1/18 + * @since 5.6.5 + * + * @param type $var Description. Default. + * @return type Description. + */ + + acf.getMimeTypes = function(){ + return this.get('mimeTypes'); + }; + + acf.getMimeType = function( name ){ + + // vars + var allTypes = acf.getMimeTypes(); + + // search + if( allTypes[name] !== undefined ) { + return allTypes[name]; + } + + // some types contain a mixed key such as "jpg|jpeg|jpe" + for( var key in allTypes ) { + if( key.indexOf(name) !== -1 ) { + return allTypes[key]; + } + } + + // return + return false; + }; + + + /** + * MediaPopup + * + * description + * + * @date 10/1/18 + * @since 5.6.5 + * + * @param type $var Description. Default. + * @return type Description. + */ + + var MediaPopup = acf.Model.extend({ + + id: 'MediaPopup', + data: {}, + defaults: {}, + frame: false, + + setup: function( props ){ + $.extend(this.data, props); + }, + + initialize: function(){ + + // vars + var options = this.getFrameOptions(); + + // add states + this.addFrameStates( options ); + + // create frame + var frame = wp.media( options ); + + // add args reference + frame.acf = this; + + // add events + this.addFrameEvents( frame, options ); + + // strore frame + this.frame = frame; + }, + + open: function(){ + this.frame.open(); + }, + + close: function(){ + this.frame.close(); + }, + + remove: function(){ + this.frame.detach(); + this.frame.remove(); + }, + + getFrameOptions: function(){ + + // vars + var options = { + title: this.get('title'), + multiple: this.get('multiple'), + library: {}, + states: [] + }; + + // type + if( this.get('type') ) { + options.library.type = this.get('type'); + } + + // type + if( this.get('library') === 'uploadedTo' ) { + options.library.uploadedTo = getPostID(); + } + + // attachment + if( this.get('attachment') ) { + options.library.post__in = [ this.get('attachment') ]; + } + + // button + if( this.get('button') ) { + options.button = { + text: this.get('button') + }; + } + + // return + return options; + }, + + addFrameStates: function( options ){ + + // create query + var Query = wp.media.query( options.library ); + + // add _acfuploader + // this is super wack! + // if you add _acfuploader to the options.library args, new uploads will not be added to the library view. + // this has been traced back to the wp.media.model.Query initialize function (which can't be overriden) + // Adding any custom args will cause the Attahcments to not observe the uploader queue + // To bypass this security issue, we add in the args AFTER the Query has been initialized + // options.library._acfuploader = settings.field; + if( this.get('field') && acf.isset(Query, 'mirroring', 'args') ) { + Query.mirroring.args._acfuploader = this.get('field'); + } + + // add states + options.states.push( + + // main state + new wp.media.controller.Library({ + library: Query, + multiple: this.get('multiple'), + title: this.get('title'), + priority: 20, + filterable: 'all', + editable: true, + allowLocalEdits: true + }) + + ); + + // edit image functionality (added in WP 3.9) + if( acf.isset(wp, 'media', 'controller', 'EditImage') ) { + options.states.push( new wp.media.controller.EditImage() ); + } + }, + + addFrameEvents: function( frame, options ){ + + // log all events + //frame.on('all', function( e ) { + // console.log( 'frame all: %o', e ); + //}); + + // add class + frame.on('open',function() { + this.$el.closest('.media-modal').addClass('acf-media-modal -' + this.acf.get('mode') ); + }, frame); + + // edit image view + // source: media-views.js:2410 editImageContent() + frame.on('content:render:edit-image', function(){ + + var image = this.state().get('image'); + var view = new wp.media.view.EditImage({ model: image, controller: this }).render(); + this.content.set( view ); + + // after creating the wrapper view, load the actual editor via an ajax call + view.loadEditor(); + + }, frame); + + // update toolbar button + //frame.on( 'toolbar:create:select', function( toolbar ) { + // toolbar.view = new wp.media.view.Toolbar.Select({ + // text: frame.options._button, + // controller: this + // }); + //}, frame ); + + // on select + frame.on('select', function() { + + // vars + var selection = frame.state().get('selection'); + + // if selecting images + if( selection ) { + + // loop + selection.each(function( attachment, i ){ + frame.acf.get('select').apply( frame.acf, [attachment, i] ); + }); + } + }); + + // on close + frame.on('close',function(){ + + // callback and remove + setTimeout(function(){ + frame.acf.get('close').apply( frame.acf ); + frame.acf.remove(); + }, 1); + }); + } + }); + + + /** + * acf.models.SelectMediaPopup + * + * description + * + * @date 10/1/18 + * @since 5.6.5 + * + * @param type $var Description. Default. + * @return type Description. + */ + + acf.models.SelectMediaPopup = MediaPopup.extend({ + id: 'SelectMediaPopup', + setup: function( props ){ + + // default button + if( !props.button ) { + props.button = acf._x('Select', 'verb'); + } + + // parent + MediaPopup.prototype.setup.apply(this, arguments); + }, + + addFrameEvents: function( frame, options ){ + + // plupload + // adds _acfuploader param to validate uploads + if( acf.isset(_wpPluploadSettings, 'defaults', 'multipart_params') ) { + + // add _acfuploader so that Uploader will inherit + _wpPluploadSettings.defaults.multipart_params._acfuploader = this.get('field'); + + // remove acf_field so future Uploaders won't inherit + frame.on('open', function(){ + delete _wpPluploadSettings.defaults.multipart_params._acfuploader; + }); + } + + // browse + frame.on('content:activate:browse', function(){ + + // vars + var toolbar = false; + + // populate above vars making sure to allow for failure + // perhaps toolbar does not exist because the frame open is Upload Files + try { + toolbar = frame.content.get().toolbar; + } catch(e) { + console.log(e); + return; + } + + // callback + frame.acf.customizeFilters.apply(frame.acf, [toolbar]); + }); + + // parent + MediaPopup.prototype.addFrameEvents.apply(this, arguments); + + }, + + customizeFilters: function( toolbar ){ + + // vars + var filters = toolbar.get('filters'); + + // image + if( this.get('type') == 'image' ) { + + // update all + filters.filters.all.text = acf.__('All images'); + + // remove some filters + delete filters.filters.audio; + delete filters.filters.video; + delete filters.filters.image; + + // update all filters to show images + $.each(filters.filters, function( i, filter ){ + filter.props.type = filter.props.type || 'image'; + }); + } + + // specific types + if( this.get('allowedTypes') ) { + + // convert ".jpg, .png" into ["jpg", "png"] + var allowedTypes = this.get('allowedTypes').split(' ').join('').split('.').join('').split(','); + + // loop + allowedTypes.map(function( name ){ + + // get type + var mimeType = acf.getMimeType( name ); + + // bail early if no type + if( !mimeType ) return; + + // create new filter + var newFilter = { + text: mimeType, + props: { + status: null, + type: mimeType, + uploadedTo: null, + orderby: 'date', + order: 'DESC' + }, + priority: 20 + }; + + // append + filters.filters[ mimeType ] = newFilter; + + }); + } + + + + // uploaded to post + if( this.get('library') === 'uploadedTo' ) { + + // vars + var uploadedTo = this.frame.options.library.uploadedTo; + + // remove some filters + delete filters.filters.unattached; + delete filters.filters.uploaded; + + // add uploadedTo to filters + $.each(filters.filters, function( i, filter ){ + filter.text += ' (' + acf.__('Uploaded to this post') + ')'; + filter.props.uploadedTo = uploadedTo; + }); + } + + // add _acfuploader to filters + var field = this.get('field'); + $.each(filters.filters, function( k, filter ){ + filter.props._acfuploader = field; + }); + + // add _acfuplaoder to search + var search = toolbar.get('search'); + search.model.attributes._acfuploader = field; + + // render (custom function added to prototype) + if( filters.renderFilters ) { + filters.renderFilters(); + } + } + }); + + + /** + * acf.models.EditMediaPopup + * + * description + * + * @date 10/1/18 + * @since 5.6.5 + * + * @param type $var Description. Default. + * @return type Description. + */ + + acf.models.EditMediaPopup = MediaPopup.extend({ + id: 'SelectMediaPopup', + setup: function( props ){ + + // default button + if( !props.button ) { + props.button = acf._x('Update', 'verb'); + } + + // parent + MediaPopup.prototype.setup.apply(this, arguments); + }, + + addFrameEvents: function( frame, options ){ + + // add class + frame.on('open',function() { + + // add class + this.$el.closest('.media-modal').addClass('acf-expanded'); + + // set to browse + if( this.content.mode() != 'browse' ) { + this.content.mode('browse'); + } + + // set selection + var state = this.state(); + var selection = state.get('selection'); + var attachment = wp.media.attachment( frame.acf.get('attachment') ); + selection.add( attachment ); + + }, frame); + + // parent + MediaPopup.prototype.addFrameEvents.apply(this, arguments); + + } + }); + + + /** + * customizePrototypes + * + * description + * + * @date 11/1/18 + * @since 5.6.5 + * + * @param type $var Description. Default. + * @return type Description. + */ + + var customizePrototypes = new acf.Model({ + id: 'customizePrototypes', + wait: 'ready', + + initialize: function(){ + + // bail early if no media views + if( !acf.isset(window, 'wp', 'media', 'view') ) { + return; + } + + // fix bug where CPT without "editor" does not set post.id setting which then prevents uploadedTo from working + var postID = getPostID(); + if( postID && acf.isset(wp, 'media', 'view', 'settings', 'post') ) { + wp.media.view.settings.post.id = postID; + } + + // customize + this.customizeAttachmentsButton(); + this.customizeAttachmentsRouter(); + this.customizeAttachmentFilters(); + this.customizeAttachmentCompat(); + this.customizeAttachmentLibrary(); + }, + + customizeAttachmentsButton: function(){ + + // validate + if( !acf.isset(wp, 'media', 'view', 'Button') ) { + return; + } + + // Extend + var Button = wp.media.view.Button; + wp.media.view.Button = Button.extend({ + + // Fix bug where "Select" button appears blank after editing an image. + // Do this by simplifying Button initialize function and avoid deleting this.options. + initialize: function() { + var options = _.defaults( this.options, this.defaults ); + this.model = new Backbone.Model( options ); + this.listenTo( this.model, 'change', this.render ); + } + }); + + }, + + customizeAttachmentsRouter: function(){ + + // validate + if( !acf.isset(wp, 'media', 'view', 'Router') ) { + return; + } + + // vars + var Parent = wp.media.view.Router; + + // extend + wp.media.view.Router = Parent.extend({ + + addExpand: function(){ + + // vars + var $a = $([ + '', + '' + acf.__('Expand Details') + '', + '' + acf.__('Collapse Details') + '', + '' + ].join('')); + + // add events + $a.on('click', function( e ){ + e.preventDefault(); + var $div = $(this).closest('.media-modal'); + if( $div.hasClass('acf-expanded') ) { + $div.removeClass('acf-expanded'); + } else { + $div.addClass('acf-expanded'); + } + }); + + // append + this.$el.append( $a ); + }, + + initialize: function(){ + + // initialize + Parent.prototype.initialize.apply( this, arguments ); + + // add buttons + this.addExpand(); + + // return + return this; + } + }); + }, + + customizeAttachmentFilters: function(){ + + // validate + if( !acf.isset(wp, 'media', 'view', 'AttachmentFilters', 'All') ) { + return; + } + + // vars + var Parent = wp.media.view.AttachmentFilters.All; + + // renderFilters + // copied from media-views.js:6939 + Parent.prototype.renderFilters = function(){ + + // Build `' ).val( value ).html( filter.text )[0], + priority: filter.priority || 50 + }; + }, this ).sortBy('priority').pluck('el').value() ); + + }; + }, + + customizeAttachmentCompat: function(){ + + // validate + if( !acf.isset(wp, 'media', 'view', 'AttachmentCompat') ) { + return; + } + + // vars + var AttachmentCompat = wp.media.view.AttachmentCompat; + var timeout = false; + + // extend + wp.media.view.AttachmentCompat = AttachmentCompat.extend({ + + render: function() { + + // WP bug + // When multiple media frames exist on the same page (WP content, WYSIWYG, image, file ), + // WP creates multiple instances of this AttachmentCompat view. + // Each instance will attempt to render when a new modal is created. + // Use a property to avoid this and only render once per instance. + if( this.rendered ) { + return this; + } + + // render HTML + AttachmentCompat.prototype.render.apply( this, arguments ); + + // when uploading, render is called twice. + // ignore first render by checking for #acf-form-data element + if( !this.$('#acf-form-data').length ) { + return this; + } + + // clear timeout + clearTimeout( timeout ); + + // setTimeout + timeout = setTimeout($.proxy(function(){ + this.rendered = true; + acf.doAction('append', this.$el); + }, this), 50); + + // return + return this; + }, + + save: function( event ) { + var data = {}; + + if ( event ) { + event.preventDefault(); + } + + //_.each( this.$el.serializeArray(), function( pair ) { + // data[ pair.name ] = pair.value; + //}); + + // Serialize data more thoroughly to allow chckbox inputs to save. + data = acf.serializeForAjax(this.$el); + + this.controller.trigger( 'attachment:compat:waiting', ['waiting'] ); + this.model.saveCompat( data ).always( _.bind( this.postSave, this ) ); + } + }); + + }, + + customizeAttachmentLibrary: function(){ + + // validate + if( !acf.isset(wp, 'media', 'view', 'Attachment', 'Library') ) { + return; + } + + // vars + var AttachmentLibrary = wp.media.view.Attachment.Library; + + // extend + wp.media.view.Attachment.Library = AttachmentLibrary.extend({ + + render: function() { + + // vars + var popup = acf.isget(this, 'controller', 'acf'); + var attributes = acf.isget(this, 'model', 'attributes'); + + // check vars exist to avoid errors + if( popup && attributes ) { + + // show errors + if( attributes.acf_errors ) { + this.$el.addClass('acf-disabled'); + } + + // disable selected + var selected = popup.get('selected'); + if( selected && selected.indexOf(attributes.id) > -1 ) { + this.$el.addClass('acf-selected'); + } + } + + // render + return AttachmentLibrary.prototype.render.apply( this, arguments ); + + }, + + + /* + * toggleSelection + * + * This function is called before an attachment is selected + * A good place to check for errors and prevent the 'select' function from being fired + * + * @type function + * @date 29/09/2016 + * @since 5.4.0 + * + * @param options (object) + * @return n/a + */ + + toggleSelection: function( options ) { + + // vars + // source: wp-includes/js/media-views.js:2880 + var collection = this.collection, + selection = this.options.selection, + model = this.model, + single = selection.single(); + + + // vars + var frame = this.controller; + var errors = acf.isget(this, 'model', 'attributes', 'acf_errors'); + var $sidebar = frame.$el.find('.media-frame-content .media-sidebar'); + + // remove previous error + $sidebar.children('.acf-selection-error').remove(); + + // show attachment details + $sidebar.children().removeClass('acf-hidden'); + + // add message + if( frame && errors ) { + + // vars + var filename = acf.isget(this, 'model', 'attributes', 'filename'); + + // hide attachment details + // Gallery field continues to show previously selected attachment... + $sidebar.children().addClass('acf-hidden'); + + // append message + $sidebar.prepend([ + '
            ', + '' + acf.__('Restricted') +'', + '' + filename + '', + '' + errors + '', + '
            ' + ].join('')); + + // reset selection (unselects all attachments) + selection.reset(); + + // set single (attachment displayed in sidebar) + selection.single( model ); + + // return and prevent 'select' form being fired + return; + + } + + // return + return AttachmentLibrary.prototype.toggleSelection.apply( this, arguments ); + } + }); + } + }); -(function ($, undefined) { - /** - * acf.newMediaPopup - * - * description - * - * @date 10/1/18 - * @since 5.6.5 - * - * @param type $var Description. Default. - * @return type Description. - */ - acf.newMediaPopup = function (args) { - // args - var popup = null; - var args = acf.parseArgs(args, { - mode: 'select', - // 'select', 'edit' - title: '', - // 'Upload Image' - button: '', - // 'Select Image' - type: '', - // 'image', '' - field: false, - // field instance - allowedTypes: '', - // '.jpg, .png, etc' - library: 'all', - // 'all', 'uploadedTo' - multiple: false, - // false, true, 'add' - attachment: 0, - // the attachment to edit - autoOpen: true, - // open the popup automatically - open: function () {}, - // callback after close - select: function () {}, - // callback after select - close: function () {} // callback after close - - }); // initialize - - if (args.mode == 'edit') { - popup = new acf.models.EditMediaPopup(args); - } else { - popup = new acf.models.SelectMediaPopup(args); - } // open popup (allow frame customization before opening) - - - if (args.autoOpen) { - setTimeout(function () { - popup.open(); - }, 1); - } // action - - - acf.doAction('new_media_popup', popup); // return - - return popup; - }; - /** - * getPostID - * - * description - * - * @date 10/1/18 - * @since 5.6.5 - * - * @param type $var Description. Default. - * @return type Description. - */ - - - var getPostID = function () { - var postID = acf.get('post_id'); - return acf.isNumeric(postID) ? postID : 0; - }; - /** - * acf.getMimeTypes - * - * description - * - * @date 11/1/18 - * @since 5.6.5 - * - * @param type $var Description. Default. - * @return type Description. - */ - - - acf.getMimeTypes = function () { - return this.get('mimeTypes'); - }; - - acf.getMimeType = function (name) { - // vars - var allTypes = acf.getMimeTypes(); // search - - if (allTypes[name] !== undefined) { - return allTypes[name]; - } // some types contain a mixed key such as "jpg|jpeg|jpe" - - - for (var key in allTypes) { - if (key.indexOf(name) !== -1) { - return allTypes[key]; - } - } // return - - - return false; - }; - /** - * MediaPopup - * - * description - * - * @date 10/1/18 - * @since 5.6.5 - * - * @param type $var Description. Default. - * @return type Description. - */ - - - var MediaPopup = acf.Model.extend({ - id: 'MediaPopup', - data: {}, - defaults: {}, - frame: false, - setup: function (props) { - $.extend(this.data, props); - }, - initialize: function () { - // vars - var options = this.getFrameOptions(); // add states - - this.addFrameStates(options); // create frame - - var frame = wp.media(options); // add args reference - - frame.acf = this; // add events - - this.addFrameEvents(frame, options); // strore frame - - this.frame = frame; - }, - open: function () { - this.frame.open(); - }, - close: function () { - this.frame.close(); - }, - remove: function () { - this.frame.detach(); - this.frame.remove(); - }, - getFrameOptions: function () { - // vars - var options = { - title: this.get('title'), - multiple: this.get('multiple'), - library: {}, - states: [] - }; // type - - if (this.get('type')) { - options.library.type = this.get('type'); - } // type - - - if (this.get('library') === 'uploadedTo') { - options.library.uploadedTo = getPostID(); - } // attachment - - - if (this.get('attachment')) { - options.library.post__in = [this.get('attachment')]; - } // button - - - if (this.get('button')) { - options.button = { - text: this.get('button') - }; - } // return - - - return options; - }, - addFrameStates: function (options) { - // create query - var Query = wp.media.query(options.library); // add _acfuploader - // this is super wack! - // if you add _acfuploader to the options.library args, new uploads will not be added to the library view. - // this has been traced back to the wp.media.model.Query initialize function (which can't be overriden) - // Adding any custom args will cause the Attahcments to not observe the uploader queue - // To bypass this security issue, we add in the args AFTER the Query has been initialized - // options.library._acfuploader = settings.field; - - if (this.get('field') && acf.isset(Query, 'mirroring', 'args')) { - Query.mirroring.args._acfuploader = this.get('field'); - } // add states - - - options.states.push( // main state - new wp.media.controller.Library({ - library: Query, - multiple: this.get('multiple'), - title: this.get('title'), - priority: 20, - filterable: 'all', - editable: true, - allowLocalEdits: true - })); // edit image functionality (added in WP 3.9) - - if (acf.isset(wp, 'media', 'controller', 'EditImage')) { - options.states.push(new wp.media.controller.EditImage()); - } - }, - addFrameEvents: function (frame, options) { - // log all events - //frame.on('all', function( e ) { - // console.log( 'frame all: %o', e ); - //}); - // add class - frame.on('open', function () { - this.$el.closest('.media-modal').addClass('acf-media-modal -' + this.acf.get('mode')); - }, frame); // edit image view - // source: media-views.js:2410 editImageContent() - - frame.on('content:render:edit-image', function () { - var image = this.state().get('image'); - var view = new wp.media.view.EditImage({ - model: image, - controller: this - }).render(); - this.content.set(view); // after creating the wrapper view, load the actual editor via an ajax call - - view.loadEditor(); - }, frame); // update toolbar button - //frame.on( 'toolbar:create:select', function( toolbar ) { - // toolbar.view = new wp.media.view.Toolbar.Select({ - // text: frame.options._button, - // controller: this - // }); - //}, frame ); - // on select - - frame.on('select', function () { - // vars - var selection = frame.state().get('selection'); // if selecting images - - if (selection) { - // loop - selection.each(function (attachment, i) { - frame.acf.get('select').apply(frame.acf, [attachment, i]); - }); - } - }); // on close - - frame.on('close', function () { - // callback and remove - setTimeout(function () { - frame.acf.get('close').apply(frame.acf); - frame.acf.remove(); - }, 1); - }); - } - }); - /** - * acf.models.SelectMediaPopup - * - * description - * - * @date 10/1/18 - * @since 5.6.5 - * - * @param type $var Description. Default. - * @return type Description. - */ - - acf.models.SelectMediaPopup = MediaPopup.extend({ - id: 'SelectMediaPopup', - setup: function (props) { - // default button - if (!props.button) { - props.button = acf._x('Select', 'verb'); - } // parent - - - MediaPopup.prototype.setup.apply(this, arguments); - }, - addFrameEvents: function (frame, options) { - // plupload - // adds _acfuploader param to validate uploads - if (acf.isset(_wpPluploadSettings, 'defaults', 'multipart_params')) { - // add _acfuploader so that Uploader will inherit - _wpPluploadSettings.defaults.multipart_params._acfuploader = this.get('field'); // remove acf_field so future Uploaders won't inherit - - frame.on('open', function () { - delete _wpPluploadSettings.defaults.multipart_params._acfuploader; - }); - } // browse - - - frame.on('content:activate:browse', function () { - // vars - var toolbar = false; // populate above vars making sure to allow for failure - // perhaps toolbar does not exist because the frame open is Upload Files - - try { - toolbar = frame.content.get().toolbar; - } catch (e) { - console.log(e); - return; - } // callback - - - frame.acf.customizeFilters.apply(frame.acf, [toolbar]); - }); // parent - - MediaPopup.prototype.addFrameEvents.apply(this, arguments); - }, - customizeFilters: function (toolbar) { - // vars - var filters = toolbar.get('filters'); // image - - if (this.get('type') == 'image') { - // update all - filters.filters.all.text = acf.__('All images'); // remove some filters - - delete filters.filters.audio; - delete filters.filters.video; - delete filters.filters.image; // update all filters to show images - - $.each(filters.filters, function (i, filter) { - filter.props.type = filter.props.type || 'image'; - }); - } // specific types - - - if (this.get('allowedTypes')) { - // convert ".jpg, .png" into ["jpg", "png"] - var allowedTypes = this.get('allowedTypes').split(' ').join('').split('.').join('').split(','); // loop - - allowedTypes.map(function (name) { - // get type - var mimeType = acf.getMimeType(name); // bail early if no type - - if (!mimeType) return; // create new filter - - var newFilter = { - text: mimeType, - props: { - status: null, - type: mimeType, - uploadedTo: null, - orderby: 'date', - order: 'DESC' - }, - priority: 20 - }; // append - - filters.filters[mimeType] = newFilter; - }); - } // uploaded to post - - - if (this.get('library') === 'uploadedTo') { - // vars - var uploadedTo = this.frame.options.library.uploadedTo; // remove some filters - - delete filters.filters.unattached; - delete filters.filters.uploaded; // add uploadedTo to filters - - $.each(filters.filters, function (i, filter) { - filter.text += ' (' + acf.__('Uploaded to this post') + ')'; - filter.props.uploadedTo = uploadedTo; - }); - } // add _acfuploader to filters - - - var field = this.get('field'); - $.each(filters.filters, function (k, filter) { - filter.props._acfuploader = field; - }); // add _acfuplaoder to search - - var search = toolbar.get('search'); - search.model.attributes._acfuploader = field; // render (custom function added to prototype) - - if (filters.renderFilters) { - filters.renderFilters(); - } - } - }); - /** - * acf.models.EditMediaPopup - * - * description - * - * @date 10/1/18 - * @since 5.6.5 - * - * @param type $var Description. Default. - * @return type Description. - */ - - acf.models.EditMediaPopup = MediaPopup.extend({ - id: 'SelectMediaPopup', - setup: function (props) { - // default button - if (!props.button) { - props.button = acf._x('Update', 'verb'); - } // parent - - - MediaPopup.prototype.setup.apply(this, arguments); - }, - addFrameEvents: function (frame, options) { - // add class - frame.on('open', function () { - // add class - this.$el.closest('.media-modal').addClass('acf-expanded'); // set to browse - - if (this.content.mode() != 'browse') { - this.content.mode('browse'); - } // set selection - - - var state = this.state(); - var selection = state.get('selection'); - var attachment = wp.media.attachment(frame.acf.get('attachment')); - selection.add(attachment); - }, frame); // parent - - MediaPopup.prototype.addFrameEvents.apply(this, arguments); - } - }); - /** - * customizePrototypes - * - * description - * - * @date 11/1/18 - * @since 5.6.5 - * - * @param type $var Description. Default. - * @return type Description. - */ - - var customizePrototypes = new acf.Model({ - id: 'customizePrototypes', - wait: 'ready', - initialize: function () { - // bail early if no media views - if (!acf.isset(window, 'wp', 'media', 'view')) { - return; - } // fix bug where CPT without "editor" does not set post.id setting which then prevents uploadedTo from working - - - var postID = getPostID(); - - if (postID && acf.isset(wp, 'media', 'view', 'settings', 'post')) { - wp.media.view.settings.post.id = postID; - } // customize - - - this.customizeAttachmentsButton(); - this.customizeAttachmentsRouter(); - this.customizeAttachmentFilters(); - this.customizeAttachmentCompat(); - this.customizeAttachmentLibrary(); - }, - customizeAttachmentsButton: function () { - // validate - if (!acf.isset(wp, 'media', 'view', 'Button')) { - return; - } // Extend - - - var Button = wp.media.view.Button; - wp.media.view.Button = Button.extend({ - // Fix bug where "Select" button appears blank after editing an image. - // Do this by simplifying Button initialize function and avoid deleting this.options. - initialize: function () { - var options = _.defaults(this.options, this.defaults); - - this.model = new Backbone.Model(options); - this.listenTo(this.model, 'change', this.render); - } - }); - }, - customizeAttachmentsRouter: function () { - // validate - if (!acf.isset(wp, 'media', 'view', 'Router')) { - return; - } // vars - - - var Parent = wp.media.view.Router; // extend - - wp.media.view.Router = Parent.extend({ - addExpand: function () { - // vars - var $a = $(['', '' + acf.__('Expand Details') + '', '' + acf.__('Collapse Details') + '', ''].join('')); // add events - - $a.on('click', function (e) { - e.preventDefault(); - var $div = $(this).closest('.media-modal'); - - if ($div.hasClass('acf-expanded')) { - $div.removeClass('acf-expanded'); - } else { - $div.addClass('acf-expanded'); - } - }); // append - - this.$el.append($a); - }, - initialize: function () { - // initialize - Parent.prototype.initialize.apply(this, arguments); // add buttons - - this.addExpand(); // return - - return this; - } - }); - }, - customizeAttachmentFilters: function () { - // validate - if (!acf.isset(wp, 'media', 'view', 'AttachmentFilters', 'All')) { - return; - } // vars - - - var Parent = wp.media.view.AttachmentFilters.All; // renderFilters - // copied from media-views.js:6939 - - Parent.prototype.renderFilters = function () { - // Build `').val(value).html(filter.text)[0], - priority: filter.priority || 50 - }; - }, this).sortBy('priority').pluck('el').value()); - }; - }, - customizeAttachmentCompat: function () { - // validate - if (!acf.isset(wp, 'media', 'view', 'AttachmentCompat')) { - return; - } // vars - - - var AttachmentCompat = wp.media.view.AttachmentCompat; - var timeout = false; // extend - - wp.media.view.AttachmentCompat = AttachmentCompat.extend({ - render: function () { - // WP bug - // When multiple media frames exist on the same page (WP content, WYSIWYG, image, file ), - // WP creates multiple instances of this AttachmentCompat view. - // Each instance will attempt to render when a new modal is created. - // Use a property to avoid this and only render once per instance. - if (this.rendered) { - return this; - } // render HTML - - - AttachmentCompat.prototype.render.apply(this, arguments); // when uploading, render is called twice. - // ignore first render by checking for #acf-form-data element - - if (!this.$('#acf-form-data').length) { - return this; - } // clear timeout - - - clearTimeout(timeout); // setTimeout - - timeout = setTimeout($.proxy(function () { - this.rendered = true; - acf.doAction('append', this.$el); - }, this), 50); // return - - return this; - }, - save: function (event) { - var data = {}; - - if (event) { - event.preventDefault(); - } //_.each( this.$el.serializeArray(), function( pair ) { - // data[ pair.name ] = pair.value; - //}); - // Serialize data more thoroughly to allow chckbox inputs to save. - - - data = acf.serializeForAjax(this.$el); - this.controller.trigger('attachment:compat:waiting', ['waiting']); - this.model.saveCompat(data).always(_.bind(this.postSave, this)); - } - }); - }, - customizeAttachmentLibrary: function () { - // validate - if (!acf.isset(wp, 'media', 'view', 'Attachment', 'Library')) { - return; - } // vars - - - var AttachmentLibrary = wp.media.view.Attachment.Library; // extend - - wp.media.view.Attachment.Library = AttachmentLibrary.extend({ - render: function () { - // vars - var popup = acf.isget(this, 'controller', 'acf'); - var attributes = acf.isget(this, 'model', 'attributes'); // check vars exist to avoid errors - - if (popup && attributes) { - // show errors - if (attributes.acf_errors) { - this.$el.addClass('acf-disabled'); - } // disable selected - - - var selected = popup.get('selected'); - - if (selected && selected.indexOf(attributes.id) > -1) { - this.$el.addClass('acf-selected'); - } - } // render - - - return AttachmentLibrary.prototype.render.apply(this, arguments); - }, - - /* - * toggleSelection - * - * This function is called before an attachment is selected - * A good place to check for errors and prevent the 'select' function from being fired - * - * @type function - * @date 29/09/2016 - * @since 5.4.0 - * - * @param options (object) - * @return n/a - */ - toggleSelection: function (options) { - // vars - // source: wp-includes/js/media-views.js:2880 - var collection = this.collection, - selection = this.options.selection, - model = this.model, - single = selection.single(); // vars - - var frame = this.controller; - var errors = acf.isget(this, 'model', 'attributes', 'acf_errors'); - var $sidebar = frame.$el.find('.media-frame-content .media-sidebar'); // remove previous error - - $sidebar.children('.acf-selection-error').remove(); // show attachment details - - $sidebar.children().removeClass('acf-hidden'); // add message - - if (frame && errors) { - // vars - var filename = acf.isget(this, 'model', 'attributes', 'filename'); // hide attachment details - // Gallery field continues to show previously selected attachment... - - $sidebar.children().addClass('acf-hidden'); // append message - - $sidebar.prepend(['
            ', '' + acf.__('Restricted') + '', '' + filename + '', '' + errors + '', '
            '].join('')); // reset selection (unselects all attachments) - - selection.reset(); // set single (attachment displayed in sidebar) - - selection.single(model); // return and prevent 'select' form being fired - - return; - } // return - - - return AttachmentLibrary.prototype.toggleSelection.apply(this, arguments); - } - }); - } - }); })(jQuery); +(function($, undefined){ + + acf.screen = new acf.Model({ + + active: true, + + xhr: false, + + timeout: false, + + wait: 'load', + + events: { + 'change #page_template': 'onChange', + 'change #parent_id': 'onChange', + 'change #post-formats-select': 'onChange', + 'change .categorychecklist': 'onChange', + 'change .tagsdiv': 'onChange', + 'change .acf-taxonomy-field[data-save="1"]': 'onChange', + 'change #product-type': 'onChange' + }, + + isPost: function(){ + return acf.get('screen') === 'post'; + }, + + isUser: function(){ + return acf.get('screen') === 'user'; + }, + + isTaxonomy: function(){ + return acf.get('screen') === 'taxonomy'; + }, + + isAttachment: function(){ + return acf.get('screen') === 'attachment'; + }, + + isNavMenu: function(){ + return acf.get('screen') === 'nav_menu'; + }, + + isWidget: function(){ + return acf.get('screen') === 'widget'; + }, + + isComment: function(){ + return acf.get('screen') === 'comment'; + }, + + getPageTemplate: function(){ + var $el = $('#page_template'); + return $el.length ? $el.val() : null; + }, + + getPageParent: function( e, $el ){ + var $el = $('#parent_id'); + return $el.length ? $el.val() : null; + }, + + getPageType: function( e, $el ){ + return this.getPageParent() ? 'child' : 'parent'; + }, + + getPostType: function(){ + return $('#post_type').val(); + }, + + getPostFormat: function( e, $el ){ + var $el = $('#post-formats-select input:checked'); + if( $el.length ) { + var val = $el.val(); + return (val == '0') ? 'standard' : val; + } + return null; + }, + + getPostCoreTerms: function(){ + + // vars + var terms = {}; + + // serialize WP taxonomy postboxes + var data = acf.serialize( $('.categorydiv, .tagsdiv') ); + + // use tax_input (tag, custom-taxonomy) when possible. + // this data is already formatted in taxonomy => [terms]. + if( data.tax_input ) { + terms = data.tax_input; + } + + // append "category" which uses a different name + if( data.post_category ) { + terms.category = data.post_category; + } + + // convert any string values (tags) into array format + for( var tax in terms ) { + if( !acf.isArray(terms[tax]) ) { + terms[tax] = terms[tax].split(/,[\s]?/); + } + } + + // return + return terms; + }, + + getPostTerms: function(){ + + // Get core terms. + var terms = this.getPostCoreTerms(); + + // loop over taxonomy fields and add their values + acf.getFields({type: 'taxonomy'}).map(function( field ){ + + // ignore fields that don't save + if( !field.get('save') ) { + return; + } + + // vars + var val = field.val(); + var tax = field.get('taxonomy'); + + // check val + if( val ) { + + // ensure terms exists + terms[ tax ] = terms[ tax ] || []; + + // ensure val is an array + val = acf.isArray(val) ? val : [val]; + + // append + terms[ tax ] = terms[ tax ].concat( val ); + } + }); + + // add WC product type + if( (productType = this.getProductType()) !== null ) { + terms.product_type = [productType]; + } + + // remove duplicate values + for( var tax in terms ) { + terms[tax] = acf.uniqueArray(terms[tax]); + } + + // return + return terms; + }, + + getProductType: function(){ + var $el = $('#product-type'); + return $el.length ? $el.val() : null; + }, + + check: function(){ + + // bail early if not for post + if( acf.get('screen') !== 'post' ) { + return; + } + + // abort XHR if is already loading AJAX data + if( this.xhr ) { + this.xhr.abort(); + } + + // vars + var ajaxData = acf.parseArgs(this.data, { + action: 'acf/ajax/check_screen', + screen: acf.get('screen'), + exists: [] + }); + + // post id + if( this.isPost() ) { + ajaxData.post_id = acf.get('post_id'); + } + + // post type + if( (postType = this.getPostType()) !== null ) { + ajaxData.post_type = postType; + } + + // page template + if( (pageTemplate = this.getPageTemplate()) !== null ) { + ajaxData.page_template = pageTemplate; + } + + // page parent + if( (pageParent = this.getPageParent()) !== null ) { + ajaxData.page_parent = pageParent; + } + + // page type + if( (pageType = this.getPageType()) !== null ) { + ajaxData.page_type = pageType; + } + + // post format + if( (postFormat = this.getPostFormat()) !== null ) { + ajaxData.post_format = postFormat; + } + + // post terms + if( (postTerms = this.getPostTerms()) !== null ) { + ajaxData.post_terms = postTerms; + } + + // add array of existing postboxes to increase performance and reduce JSON HTML + acf.getPostboxes().map(function( postbox ){ + ajaxData.exists.push( postbox.get('key') ); + }); + + // filter + ajaxData = acf.applyFilters('check_screen_args', ajaxData); + + // success + var onSuccess = function( json ){ + + // Render post screen. + if( acf.get('screen') == 'post' ) { + this.renderPostScreen( json ); + + // Render user screen. + } else if( acf.get('screen') == 'user' ) { + this.renderUserScreen( json ); + } + + // action + acf.doAction('check_screen_complete', json, ajaxData); + }; + + // ajax + this.xhr = $.ajax({ + url: acf.get('ajaxurl'), + data: acf.prepareForAjax( ajaxData ), + type: 'post', + dataType: 'json', + context: this, + success: onSuccess + }); + }, + + onChange: function( e, $el ){ + this.setTimeout(this.check, 1); + }, + + renderPostScreen: function( data ){ + + // Helper function to copy events + var copyEvents = function( $from, $to ){ + var events = $._data($from[0]).events; + for( var type in events ) { + for( var i = 0; i < events[type].length; i++ ) { + $to.on( type, events[type][i].handler ); + } + } + } + + // Helper function to sort metabox. + var sortMetabox = function( id, ids ){ + + // Find position of id within ids. + var index = ids.indexOf( id ); + + // Bail early if index not found. + if( index == -1 ) { + return false; + } + + // Loop over metaboxes behind (in reverse order). + for( var i = index-1; i >= 0; i-- ) { + if( $('#'+ids[i]).length ) { + return $('#'+ids[i]).after( $('#'+id) ); + } + } + + // Loop over metaboxes infront. + for( var i = index+1; i < ids.length; i++ ) { + if( $('#'+ids[i]).length ) { + return $('#'+ids[i]).before( $('#'+id) ); + } + } + + // Return false if not sorted. + return false; + }; + + // Keep track of visible and hidden postboxes. + data.visible = []; + data.hidden = []; + + // Show these postboxes. + data.results = data.results.map(function( result, i ){ + + // vars + var postbox = acf.getPostbox( result.id ); + + // Prevent "acf_after_title" position in Block Editor. + if( acf.isGutenberg() && result.position == "acf_after_title" ) { + result.position = 'normal'; + } + + // Create postbox if doesn't exist. + if( !postbox ) { + var wpMinorVersion = parseFloat( acf.get('wp_version') ); + if( wpMinorVersion >= 5.5 ) { + var postboxHeader = [ + '
            ', + '

            ', + '' + acf.escHtml( result.title ) + '', + '

            ', + '
            ', + '', + '
            ', + '
            ' + ].join(''); + } else { + var postboxHeader = [ + '', + '

            ', + '' + acf.escHtml( result.title ) + '', + '

            ', + ].join(''); + } + + // Create it. + var $postbox = $([ + '
            ', + postboxHeader, + '
            ', + result.html, + '
            ', + '
            ' + ].join('')); + + // Create new hide toggle. + if( $('#adv-settings').length ) { + var $prefs = $('#adv-settings .metabox-prefs'); + var $label = $([ + '' + ].join('')); + + // Copy default WP events onto checkbox. + copyEvents( $prefs.find('input').first(), $label.find('input') ); + + // Append hide label + $prefs.append( $label ); + } + + // Copy default WP events onto metabox. + if( $('.postbox').length ) { + copyEvents( $('.postbox .handlediv').first(), $postbox.children('.handlediv') ); + copyEvents( $('.postbox .hndle').first(), $postbox.children('.hndle') ); + } + + // Append metabox to the bottom of "side-sortables". + if( result.position === 'side' ) { + $('#' + result.position + '-sortables').append( $postbox ); + + // Prepend metabox to the top of "normal-sortbables". + } else { + $('#' + result.position + '-sortables').prepend( $postbox ); + } + + // Position metabox amongst existing ACF metaboxes within the same location. + var order = []; + data.results.map(function( _result ){ + if( result.position === _result.position && $('#' + result.position + '-sortables #' + _result.id).length ) { + order.push( _result.id ); + } + }); + sortMetabox(result.id, order) + + // Check 'sorted' for user preference. + if( data.sorted ) { + + // Loop over each position (acf_after_title, side, normal). + for( var position in data.sorted ) { + + // Explode string into array of ids. + var order = data.sorted[position].split(','); + + // Position metabox relative to order. + if( sortMetabox(result.id, order) ) { + break; + } + } + } + + // Initalize it (modifies HTML). + postbox = acf.newPostbox( result ); + + // Trigger action. + acf.doAction('append', $postbox); + acf.doAction('append_postbox', postbox); + } + + // show postbox + postbox.showEnable(); + + // append + data.visible.push( result.id ); + + // Return result (may have changed). + return result; + }); + + // Hide these postboxes. + acf.getPostboxes().map(function( postbox ){ + if( data.visible.indexOf( postbox.get('id') ) === -1 ) { + + // Hide postbox. + postbox.hideDisable(); + + // Append to data. + data.hidden.push( postbox.get('id') ); + } + }); + + // Update style. + $('#acf-style').html( data.style ); + + // Do action. + acf.doAction( 'refresh_post_screen', data ); + }, + + renderUserScreen: function( json ){ + + } + }); + + /** + * gutenScreen + * + * Adds compatibility with the Gutenberg edit screen. + * + * @date 11/12/18 + * @since 5.8.0 + * + * @param void + * @return void + */ + var gutenScreen = new acf.Model({ + + // Keep a reference to the most recent post attributes. + postEdits: {}, + + // Wait until assets have been loaded. + wait: 'prepare', + + initialize: function(){ + + // Bail early if not Gutenberg. + if( !acf.isGutenberg() ) { + return; + } + + // Listen for changes (use debounced version as this can fires often). + wp.data.subscribe( acf.debounce(this.onChange).bind(this) ); + + // Customize "acf.screen.get" functions. + acf.screen.getPageTemplate = this.getPageTemplate; + acf.screen.getPageParent = this.getPageParent; + acf.screen.getPostType = this.getPostType; + acf.screen.getPostFormat = this.getPostFormat; + acf.screen.getPostCoreTerms = this.getPostCoreTerms; + + // Disable unload + acf.unload.disable(); + + // Refresh metaboxes since WP 5.3. + var wpMinorVersion = parseFloat( acf.get('wp_version') ); + if( wpMinorVersion >= 5.3 ) { + this.addAction( 'refresh_post_screen', this.onRefreshPostScreen ); + } + + // Trigger "refresh" after WP has moved metaboxes into place. + wp.domReady( acf.refresh ); + }, + + onChange: function(){ + + // Determine attributes that can trigger a refresh. + var attributes = [ 'template', 'parent', 'format' ]; + + // Append taxonomy attribute names to this list. + ( wp.data.select( 'core' ).getTaxonomies() || [] ).map(function( taxonomy ){ + attributes.push( taxonomy.rest_base ); + }); + + // Get relevant current post edits. + var _postEdits = wp.data.select( 'core/editor' ).getPostEdits(); + var postEdits = {}; + attributes.map(function( k ){ + if( _postEdits[k] !== undefined ) { + postEdits[k] = _postEdits[k]; + } + }); + + // Detect change. + if( JSON.stringify(postEdits) !== JSON.stringify(this.postEdits) ) { + this.postEdits = postEdits; + + // Check screen. + acf.screen.check(); + } + }, + + getPageTemplate: function(){ + return wp.data.select( 'core/editor' ).getEditedPostAttribute( 'template' ); + }, + + getPageParent: function( e, $el ){ + return wp.data.select( 'core/editor' ).getEditedPostAttribute( 'parent' ); + }, + + getPostType: function(){ + return wp.data.select( 'core/editor' ).getEditedPostAttribute( 'type' ); + }, + + getPostFormat: function( e, $el ){ + return wp.data.select( 'core/editor' ).getEditedPostAttribute( 'format' ); + }, + + getPostCoreTerms: function(){ + + // vars + var terms = {}; + + // Loop over taxonomies. + var taxonomies = wp.data.select( 'core' ).getTaxonomies() || []; + taxonomies.map(function( taxonomy ){ + + // Append selected taxonomies to terms object. + var postTerms = wp.data.select( 'core/editor' ).getEditedPostAttribute( taxonomy.rest_base ); + if( postTerms ) { + terms[ taxonomy.slug ] = postTerms; + } + }); + + // return + return terms; + }, + + /** + * onRefreshPostScreen + * + * Fires after the Post edit screen metaboxs are refreshed to update the Block Editor API state. + * + * @date 11/11/19 + * @since 5.8.7 + * + * @param object data The "check_screen" JSON response data. + * @return void + */ + onRefreshPostScreen: function( data ) { + + // Extract vars. + var select = wp.data.select( 'core/edit-post' ); + var dispatch = wp.data.dispatch( 'core/edit-post' ); + + // Load current metabox locations and data. + var locations = {}; + select.getActiveMetaBoxLocations().map(function( location ){ + locations[ location ] = select.getMetaBoxesPerLocation( location ); + }); + + // Generate flat array of existing ids. + var ids = []; + for( var k in locations ) { + locations[k].map(function( m ){ + ids.push( m.id ); + }); + } + + // Append new ACF metaboxes (ignore those which already exist). + data.results.filter(function( r ){ + return ( ids.indexOf( r.id ) === -1 ); + }).map(function( result, i ){ + + // Ensure location exists. + var location = result.position; + locations[ location ] = locations[ location ] || []; + + // Append. + locations[ location ].push({ + id: result.id, + title: result.title + }); + }); + + // Remove hidden ACF metaboxes. + for( var k in locations ) { + locations[k] = locations[k].filter(function( m ){ + return ( data.hidden.indexOf( m.id ) === -1 ); + }); + } + + // Update state. + dispatch.setAvailableMetaBoxesPerLocation( locations ); + } + }); -(function ($, undefined) { - acf.screen = new acf.Model({ - active: true, - xhr: false, - timeout: false, - wait: 'load', - events: { - 'change #page_template': 'onChange', - 'change #parent_id': 'onChange', - 'change #post-formats-select': 'onChange', - 'change .categorychecklist': 'onChange', - 'change .tagsdiv': 'onChange', - 'change .acf-taxonomy-field[data-save="1"]': 'onChange', - 'change #product-type': 'onChange' - }, - isPost: function () { - return acf.get('screen') === 'post'; - }, - isUser: function () { - return acf.get('screen') === 'user'; - }, - isTaxonomy: function () { - return acf.get('screen') === 'taxonomy'; - }, - isAttachment: function () { - return acf.get('screen') === 'attachment'; - }, - isNavMenu: function () { - return acf.get('screen') === 'nav_menu'; - }, - isWidget: function () { - return acf.get('screen') === 'widget'; - }, - isComment: function () { - return acf.get('screen') === 'comment'; - }, - getPageTemplate: function () { - var $el = $('#page_template'); - return $el.length ? $el.val() : null; - }, - getPageParent: function (e, $el) { - var $el = $('#parent_id'); - return $el.length ? $el.val() : null; - }, - getPageType: function (e, $el) { - return this.getPageParent() ? 'child' : 'parent'; - }, - getPostType: function () { - return $('#post_type').val(); - }, - getPostFormat: function (e, $el) { - var $el = $('#post-formats-select input:checked'); - - if ($el.length) { - var val = $el.val(); - return val == '0' ? 'standard' : val; - } - - return null; - }, - getPostCoreTerms: function () { - // vars - var terms = {}; // serialize WP taxonomy postboxes - - var data = acf.serialize($('.categorydiv, .tagsdiv')); // use tax_input (tag, custom-taxonomy) when possible. - // this data is already formatted in taxonomy => [terms]. - - if (data.tax_input) { - terms = data.tax_input; - } // append "category" which uses a different name - - - if (data.post_category) { - terms.category = data.post_category; - } // convert any string values (tags) into array format - - - for (var tax in terms) { - if (!acf.isArray(terms[tax])) { - terms[tax] = terms[tax].split(/,[\s]?/); - } - } // return - - - return terms; - }, - getPostTerms: function () { - // Get core terms. - var terms = this.getPostCoreTerms(); // loop over taxonomy fields and add their values - - acf.getFields({ - type: 'taxonomy' - }).map(function (field) { - // ignore fields that don't save - if (!field.get('save')) { - return; - } // vars - - - var val = field.val(); - var tax = field.get('taxonomy'); // check val - - if (val) { - // ensure terms exists - terms[tax] = terms[tax] || []; // ensure val is an array - - val = acf.isArray(val) ? val : [val]; // append - - terms[tax] = terms[tax].concat(val); - } - }); // add WC product type - - if ((productType = this.getProductType()) !== null) { - terms.product_type = [productType]; - } // remove duplicate values - - - for (var tax in terms) { - terms[tax] = acf.uniqueArray(terms[tax]); - } // return - - - return terms; - }, - getProductType: function () { - var $el = $('#product-type'); - return $el.length ? $el.val() : null; - }, - check: function () { - // bail early if not for post - if (acf.get('screen') !== 'post') { - return; - } // abort XHR if is already loading AJAX data - - - if (this.xhr) { - this.xhr.abort(); - } // vars - - - var ajaxData = acf.parseArgs(this.data, { - action: 'acf/ajax/check_screen', - screen: acf.get('screen'), - exists: [] - }); // post id - - if (this.isPost()) { - ajaxData.post_id = acf.get('post_id'); - } // post type - - - if ((postType = this.getPostType()) !== null) { - ajaxData.post_type = postType; - } // page template - - - if ((pageTemplate = this.getPageTemplate()) !== null) { - ajaxData.page_template = pageTemplate; - } // page parent - - - if ((pageParent = this.getPageParent()) !== null) { - ajaxData.page_parent = pageParent; - } // page type - - - if ((pageType = this.getPageType()) !== null) { - ajaxData.page_type = pageType; - } // post format - - - if ((postFormat = this.getPostFormat()) !== null) { - ajaxData.post_format = postFormat; - } // post terms - - - if ((postTerms = this.getPostTerms()) !== null) { - ajaxData.post_terms = postTerms; - } // add array of existing postboxes to increase performance and reduce JSON HTML - - - acf.getPostboxes().map(function (postbox) { - ajaxData.exists.push(postbox.get('key')); - }); // filter - - ajaxData = acf.applyFilters('check_screen_args', ajaxData); // success - - var onSuccess = function (json) { - // Render post screen. - if (acf.get('screen') == 'post') { - this.renderPostScreen(json); // Render user screen. - } else if (acf.get('screen') == 'user') { - this.renderUserScreen(json); - } // action - - - acf.doAction('check_screen_complete', json, ajaxData); - }; // ajax - - - this.xhr = $.ajax({ - url: acf.get('ajaxurl'), - data: acf.prepareForAjax(ajaxData), - type: 'post', - dataType: 'json', - context: this, - success: onSuccess - }); - }, - onChange: function (e, $el) { - this.setTimeout(this.check, 1); - }, - renderPostScreen: function (data) { - // Helper function to copy events - var copyEvents = function ($from, $to) { - var events = $._data($from[0]).events; - - for (var type in events) { - for (var i = 0; i < events[type].length; i++) { - $to.on(type, events[type][i].handler); - } - } - }; // Helper function to sort metabox. - - - var sortMetabox = function (id, ids) { - // Find position of id within ids. - var index = ids.indexOf(id); // Bail early if index not found. - - if (index == -1) { - return false; - } // Loop over metaboxes behind (in reverse order). - - - for (var i = index - 1; i >= 0; i--) { - if ($('#' + ids[i]).length) { - return $('#' + ids[i]).after($('#' + id)); - } - } // Loop over metaboxes infront. - - - for (var i = index + 1; i < ids.length; i++) { - if ($('#' + ids[i]).length) { - return $('#' + ids[i]).before($('#' + id)); - } - } // Return false if not sorted. - - - return false; - }; // Keep track of visible and hidden postboxes. - - - data.visible = []; - data.hidden = []; // Show these postboxes. - - data.results = data.results.map(function (result, i) { - // vars - var postbox = acf.getPostbox(result.id); // Prevent "acf_after_title" position in Block Editor. - - if (acf.isGutenberg() && result.position == "acf_after_title") { - result.position = 'normal'; - } // Create postbox if doesn't exist. - - - if (!postbox) { - var wpMinorVersion = parseFloat(acf.get('wp_version')); - - if (wpMinorVersion >= 5.5) { - var postboxHeader = ['
            ', '

            ', '' + acf.escHtml(result.title) + '', '

            ', '
            ', '', '
            ', '
            '].join(''); - } else { - var postboxHeader = ['', '

            ', '' + acf.escHtml(result.title) + '', '

            '].join(''); - } // Create it. - - - var $postbox = $(['
            ', postboxHeader, '
            ', result.html, '
            ', '
            '].join('')); // Create new hide toggle. - - if ($('#adv-settings').length) { - var $prefs = $('#adv-settings .metabox-prefs'); - var $label = $([''].join('')); // Copy default WP events onto checkbox. - - copyEvents($prefs.find('input').first(), $label.find('input')); // Append hide label - - $prefs.append($label); - } // Copy default WP events onto metabox. - - - if ($('.postbox').length) { - copyEvents($('.postbox .handlediv').first(), $postbox.children('.handlediv')); - copyEvents($('.postbox .hndle').first(), $postbox.children('.hndle')); - } // Append metabox to the bottom of "side-sortables". - - - if (result.position === 'side') { - $('#' + result.position + '-sortables').append($postbox); // Prepend metabox to the top of "normal-sortbables". - } else { - $('#' + result.position + '-sortables').prepend($postbox); - } // Position metabox amongst existing ACF metaboxes within the same location. - - - var order = []; - data.results.map(function (_result) { - if (result.position === _result.position && $('#' + result.position + '-sortables #' + _result.id).length) { - order.push(_result.id); - } - }); - sortMetabox(result.id, order); // Check 'sorted' for user preference. - - if (data.sorted) { - // Loop over each position (acf_after_title, side, normal). - for (var position in data.sorted) { - // Explode string into array of ids. - var order = data.sorted[position].split(','); // Position metabox relative to order. - - if (sortMetabox(result.id, order)) { - break; - } - } - } // Initalize it (modifies HTML). - - - postbox = acf.newPostbox(result); // Trigger action. - - acf.doAction('append', $postbox); - acf.doAction('append_postbox', postbox); - } // show postbox - - - postbox.showEnable(); // append - - data.visible.push(result.id); // Return result (may have changed). - - return result; - }); // Hide these postboxes. - - acf.getPostboxes().map(function (postbox) { - if (data.visible.indexOf(postbox.get('id')) === -1) { - // Hide postbox. - postbox.hideDisable(); // Append to data. - - data.hidden.push(postbox.get('id')); - } - }); // Update style. - - $('#acf-style').html(data.style); // Do action. - - acf.doAction('refresh_post_screen', data); - }, - renderUserScreen: function (json) {} - }); - /** - * gutenScreen - * - * Adds compatibility with the Gutenberg edit screen. - * - * @date 11/12/18 - * @since 5.8.0 - * - * @param void - * @return void - */ - - var gutenScreen = new acf.Model({ - // Keep a reference to the most recent post attributes. - postEdits: {}, - // Wait until assets have been loaded. - wait: 'prepare', - initialize: function () { - // Bail early if not Gutenberg. - if (!acf.isGutenberg()) { - return; - } // Listen for changes (use debounced version as this can fires often). - - - wp.data.subscribe(acf.debounce(this.onChange).bind(this)); // Customize "acf.screen.get" functions. - - acf.screen.getPageTemplate = this.getPageTemplate; - acf.screen.getPageParent = this.getPageParent; - acf.screen.getPostType = this.getPostType; - acf.screen.getPostFormat = this.getPostFormat; - acf.screen.getPostCoreTerms = this.getPostCoreTerms; // Disable unload - - acf.unload.disable(); // Refresh metaboxes since WP 5.3. - - var wpMinorVersion = parseFloat(acf.get('wp_version')); - - if (wpMinorVersion >= 5.3) { - this.addAction('refresh_post_screen', this.onRefreshPostScreen); - } // Trigger "refresh" after WP has moved metaboxes into place. - - - wp.domReady(acf.refresh); - }, - onChange: function () { - // Determine attributes that can trigger a refresh. - var attributes = ['template', 'parent', 'format']; // Append taxonomy attribute names to this list. - - (wp.data.select('core').getTaxonomies() || []).map(function (taxonomy) { - attributes.push(taxonomy.rest_base); - }); // Get relevant current post edits. - - var _postEdits = wp.data.select('core/editor').getPostEdits(); - - var postEdits = {}; - attributes.map(function (k) { - if (_postEdits[k] !== undefined) { - postEdits[k] = _postEdits[k]; - } - }); // Detect change. - - if (JSON.stringify(postEdits) !== JSON.stringify(this.postEdits)) { - this.postEdits = postEdits; // Check screen. - - acf.screen.check(); - } - }, - getPageTemplate: function () { - return wp.data.select('core/editor').getEditedPostAttribute('template'); - }, - getPageParent: function (e, $el) { - return wp.data.select('core/editor').getEditedPostAttribute('parent'); - }, - getPostType: function () { - return wp.data.select('core/editor').getEditedPostAttribute('type'); - }, - getPostFormat: function (e, $el) { - return wp.data.select('core/editor').getEditedPostAttribute('format'); - }, - getPostCoreTerms: function () { - // vars - var terms = {}; // Loop over taxonomies. - - var taxonomies = wp.data.select('core').getTaxonomies() || []; - taxonomies.map(function (taxonomy) { - // Append selected taxonomies to terms object. - var postTerms = wp.data.select('core/editor').getEditedPostAttribute(taxonomy.rest_base); - - if (postTerms) { - terms[taxonomy.slug] = postTerms; - } - }); // return - - return terms; - }, - - /** - * onRefreshPostScreen - * - * Fires after the Post edit screen metaboxs are refreshed to update the Block Editor API state. - * - * @date 11/11/19 - * @since 5.8.7 - * - * @param object data The "check_screen" JSON response data. - * @return void - */ - onRefreshPostScreen: function (data) { - // Extract vars. - var select = wp.data.select('core/edit-post'); - var dispatch = wp.data.dispatch('core/edit-post'); // Load current metabox locations and data. - - var locations = {}; - select.getActiveMetaBoxLocations().map(function (location) { - locations[location] = select.getMetaBoxesPerLocation(location); - }); // Generate flat array of existing ids. - - var ids = []; - - for (var k in locations) { - locations[k].map(function (m) { - ids.push(m.id); - }); - } // Append new ACF metaboxes (ignore those which already exist). - - - data.results.filter(function (r) { - return ids.indexOf(r.id) === -1; - }).map(function (result, i) { - // Ensure location exists. - var location = result.position; - locations[location] = locations[location] || []; // Append. - - locations[location].push({ - id: result.id, - title: result.title - }); - }); // Remove hidden ACF metaboxes. - - for (var k in locations) { - locations[k] = locations[k].filter(function (m) { - return data.hidden.indexOf(m.id) === -1; - }); - } // Update state. - - - dispatch.setAvailableMetaBoxesPerLocation(locations); - } - }); })(jQuery); - -(function ($, undefined) { - /** - * acf.newSelect2 - * - * description - * - * @date 13/1/18 - * @since 5.6.5 - * - * @param type $var Description. Default. - * @return type Description. - */ - acf.newSelect2 = function ($select, props) { - // defaults - props = acf.parseArgs(props, { - allowNull: false, - placeholder: '', - multiple: false, - field: false, - ajax: false, - ajaxAction: '', - ajaxData: function (data) { - return data; - }, - ajaxResults: function (json) { - return json; - } - }); // initialize - - if (getVersion() == 4) { - var select2 = new Select2_4($select, props); - } else { - var select2 = new Select2_3($select, props); - } // actions - - - acf.doAction('new_select2', select2); // return - - return select2; - }; - /** - * getVersion - * - * description - * - * @date 13/1/18 - * @since 5.6.5 - * - * @param type $var Description. Default. - * @return type Description. - */ - - - function getVersion() { - // v4 - if (acf.isset(window, 'jQuery', 'fn', 'select2', 'amd')) { - return 4; - } // v3 - - - if (acf.isset(window, 'Select2')) { - return 3; - } // return - - - return false; - } - /** - * Select2 - * - * description - * - * @date 13/1/18 - * @since 5.6.5 - * - * @param type $var Description. Default. - * @return type Description. - */ - - - var Select2 = acf.Model.extend({ - setup: function ($select, props) { - $.extend(this.data, props); - this.$el = $select; - }, - initialize: function () {}, - selectOption: function (value) { - var $option = this.getOption(value); - - if (!$option.prop('selected')) { - $option.prop('selected', true).trigger('change'); - } - }, - unselectOption: function (value) { - var $option = this.getOption(value); - - if ($option.prop('selected')) { - $option.prop('selected', false).trigger('change'); - } - }, - getOption: function (value) { - return this.$('option[value="' + value + '"]'); - }, - addOption: function (option) { - // defaults - option = acf.parseArgs(option, { - id: '', - text: '', - selected: false - }); // vars - - var $option = this.getOption(option.id); // append - - if (!$option.length) { - $option = $(''); - $option.html(option.text); - $option.attr('value', option.id); - $option.prop('selected', option.selected); - this.$el.append($option); - } // chain - - - return $option; - }, - getValue: function () { - // vars - var val = []; - var $options = this.$el.find('option:selected'); // bail early if no selected - - if (!$options.exists()) { - return val; - } // sort by attribute - - - $options = $options.sort(function (a, b) { - return +a.getAttribute('data-i') - +b.getAttribute('data-i'); - }); // loop - - $options.each(function () { - var $el = $(this); - val.push({ - $el: $el, - id: $el.attr('value'), - text: $el.text() - }); - }); // return - - return val; - }, - mergeOptions: function () {}, - getChoices: function () { - // callback - var crawl = function ($parent) { - // vars - var choices = []; // loop - - $parent.children().each(function () { - // vars - var $child = $(this); // optgroup - - if ($child.is('optgroup')) { - choices.push({ - text: $child.attr('label'), - children: crawl($child) - }); // option - } else { - choices.push({ - id: $child.attr('value'), - text: $child.text() - }); - } - }); // return - - return choices; - }; // crawl - - - return crawl(this.$el); - }, - getAjaxData: function (params) { - // vars - var ajaxData = { - action: this.get('ajaxAction'), - s: params.term || '', - paged: params.page || 1 - }; // field helper - - var field = this.get('field'); - - if (field) { - ajaxData.field_key = field.get('key'); - } // callback - - - var callback = this.get('ajaxData'); - - if (callback) { - ajaxData = callback.apply(this, [ajaxData, params]); - } // filter - - - ajaxData = acf.applyFilters('select2_ajax_data', ajaxData, this.data, this.$el, field || false, this); // return - - return acf.prepareForAjax(ajaxData); - }, - getAjaxResults: function (json, params) { - // defaults - json = acf.parseArgs(json, { - results: false, - more: false - }); // callback - - var callback = this.get('ajaxResults'); - - if (callback) { - json = callback.apply(this, [json, params]); - } // filter - - - json = acf.applyFilters('select2_ajax_results', json, params, this); // return - - return json; - }, - processAjaxResults: function (json, params) { - // vars - var json = this.getAjaxResults(json, params); // change more to pagination - - if (json.more) { - json.pagination = { - more: true - }; - } // merge together groups - - - setTimeout($.proxy(this.mergeOptions, this), 1); // return - - return json; - }, - destroy: function () { - // destroy via api - if (this.$el.data('select2')) { - this.$el.select2('destroy'); - } // destory via HTML (duplicating HTML does not contain data) - - - this.$el.siblings('.select2-container').remove(); - } - }); - /** - * Select2_4 - * - * description - * - * @date 13/1/18 - * @since 5.6.5 - * - * @param type $var Description. Default. - * @return type Description. - */ - - var Select2_4 = Select2.extend({ - initialize: function () { - // vars - var $select = this.$el; - var options = { - width: '100%', - allowClear: this.get('allowNull'), - placeholder: this.get('placeholder'), - multiple: this.get('multiple'), - data: [], - escapeMarkup: function (string) { - return acf.escHtml(string); - } - }; // multiple - - if (options.multiple) { - // reorder options - this.getValue().map(function (item) { - item.$el.detach().appendTo($select); - }); - } // Temporarily remove conflicting attribute. - - - var attrAjax = $select.attr('data-ajax'); - - if (attrAjax !== undefined) { - $select.removeData('ajax'); - $select.removeAttr('data-ajax'); - } // ajax - - - if (this.get('ajax')) { - options.ajax = { - url: acf.get('ajaxurl'), - delay: 250, - dataType: 'json', - type: 'post', - cache: false, - data: $.proxy(this.getAjaxData, this), - processResults: $.proxy(this.processAjaxResults, this) - }; - } // filter for 3rd party customization - //options = acf.applyFilters( 'select2_args', options, $select, this ); - - - var field = this.get('field'); - options = acf.applyFilters('select2_args', options, $select, this.data, field || false, this); // add select2 - - $select.select2(options); // get container (Select2 v4 does not return this from constructor) - - var $container = $select.next('.select2-container'); // multiple - - if (options.multiple) { - // vars - var $ul = $container.find('ul'); // sortable - - $ul.sortable({ - stop: function (e) { - // loop - $ul.find('.select2-selection__choice').each(function () { - // vars - var $option = $($(this).data('data').element); // detach and re-append to end - - $option.detach().appendTo($select); - }); // trigger change on input (JS error if trigger on select) - - $select.trigger('change'); - } - }); // on select, move to end - - $select.on('select2:select', this.proxy(function (e) { - this.getOption(e.params.data.id).detach().appendTo(this.$el); - })); - } // add class - - - $container.addClass('-acf'); // Add back temporarily removed attr. - - if (attrAjax !== undefined) { - $select.attr('data-ajax', attrAjax); - } // action for 3rd party customization - - - acf.doAction('select2_init', $select, options, this.data, field || false, this); - }, - mergeOptions: function () { - // vars - var $prevOptions = false; - var $prevGroup = false; // loop - - $('.select2-results__option[role="group"]').each(function () { - // vars - var $options = $(this).children('ul'); - var $group = $(this).children('strong'); // compare to previous - - if ($prevGroup && $prevGroup.text() === $group.text()) { - $prevOptions.append($options.children()); - $(this).remove(); - return; - } // update vars - - - $prevOptions = $options; - $prevGroup = $group; - }); - } - }); - /** - * Select2_3 - * - * description - * - * @date 13/1/18 - * @since 5.6.5 - * - * @param type $var Description. Default. - * @return type Description. - */ - - var Select2_3 = Select2.extend({ - initialize: function () { - // vars - var $select = this.$el; - var value = this.getValue(); - var multiple = this.get('multiple'); - var options = { - width: '100%', - allowClear: this.get('allowNull'), - placeholder: this.get('placeholder'), - separator: '||', - multiple: this.get('multiple'), - data: this.getChoices(), - escapeMarkup: function (string) { - return acf.escHtml(string); - }, - dropdownCss: { - 'z-index': '999999999' - }, - initSelection: function (element, callback) { - if (multiple) { - callback(value); - } else { - callback(value.shift()); - } - } - }; // get hidden input - - var $input = $select.siblings('input'); - - if (!$input.length) { - $input = $(''); - $select.before($input); - } // set input value - - - inputValue = value.map(function (item) { - return item.id; - }).join('||'); - $input.val(inputValue); // multiple - - if (options.multiple) { - // reorder options - value.map(function (item) { - item.$el.detach().appendTo($select); - }); - } // remove blank option as we have a clear all button - - - if (options.allowClear) { - options.data = options.data.filter(function (item) { - return item.id !== ''; - }); - } // remove conflicting atts - - - $select.removeData('ajax'); - $select.removeAttr('data-ajax'); // ajax - - if (this.get('ajax')) { - options.ajax = { - url: acf.get('ajaxurl'), - quietMillis: 250, - dataType: 'json', - type: 'post', - cache: false, - data: $.proxy(this.getAjaxData, this), - results: $.proxy(this.processAjaxResults, this) - }; - } // filter for 3rd party customization - - - var field = this.get('field'); - options = acf.applyFilters('select2_args', options, $select, this.data, field || false, this); // add select2 - - $input.select2(options); // get container - - var $container = $input.select2('container'); // helper to find this select's option - - var getOption = $.proxy(this.getOption, this); // multiple - - if (options.multiple) { - // vars - var $ul = $container.find('ul'); // sortable - - $ul.sortable({ - stop: function () { - // loop - $ul.find('.select2-search-choice').each(function () { - // vars - var data = $(this).data('select2Data'); - var $option = getOption(data.id); // detach and re-append to end - - $option.detach().appendTo($select); - }); // trigger change on input (JS error if trigger on select) - - $select.trigger('change'); - } - }); - } // on select, create option and move to end - - - $input.on('select2-selecting', function (e) { - // vars - var item = e.choice; - var $option = getOption(item.id); // create if doesn't exist - - if (!$option.length) { - $option = $(''); - } // detach and re-append to end - - - $option.detach().appendTo($select); - }); // add class - - $container.addClass('-acf'); // action for 3rd party customization - - acf.doAction('select2_init', $select, options, this.data, field || false, this); // change - - $input.on('change', function () { - var val = $input.val(); - - if (val.indexOf('||')) { - val = val.split('||'); - } - - $select.val(val).trigger('change'); - }); // hide select - - $select.hide(); - }, - mergeOptions: function () { - // vars - var $prevOptions = false; - var $prevGroup = false; // loop - - $('#select2-drop .select2-result-with-children').each(function () { - // vars - var $options = $(this).children('ul'); - var $group = $(this).children('.select2-result-label'); // compare to previous - - if ($prevGroup && $prevGroup.text() === $group.text()) { - $prevGroup.append($options.children()); - $(this).remove(); - return; - } // update vars - - - $prevOptions = $options; - $prevGroup = $group; - }); - }, - getAjaxData: function (term, page) { - // create Select2 v4 params - var params = { - term: term, - page: page - }; // return - - return Select2.prototype.getAjaxData.apply(this, [params]); - } - }); // manager - - var select2Manager = new acf.Model({ - priority: 5, - wait: 'prepare', - actions: { - 'duplicate': 'onDuplicate' - }, - initialize: function () { - // vars - var locale = acf.get('locale'); - var rtl = acf.get('rtl'); - var l10n = acf.get('select2L10n'); - var version = getVersion(); // bail ealry if no l10n - - if (!l10n) { - return false; - } // bail early if 'en' - - - if (locale.indexOf('en') === 0) { - return false; - } // initialize - - - if (version == 4) { - this.addTranslations4(); - } else if (version == 3) { - this.addTranslations3(); - } - }, - addTranslations4: function () { - // vars - var l10n = acf.get('select2L10n'); - var locale = acf.get('locale'); // modify local to match html[lang] attribute (used by Select2) - - locale = locale.replace('_', '-'); // select2L10n - - var select2L10n = { - errorLoading: function () { - return l10n.load_fail; - }, - inputTooLong: function (args) { - var overChars = args.input.length - args.maximum; - - if (overChars > 1) { - return l10n.input_too_long_n.replace('%d', overChars); - } - - return l10n.input_too_long_1; - }, - inputTooShort: function (args) { - var remainingChars = args.minimum - args.input.length; - - if (remainingChars > 1) { - return l10n.input_too_short_n.replace('%d', remainingChars); - } - - return l10n.input_too_short_1; - }, - loadingMore: function () { - return l10n.load_more; - }, - maximumSelected: function (args) { - var maximum = args.maximum; - - if (maximum > 1) { - return l10n.selection_too_long_n.replace('%d', maximum); - } - - return l10n.selection_too_long_1; - }, - noResults: function () { - return l10n.matches_0; - }, - searching: function () { - return l10n.searching; - } - }; // append - - jQuery.fn.select2.amd.define('select2/i18n/' + locale, [], function () { - return select2L10n; - }); - }, - addTranslations3: function () { - // vars - var l10n = acf.get('select2L10n'); - var locale = acf.get('locale'); // modify local to match html[lang] attribute (used by Select2) - - locale = locale.replace('_', '-'); // select2L10n - - var select2L10n = { - formatMatches: function (matches) { - if (matches > 1) { - return l10n.matches_n.replace('%d', matches); - } - - return l10n.matches_1; - }, - formatNoMatches: function () { - return l10n.matches_0; - }, - formatAjaxError: function () { - return l10n.load_fail; - }, - formatInputTooShort: function (input, min) { - var remainingChars = min - input.length; - - if (remainingChars > 1) { - return l10n.input_too_short_n.replace('%d', remainingChars); - } - - return l10n.input_too_short_1; - }, - formatInputTooLong: function (input, max) { - var overChars = input.length - max; - - if (overChars > 1) { - return l10n.input_too_long_n.replace('%d', overChars); - } - - return l10n.input_too_long_1; - }, - formatSelectionTooBig: function (maximum) { - if (maximum > 1) { - return l10n.selection_too_long_n.replace('%d', maximum); - } - - return l10n.selection_too_long_1; - }, - formatLoadMore: function () { - return l10n.load_more; - }, - formatSearching: function () { - return l10n.searching; - } - }; // ensure locales exists - - $.fn.select2.locales = $.fn.select2.locales || {}; // append - - $.fn.select2.locales[locale] = select2L10n; - $.extend($.fn.select2.defaults, select2L10n); - }, - onDuplicate: function ($el, $el2) { - $el2.find('.select2-container').remove(); - } - }); +(function($, undefined){ + + /** + * acf.newSelect2 + * + * description + * + * @date 13/1/18 + * @since 5.6.5 + * + * @param type $var Description. Default. + * @return type Description. + */ + + acf.newSelect2 = function( $select, props ){ + + // defaults + props = acf.parseArgs(props, { + allowNull: false, + placeholder: '', + multiple: false, + field: false, + ajax: false, + ajaxAction: '', + ajaxData: function( data ){ return data; }, + ajaxResults: function( json ){ return json; }, + }); + + // initialize + if( getVersion() == 4 ) { + var select2 = new Select2_4( $select, props ); + } else { + var select2 = new Select2_3( $select, props ); + } + + // actions + acf.doAction('new_select2', select2); + + // return + return select2; + }; + + /** + * getVersion + * + * description + * + * @date 13/1/18 + * @since 5.6.5 + * + * @param type $var Description. Default. + * @return type Description. + */ + + function getVersion() { + + // v4 + if( acf.isset(window, 'jQuery', 'fn', 'select2', 'amd') ) { + return 4; + } + + // v3 + if( acf.isset(window, 'Select2') ) { + return 3; + } + + // return + return false; + } + + /** + * Select2 + * + * description + * + * @date 13/1/18 + * @since 5.6.5 + * + * @param type $var Description. Default. + * @return type Description. + */ + + var Select2 = acf.Model.extend({ + + setup: function( $select, props ){ + $.extend(this.data, props); + this.$el = $select; + }, + + initialize: function(){ + + }, + + selectOption: function( value ){ + var $option = this.getOption( value ); + if( !$option.prop('selected') ) { + $option.prop('selected', true).trigger('change'); + } + }, + + unselectOption: function( value ){ + var $option = this.getOption( value ); + if( $option.prop('selected') ) { + $option.prop('selected', false).trigger('change'); + } + }, + + getOption: function( value ){ + return this.$('option[value="' + value + '"]'); + }, + + addOption: function( option ){ + + // defaults + option = acf.parseArgs(option, { + id: '', + text: '', + selected: false + }); + + // vars + var $option = this.getOption( option.id ); + + // append + if( !$option.length ) { + $option = $(''); + $option.html( option.text ); + $option.attr('value', option.id); + $option.prop('selected', option.selected); + this.$el.append($option); + } + + // chain + return $option; + }, + + getValue: function(){ + + // vars + var val = []; + var $options = this.$el.find('option:selected'); + + // bail early if no selected + if( !$options.exists() ) { + return val; + } + + // sort by attribute + $options = $options.sort(function(a, b) { + return +a.getAttribute('data-i') - +b.getAttribute('data-i'); + }); + + // loop + $options.each(function(){ + var $el = $(this); + val.push({ + $el: $el, + id: $el.attr('value'), + text: $el.text(), + }); + }); + + // return + return val; + + }, + + mergeOptions: function(){ + + }, + + getChoices: function(){ + + // callback + var crawl = function( $parent ){ + + // vars + var choices = []; + + // loop + $parent.children().each(function(){ + + // vars + var $child = $(this); + + // optgroup + if( $child.is('optgroup') ) { + + choices.push({ + text: $child.attr('label'), + children: crawl( $child ) + }); + + // option + } else { + + choices.push({ + id: $child.attr('value'), + text: $child.text() + }); + } + }); + + // return + return choices; + }; + + // crawl + return crawl( this.$el ); + }, + + getAjaxData: function( params ){ + + // vars + var ajaxData = { + action: this.get('ajaxAction'), + s: params.term || '', + paged: params.page || 1 + }; + + // field helper + var field = this.get('field'); + if( field ) { + ajaxData.field_key = field.get('key'); + } + + // callback + var callback = this.get('ajaxData'); + if( callback ) { + ajaxData = callback.apply( this, [ajaxData, params] ); + } + + // filter + ajaxData = acf.applyFilters( 'select2_ajax_data', ajaxData, this.data, this.$el, (field || false), this ); + + // return + return acf.prepareForAjax(ajaxData); + }, + + getAjaxResults: function( json, params ){ + + // defaults + json = acf.parseArgs(json, { + results: false, + more: false, + }); + + // callback + var callback = this.get('ajaxResults'); + if( callback ) { + json = callback.apply( this, [json, params] ); + } + + // filter + json = acf.applyFilters( 'select2_ajax_results', json, params, this ); + + // return + return json; + }, + + processAjaxResults: function( json, params ){ + + // vars + var json = this.getAjaxResults( json, params ); + + // change more to pagination + if( json.more ) { + json.pagination = { more: true }; + } + + // merge together groups + setTimeout($.proxy(this.mergeOptions, this), 1); + + // return + return json; + }, + + destroy: function(){ + + // destroy via api + if( this.$el.data('select2') ) { + this.$el.select2('destroy'); + } + + // destory via HTML (duplicating HTML does not contain data) + this.$el.siblings('.select2-container').remove(); + } + + }); + + + /** + * Select2_4 + * + * description + * + * @date 13/1/18 + * @since 5.6.5 + * + * @param type $var Description. Default. + * @return type Description. + */ + + var Select2_4 = Select2.extend({ + + initialize: function(){ + + // vars + var $select = this.$el; + var options = { + width: '100%', + allowClear: this.get('allowNull'), + placeholder: this.get('placeholder'), + multiple: this.get('multiple'), + data: [], + escapeMarkup: function( string ){ + return acf.escHtml( string ); + }, + }; + + // multiple + if( options.multiple ) { + + // reorder options + this.getValue().map(function( item ){ + item.$el.detach().appendTo( $select ); + }); + } + + // Temporarily remove conflicting attribute. + var attrAjax = $select.attr( 'data-ajax' ); + if( attrAjax !== undefined ) { + $select.removeData('ajax'); + $select.removeAttr('data-ajax'); + } + + // ajax + if( this.get('ajax') ) { + + options.ajax = { + url: acf.get('ajaxurl'), + delay: 250, + dataType: 'json', + type: 'post', + cache: false, + data: $.proxy(this.getAjaxData, this), + processResults: $.proxy(this.processAjaxResults, this), + }; + } + + // filter for 3rd party customization + //options = acf.applyFilters( 'select2_args', options, $select, this ); + var field = this.get('field'); + options = acf.applyFilters( 'select2_args', options, $select, this.data, (field || false), this ); + + // add select2 + $select.select2( options ); + + // get container (Select2 v4 does not return this from constructor) + var $container = $select.next('.select2-container'); + + // multiple + if( options.multiple ) { + + // vars + var $ul = $container.find('ul'); + + // sortable + $ul.sortable({ + stop: function( e ) { + + // loop + $ul.find('.select2-selection__choice').each(function() { + + // vars + var $option = $( $(this).data('data').element ); + + // detach and re-append to end + $option.detach().appendTo( $select ); + }); + + // trigger change on input (JS error if trigger on select) + $select.trigger('change'); + } + }); + + // on select, move to end + $select.on('select2:select', this.proxy(function( e ){ + this.getOption( e.params.data.id ).detach().appendTo( this.$el ); + })); + } + + // add class + $container.addClass('-acf'); + + // Add back temporarily removed attr. + if( attrAjax !== undefined ) { + $select.attr('data-ajax', attrAjax); + } + + // action for 3rd party customization + acf.doAction('select2_init', $select, options, this.data, (field || false), this); + }, + + mergeOptions: function(){ + + // vars + var $prevOptions = false; + var $prevGroup = false; + + // loop + $('.select2-results__option[role="group"]').each(function(){ + + // vars + var $options = $(this).children('ul'); + var $group = $(this).children('strong'); + + // compare to previous + if( $prevGroup && $prevGroup.text() === $group.text() ) { + $prevOptions.append( $options.children() ); + $(this).remove(); + return; + } + + // update vars + $prevOptions = $options; + $prevGroup = $group; + + }); + }, + + }); + + /** + * Select2_3 + * + * description + * + * @date 13/1/18 + * @since 5.6.5 + * + * @param type $var Description. Default. + * @return type Description. + */ + + var Select2_3 = Select2.extend({ + + initialize: function(){ + + // vars + var $select = this.$el; + var value = this.getValue(); + var multiple = this.get('multiple'); + var options = { + width: '100%', + allowClear: this.get('allowNull'), + placeholder: this.get('placeholder'), + separator: '||', + multiple: this.get('multiple'), + data: this.getChoices(), + escapeMarkup: function( string ){ + return acf.escHtml( string ); + }, + dropdownCss: { + 'z-index': '999999999' + }, + initSelection: function( element, callback ) { + if( multiple ) { + callback( value ); + } else { + callback( value.shift() ); + } + } + }; + + // get hidden input + var $input = $select.siblings('input'); + if( !$input.length ) { + $input = $(''); + $select.before( $input ); + } + + // set input value + inputValue = value.map(function(item){ return item.id }).join('||'); + $input.val( inputValue ); + + // multiple + if( options.multiple ) { + + // reorder options + value.map(function( item ){ + item.$el.detach().appendTo( $select ); + }); + } + + // remove blank option as we have a clear all button + if( options.allowClear ) { + options.data = options.data.filter(function(item){ + return item.id !== ''; + }); + } + + // remove conflicting atts + $select.removeData('ajax'); + $select.removeAttr('data-ajax'); + + // ajax + if( this.get('ajax') ) { + + options.ajax = { + url: acf.get('ajaxurl'), + quietMillis: 250, + dataType: 'json', + type: 'post', + cache: false, + data: $.proxy(this.getAjaxData, this), + results: $.proxy(this.processAjaxResults, this), + }; + } + + // filter for 3rd party customization + var field = this.get('field'); + options = acf.applyFilters( 'select2_args', options, $select, this.data, (field || false), this ); + + // add select2 + $input.select2( options ); + + // get container + var $container = $input.select2('container'); + + // helper to find this select's option + var getOption = $.proxy(this.getOption, this); + + // multiple + if( options.multiple ) { + + // vars + var $ul = $container.find('ul'); + + // sortable + $ul.sortable({ + stop: function() { + + // loop + $ul.find('.select2-search-choice').each(function() { + + // vars + var data = $(this).data('select2Data'); + var $option = getOption( data.id ); + + // detach and re-append to end + $option.detach().appendTo( $select ); + }); + + // trigger change on input (JS error if trigger on select) + $select.trigger('change'); + } + }); + } + + // on select, create option and move to end + $input.on('select2-selecting', function( e ){ + + // vars + var item = e.choice; + var $option = getOption( item.id ); + + // create if doesn't exist + if( !$option.length ) { + $option = $(''); + } + + // detach and re-append to end + $option.detach().appendTo( $select ); + }); + + // add class + $container.addClass('-acf'); + + // action for 3rd party customization + acf.doAction('select2_init', $select, options, this.data, (field || false), this); + + // change + $input.on('change', function(){ + var val = $input.val(); + if( val.indexOf('||') ) { + val = val.split('||'); + } + $select.val( val ).trigger('change'); + }); + + // hide select + $select.hide(); + }, + + mergeOptions: function(){ + + // vars + var $prevOptions = false; + var $prevGroup = false; + + // loop + $('#select2-drop .select2-result-with-children').each(function(){ + + // vars + var $options = $(this).children('ul'); + var $group = $(this).children('.select2-result-label'); + + // compare to previous + if( $prevGroup && $prevGroup.text() === $group.text() ) { + $prevGroup.append( $options.children() ); + $(this).remove(); + return; + } + + // update vars + $prevOptions = $options; + $prevGroup = $group; + + }); + + }, + + getAjaxData: function( term, page ){ + + // create Select2 v4 params + var params = { + term: term, + page: page + } + + // return + return Select2.prototype.getAjaxData.apply(this, [params]); + }, + + }); + + + // manager + var select2Manager = new acf.Model({ + priority: 5, + wait: 'prepare', + actions: { + 'duplicate': 'onDuplicate' + }, + initialize: function(){ + + // vars + var locale = acf.get('locale'); + var rtl = acf.get('rtl'); + var l10n = acf.get('select2L10n'); + var version = getVersion(); + + // bail ealry if no l10n + if( !l10n ) { + return false; + } + + // bail early if 'en' + if( locale.indexOf('en') === 0 ) { + return false; + } + + // initialize + if( version == 4 ) { + this.addTranslations4(); + } else if( version == 3 ) { + this.addTranslations3(); + } + }, + + addTranslations4: function(){ + + // vars + var l10n = acf.get('select2L10n'); + var locale = acf.get('locale'); + + // modify local to match html[lang] attribute (used by Select2) + locale = locale.replace('_', '-'); + + // select2L10n + var select2L10n = { + errorLoading: function () { + return l10n.load_fail; + }, + inputTooLong: function (args) { + var overChars = args.input.length - args.maximum; + if( overChars > 1 ) { + return l10n.input_too_long_n.replace( '%d', overChars ); + } + return l10n.input_too_long_1; + }, + inputTooShort: function( args ){ + var remainingChars = args.minimum - args.input.length; + if( remainingChars > 1 ) { + return l10n.input_too_short_n.replace( '%d', remainingChars ); + } + return l10n.input_too_short_1; + }, + loadingMore: function () { + return l10n.load_more; + }, + maximumSelected: function( args ) { + var maximum = args.maximum; + if( maximum > 1 ) { + return l10n.selection_too_long_n.replace( '%d', maximum ); + } + return l10n.selection_too_long_1; + }, + noResults: function () { + return l10n.matches_0; + }, + searching: function () { + return l10n.searching; + } + }; + + // append + jQuery.fn.select2.amd.define('select2/i18n/' + locale, [], function(){ + return select2L10n; + }); + }, + + addTranslations3: function(){ + + // vars + var l10n = acf.get('select2L10n'); + var locale = acf.get('locale'); + + // modify local to match html[lang] attribute (used by Select2) + locale = locale.replace('_', '-'); + + // select2L10n + var select2L10n = { + formatMatches: function( matches ) { + if( matches > 1 ) { + return l10n.matches_n.replace( '%d', matches ); + } + return l10n.matches_1; + }, + formatNoMatches: function() { + return l10n.matches_0; + }, + formatAjaxError: function() { + return l10n.load_fail; + }, + formatInputTooShort: function( input, min ) { + var remainingChars = min - input.length; + if( remainingChars > 1 ) { + return l10n.input_too_short_n.replace( '%d', remainingChars ); + } + return l10n.input_too_short_1; + }, + formatInputTooLong: function( input, max ) { + var overChars = input.length - max; + if( overChars > 1 ) { + return l10n.input_too_long_n.replace( '%d', overChars ); + } + return l10n.input_too_long_1; + }, + formatSelectionTooBig: function( maximum ) { + if( maximum > 1 ) { + return l10n.selection_too_long_n.replace( '%d', maximum ); + } + return l10n.selection_too_long_1; + }, + formatLoadMore: function() { + return l10n.load_more; + }, + formatSearching: function() { + return l10n.searching; + } + }; + + // ensure locales exists + $.fn.select2.locales = $.fn.select2.locales || {}; + + // append + $.fn.select2.locales[ locale ] = select2L10n; + $.extend($.fn.select2.defaults, select2L10n); + }, + + onDuplicate: function( $el, $el2 ){ + $el2.find('.select2-container').remove(); + } + + }); + })(jQuery); - -(function ($, undefined) { - acf.tinymce = { - /* - * defaults - * - * This function will return default mce and qt settings - * - * @type function - * @date 18/8/17 - * @since 5.6.0 - * - * @param $post_id (int) - * @return $post_id (int) - */ - defaults: function () { - // bail early if no tinyMCEPreInit - if (typeof tinyMCEPreInit === 'undefined') return false; // vars - - var defaults = { - tinymce: tinyMCEPreInit.mceInit.acf_content, - quicktags: tinyMCEPreInit.qtInit.acf_content - }; // return - - return defaults; - }, - - /* - * initialize - * - * This function will initialize the tinymce and quicktags instances - * - * @type function - * @date 18/8/17 - * @since 5.6.0 - * - * @param $post_id (int) - * @return $post_id (int) - */ - initialize: function (id, args) { - // defaults - args = acf.parseArgs(args, { - tinymce: true, - quicktags: true, - toolbar: 'full', - mode: 'visual', - // visual,text - field: false - }); // tinymce - - if (args.tinymce) { - this.initializeTinymce(id, args); - } // quicktags - - - if (args.quicktags) { - this.initializeQuicktags(id, args); - } - }, - - /* - * initializeTinymce - * - * This function will initialize the tinymce instance - * - * @type function - * @date 18/8/17 - * @since 5.6.0 - * - * @param $post_id (int) - * @return $post_id (int) - */ - initializeTinymce: function (id, args) { - // vars - var $textarea = $('#' + id); - var defaults = this.defaults(); - var toolbars = acf.get('toolbars'); - var field = args.field || false; - var $field = field.$el || false; // bail early - - if (typeof tinymce === 'undefined') return false; - if (!defaults) return false; // check if exists - - if (tinymce.get(id)) { - return this.enable(id); - } // settings - - - var init = $.extend({}, defaults.tinymce, args.tinymce); - init.id = id; - init.selector = '#' + id; // toolbar - - var toolbar = args.toolbar; - - if (toolbar && toolbars && toolbars[toolbar]) { - for (var i = 1; i <= 4; i++) { - init['toolbar' + i] = toolbars[toolbar][i] || ''; - } - } // event - - - init.setup = function (ed) { - ed.on('change', function (e) { - ed.save(); // save to textarea - - $textarea.trigger('change'); - }); // Fix bug where Gutenberg does not hear "mouseup" event and tries to select multiple blocks. - - ed.on('mouseup', function (e) { - var event = new MouseEvent('mouseup'); - window.dispatchEvent(event); - }); // Temporarily comment out. May not be necessary due to wysiwyg field actions. - //ed.on('unload', function(e) { - // acf.tinymce.remove( id ); - //}); - }; // disable wp_autoresize_on (no solution yet for fixed toolbar) - - - init.wp_autoresize_on = false; // Enable wpautop allowing value to save without

            tags. - // Only if the "TinyMCE Advanced" plugin hasn't already set this functionality. - - if (!init.tadv_noautop) { - init.wpautop = true; - } // hook for 3rd party customization - - - init = acf.applyFilters('wysiwyg_tinymce_settings', init, id, field); // z-index fix (caused too many conflicts) - //if( acf.isset(tinymce,'ui','FloatPanel') ) { - // tinymce.ui.FloatPanel.zIndex = 900000; - //} - // store settings - - tinyMCEPreInit.mceInit[id] = init; // visual tab is active - - if (args.mode == 'visual') { - // init - var result = tinymce.init(init); // get editor - - var ed = tinymce.get(id); // validate - - if (!ed) { - return false; - } // add reference - - - ed.acf = args.field; // action - - acf.doAction('wysiwyg_tinymce_init', ed, ed.id, init, field); - } - }, - - /* - * initializeQuicktags - * - * This function will initialize the quicktags instance - * - * @type function - * @date 18/8/17 - * @since 5.6.0 - * - * @param $post_id (int) - * @return $post_id (int) - */ - initializeQuicktags: function (id, args) { - // vars - var defaults = this.defaults(); // bail early - - if (typeof quicktags === 'undefined') return false; - if (!defaults) return false; // settings - - var init = $.extend({}, defaults.quicktags, args.quicktags); - init.id = id; // filter - - var field = args.field || false; - var $field = field.$el || false; - init = acf.applyFilters('wysiwyg_quicktags_settings', init, init.id, field); // store settings - - tinyMCEPreInit.qtInit[id] = init; // init - - var ed = quicktags(init); // validate - - if (!ed) { - return false; - } // generate HTML - - - this.buildQuicktags(ed); // action for 3rd party customization - - acf.doAction('wysiwyg_quicktags_init', ed, ed.id, init, field); - }, - - /* - * buildQuicktags - * - * This function will build the quicktags HTML - * - * @type function - * @date 18/8/17 - * @since 5.6.0 - * - * @param $post_id (int) - * @return $post_id (int) - */ - buildQuicktags: function (ed) { - var canvas, - name, - settings, - theButtons, - html, - ed, - id, - i, - use, - instanceId, - defaults = ',strong,em,link,block,del,ins,img,ul,ol,li,code,more,close,'; - canvas = ed.canvas; - name = ed.name; - settings = ed.settings; - html = ''; - theButtons = {}; - use = ''; - instanceId = ed.id; // set buttons - - if (settings.buttons) { - use = ',' + settings.buttons + ','; - } - - for (i in edButtons) { - if (!edButtons[i]) { - continue; - } - - id = edButtons[i].id; - - if (use && defaults.indexOf(',' + id + ',') !== -1 && use.indexOf(',' + id + ',') === -1) { - continue; - } - - if (!edButtons[i].instance || edButtons[i].instance === instanceId) { - theButtons[id] = edButtons[i]; - - if (edButtons[i].html) { - html += edButtons[i].html(name + '_'); - } - } - } - - if (use && use.indexOf(',dfw,') !== -1) { - theButtons.dfw = new QTags.DFWButton(); - html += theButtons.dfw.html(name + '_'); - } - - if ('rtl' === document.getElementsByTagName('html')[0].dir) { - theButtons.textdirection = new QTags.TextDirectionButton(); - html += theButtons.textdirection.html(name + '_'); - } - - ed.toolbar.innerHTML = html; - ed.theButtons = theButtons; - - if (typeof jQuery !== 'undefined') { - jQuery(document).triggerHandler('quicktags-init', [ed]); - } - }, - disable: function (id) { - this.destroyTinymce(id); - }, - remove: function (id) { - this.destroyTinymce(id); - }, - destroy: function (id) { - this.destroyTinymce(id); - }, - destroyTinymce: function (id) { - // bail early - if (typeof tinymce === 'undefined') return false; // get editor - - var ed = tinymce.get(id); // bail early if no editor - - if (!ed) return false; // save - - ed.save(); // destroy editor - - ed.destroy(); // return - - return true; - }, - enable: function (id) { - this.enableTinymce(id); - }, - enableTinymce: function (id) { - // bail early - if (typeof switchEditors === 'undefined') return false; // bail ealry if not initialized - - if (typeof tinyMCEPreInit.mceInit[id] === 'undefined') return false; // Ensure textarea element is visible - // - Fixes bug in block editor when switching between "Block" and "Document" tabs. - - $('#' + id).show(); // toggle - - switchEditors.go(id, 'tmce'); // return - - return true; - } - }; - var editorManager = new acf.Model({ - // hook in before fieldsEventManager, conditions, etc - priority: 5, - actions: { - 'prepare': 'onPrepare', - 'ready': 'onReady' - }, - onPrepare: function () { - // find hidden editor which may exist within a field - var $div = $('#acf-hidden-wp-editor'); // move to footer - - if ($div.exists()) { - $div.appendTo('body'); - } - }, - onReady: function () { - // Restore wp.editor functions used by tinymce removed in WP5. - if (acf.isset(window, 'wp', 'oldEditor')) { - wp.editor.autop = wp.oldEditor.autop; - wp.editor.removep = wp.oldEditor.removep; - } // bail early if no tinymce - - - if (!acf.isset(window, 'tinymce', 'on')) return; // restore default activeEditor - - tinymce.on('AddEditor', function (data) { - // vars - var editor = data.editor; // bail early if not 'acf' - - if (editor.id.substr(0, 3) !== 'acf') return; // override if 'content' exists - - editor = tinymce.editors.content || editor; // update vars - - tinymce.activeEditor = editor; - wpActiveEditor = editor.id; - }); - } - }); +(function($, undefined){ + + acf.tinymce = { + + /* + * defaults + * + * This function will return default mce and qt settings + * + * @type function + * @date 18/8/17 + * @since 5.6.0 + * + * @param $post_id (int) + * @return $post_id (int) + */ + + defaults: function(){ + + // bail early if no tinyMCEPreInit + if( typeof tinyMCEPreInit === 'undefined' ) return false; + + // vars + var defaults = { + tinymce: tinyMCEPreInit.mceInit.acf_content, + quicktags: tinyMCEPreInit.qtInit.acf_content + }; + + // return + return defaults; + }, + + + /* + * initialize + * + * This function will initialize the tinymce and quicktags instances + * + * @type function + * @date 18/8/17 + * @since 5.6.0 + * + * @param $post_id (int) + * @return $post_id (int) + */ + + initialize: function( id, args ){ + + // defaults + args = acf.parseArgs(args, { + tinymce: true, + quicktags: true, + toolbar: 'full', + mode: 'visual', // visual,text + field: false + }); + + // tinymce + if( args.tinymce ) { + this.initializeTinymce( id, args ); + } + + // quicktags + if( args.quicktags ) { + this.initializeQuicktags( id, args ); + } + }, + + + /* + * initializeTinymce + * + * This function will initialize the tinymce instance + * + * @type function + * @date 18/8/17 + * @since 5.6.0 + * + * @param $post_id (int) + * @return $post_id (int) + */ + + initializeTinymce: function( id, args ){ + + // vars + var $textarea = $('#'+id); + var defaults = this.defaults(); + var toolbars = acf.get('toolbars'); + var field = args.field || false; + var $field = field.$el || false; + + // bail early + if( typeof tinymce === 'undefined' ) return false; + if( !defaults ) return false; + + // check if exists + if( tinymce.get(id) ) { + return this.enable( id ); + } + + // settings + var init = $.extend( {}, defaults.tinymce, args.tinymce ); + init.id = id; + init.selector = '#' + id; + + // toolbar + var toolbar = args.toolbar; + if( toolbar && toolbars && toolbars[toolbar] ) { + + for( var i = 1; i <= 4; i++ ) { + init[ 'toolbar' + i ] = toolbars[toolbar][i] || ''; + } + } + + // event + init.setup = function( ed ){ + + ed.on('change', function(e) { + ed.save(); // save to textarea + $textarea.trigger('change'); + }); + + // Fix bug where Gutenberg does not hear "mouseup" event and tries to select multiple blocks. + ed.on('mouseup', function(e) { + var event = new MouseEvent('mouseup'); + window.dispatchEvent(event); + }); + + // Temporarily comment out. May not be necessary due to wysiwyg field actions. + //ed.on('unload', function(e) { + // acf.tinymce.remove( id ); + //}); + }; + + // disable wp_autoresize_on (no solution yet for fixed toolbar) + init.wp_autoresize_on = false; + + // Enable wpautop allowing value to save without

            tags. + // Only if the "TinyMCE Advanced" plugin hasn't already set this functionality. + if( !init.tadv_noautop ) { + init.wpautop = true; + } + + // hook for 3rd party customization + init = acf.applyFilters('wysiwyg_tinymce_settings', init, id, field); + + // z-index fix (caused too many conflicts) + //if( acf.isset(tinymce,'ui','FloatPanel') ) { + // tinymce.ui.FloatPanel.zIndex = 900000; + //} + + // store settings + tinyMCEPreInit.mceInit[ id ] = init; + + // visual tab is active + if( args.mode == 'visual' ) { + + // init + var result = tinymce.init( init ); + + // get editor + var ed = tinymce.get( id ); + + // validate + if( !ed ) { + return false; + } + + // add reference + ed.acf = args.field; + + // action + acf.doAction('wysiwyg_tinymce_init', ed, ed.id, init, field); + } + }, + + /* + * initializeQuicktags + * + * This function will initialize the quicktags instance + * + * @type function + * @date 18/8/17 + * @since 5.6.0 + * + * @param $post_id (int) + * @return $post_id (int) + */ + + initializeQuicktags: function( id, args ){ + + // vars + var defaults = this.defaults(); + + // bail early + if( typeof quicktags === 'undefined' ) return false; + if( !defaults ) return false; + + // settings + var init = $.extend( {}, defaults.quicktags, args.quicktags ); + init.id = id; + + // filter + var field = args.field || false; + var $field = field.$el || false; + init = acf.applyFilters('wysiwyg_quicktags_settings', init, init.id, field); + + // store settings + tinyMCEPreInit.qtInit[ id ] = init; + + // init + var ed = quicktags( init ); + + // validate + if( !ed ) { + return false; + } + + // generate HTML + this.buildQuicktags( ed ); + + // action for 3rd party customization + acf.doAction('wysiwyg_quicktags_init', ed, ed.id, init, field); + }, + + + /* + * buildQuicktags + * + * This function will build the quicktags HTML + * + * @type function + * @date 18/8/17 + * @since 5.6.0 + * + * @param $post_id (int) + * @return $post_id (int) + */ + + buildQuicktags: function( ed ){ + + var canvas, name, settings, theButtons, html, ed, id, i, use, instanceId, + defaults = ',strong,em,link,block,del,ins,img,ul,ol,li,code,more,close,'; + + canvas = ed.canvas; + name = ed.name; + settings = ed.settings; + html = ''; + theButtons = {}; + use = ''; + instanceId = ed.id; + + // set buttons + if ( settings.buttons ) { + use = ','+settings.buttons+','; + } + + for ( i in edButtons ) { + if ( ! edButtons[i] ) { + continue; + } + + id = edButtons[i].id; + if ( use && defaults.indexOf( ',' + id + ',' ) !== -1 && use.indexOf( ',' + id + ',' ) === -1 ) { + continue; + } + + if ( ! edButtons[i].instance || edButtons[i].instance === instanceId ) { + theButtons[id] = edButtons[i]; + + if ( edButtons[i].html ) { + html += edButtons[i].html( name + '_' ); + } + } + } + + if ( use && use.indexOf(',dfw,') !== -1 ) { + theButtons.dfw = new QTags.DFWButton(); + html += theButtons.dfw.html( name + '_' ); + } + + if ( 'rtl' === document.getElementsByTagName( 'html' )[0].dir ) { + theButtons.textdirection = new QTags.TextDirectionButton(); + html += theButtons.textdirection.html( name + '_' ); + } + + ed.toolbar.innerHTML = html; + ed.theButtons = theButtons; + + if ( typeof jQuery !== 'undefined' ) { + jQuery( document ).triggerHandler( 'quicktags-init', [ ed ] ); + } + + }, + + disable: function( id ){ + this.destroyTinymce( id ); + }, + + remove: function( id ){ + this.destroyTinymce( id ); + }, + + destroy: function( id ){ + this.destroyTinymce( id ); + }, + + destroyTinymce: function( id ){ + + // bail early + if( typeof tinymce === 'undefined' ) return false; + + // get editor + var ed = tinymce.get( id ); + + // bail early if no editor + if( !ed ) return false; + + // save + ed.save(); + + // destroy editor + ed.destroy(); + + // return + return true; + }, + + enable: function( id ){ + this.enableTinymce( id ); + }, + + enableTinymce: function( id ){ + + // bail early + if( typeof switchEditors === 'undefined' ) return false; + + // bail ealry if not initialized + if( typeof tinyMCEPreInit.mceInit[ id ] === 'undefined' ) return false; + + // Ensure textarea element is visible + // - Fixes bug in block editor when switching between "Block" and "Document" tabs. + $('#'+id).show(); + + // toggle + switchEditors.go( id, 'tmce'); + + // return + return true; + } + }; + + var editorManager = new acf.Model({ + + // hook in before fieldsEventManager, conditions, etc + priority: 5, + + actions: { + 'prepare': 'onPrepare', + 'ready': 'onReady', + }, + onPrepare: function(){ + + // find hidden editor which may exist within a field + var $div = $('#acf-hidden-wp-editor'); + + // move to footer + if( $div.exists() ) { + $div.appendTo('body'); + } + }, + onReady: function(){ + + // Restore wp.editor functions used by tinymce removed in WP5. + if( acf.isset(window,'wp','oldEditor') ) { + wp.editor.autop = wp.oldEditor.autop; + wp.editor.removep = wp.oldEditor.removep; + } + + // bail early if no tinymce + if( !acf.isset(window,'tinymce','on') ) return; + + // restore default activeEditor + tinymce.on('AddEditor', function( data ){ + + // vars + var editor = data.editor; + + // bail early if not 'acf' + if( editor.id.substr(0, 3) !== 'acf' ) return; + + // override if 'content' exists + editor = tinymce.editors.content || editor; + + // update vars + tinymce.activeEditor = editor; + wpActiveEditor = editor.id; + }); + } + }); + })(jQuery); - -(function ($, undefined) { - /** - * Validator - * - * The model for validating forms - * - * @date 4/9/18 - * @since 5.7.5 - * - * @param void - * @return void - */ - var Validator = acf.Model.extend({ - /** @var string The model identifier. */ - id: 'Validator', - - /** @var object The model data. */ - data: { - /** @var array The form errors. */ - errors: [], - - /** @var object The form notice. */ - notice: null, - - /** @var string The form status. loading, invalid, valid */ - status: '' - }, - - /** @var object The model events. */ - events: { - 'changed:status': 'onChangeStatus' - }, - - /** - * addErrors - * - * Adds errors to the form. - * - * @date 4/9/18 - * @since 5.7.5 - * - * @param array errors An array of errors. - * @return void - */ - addErrors: function (errors) { - errors.map(this.addError, this); - }, - - /** - * addError - * - * Adds and error to the form. - * - * @date 4/9/18 - * @since 5.7.5 - * - * @param object error An error object containing input and message. - * @return void - */ - addError: function (error) { - this.data.errors.push(error); - }, - - /** - * hasErrors - * - * Returns true if the form has errors. - * - * @date 4/9/18 - * @since 5.7.5 - * - * @param void - * @return bool - */ - hasErrors: function () { - return this.data.errors.length; - }, - - /** - * clearErrors - * - * Removes any errors. - * - * @date 4/9/18 - * @since 5.7.5 - * - * @param void - * @return void - */ - clearErrors: function () { - return this.data.errors = []; - }, - - /** - * getErrors - * - * Returns the forms errors. - * - * @date 4/9/18 - * @since 5.7.5 - * - * @param void - * @return array - */ - getErrors: function () { - return this.data.errors; - }, - - /** - * getFieldErrors - * - * Returns the forms field errors. - * - * @date 4/9/18 - * @since 5.7.5 - * - * @param void - * @return array - */ - getFieldErrors: function () { - // vars - var errors = []; - var inputs = []; // loop - - this.getErrors().map(function (error) { - // bail early if global - if (!error.input) return; // update if exists - - var i = inputs.indexOf(error.input); - - if (i > -1) { - errors[i] = error; // update - } else { - errors.push(error); - inputs.push(error.input); - } - }); // return - - return errors; - }, - - /** - * getGlobalErrors - * - * Returns the forms global errors (errors without a specific input). - * - * @date 4/9/18 - * @since 5.7.5 - * - * @param void - * @return array - */ - getGlobalErrors: function () { - // return array of errors that contain no input - return this.getErrors().filter(function (error) { - return !error.input; - }); - }, - - /** - * showErrors - * - * Displays all errors for this form. - * - * @date 4/9/18 - * @since 5.7.5 - * - * @param void - * @return void - */ - showErrors: function () { - // bail early if no errors - if (!this.hasErrors()) { - return; - } // vars - - - var fieldErrors = this.getFieldErrors(); - var globalErrors = this.getGlobalErrors(); // vars - - var errorCount = 0; - var $scrollTo = false; // loop - - fieldErrors.map(function (error) { - // get input - var $input = this.$('[name="' + error.input + '"]').first(); // if $_POST value was an array, this $input may not exist - - if (!$input.length) { - $input = this.$('[name^="' + error.input + '"]').first(); - } // bail early if input doesn't exist - - - if (!$input.length) { - return; - } // increase - - - errorCount++; // get field - - var field = acf.getClosestField($input); // show error - - field.showError(error.message); // set $scrollTo - - if (!$scrollTo) { - $scrollTo = field.$el; - } - }, this); // errorMessage - - var errorMessage = acf.__('Validation failed'); - - globalErrors.map(function (error) { - errorMessage += '. ' + error.message; - }); - - if (errorCount == 1) { - errorMessage += '. ' + acf.__('1 field requires attention'); - } else if (errorCount > 1) { - errorMessage += '. ' + acf.__('%d fields require attention').replace('%d', errorCount); - } // notice - - - if (this.has('notice')) { - this.get('notice').update({ - type: 'error', - text: errorMessage - }); - } else { - var notice = acf.newNotice({ - type: 'error', - text: errorMessage, - target: this.$el - }); - this.set('notice', notice); - } // if no $scrollTo, set to message - - - if (!$scrollTo) { - $scrollTo = this.get('notice').$el; - } // timeout - - - setTimeout(function () { - $("html, body").animate({ - scrollTop: $scrollTo.offset().top - $(window).height() / 2 - }, 500); - }, 10); - }, - - /** - * onChangeStatus - * - * Update the form class when changing the 'status' data - * - * @date 4/9/18 - * @since 5.7.5 - * - * @param object e The event object. - * @param jQuery $el The form element. - * @param string value The new status. - * @param string prevValue The old status. - * @return void - */ - onChangeStatus: function (e, $el, value, prevValue) { - this.$el.removeClass('is-' + prevValue).addClass('is-' + value); - }, - - /** - * validate - * - * Vaildates the form via AJAX. - * - * @date 4/9/18 - * @since 5.7.5 - * - * @param object args A list of settings to customize the validation process. - * @return bool True if the form is valid. - */ - validate: function (args) { - // default args - args = acf.parseArgs(args, { - // trigger event - event: false, - // reset the form after submit - reset: false, - // loading callback - loading: function () {}, - // complete callback - complete: function () {}, - // failure callback - failure: function () {}, - // success callback - success: function ($form) { - $form.submit(); - } - }); // return true if is valid - allows form submit - - if (this.get('status') == 'valid') { - return true; - } // return false if is currently validating - prevents form submit - - - if (this.get('status') == 'validating') { - return false; - } // return true if no ACF fields exist (no need to validate) - - - if (!this.$('.acf-field').length) { - return true; - } // if event is provided, create a new success callback. - - - if (args.event) { - var event = $.Event(null, args.event); - - args.success = function () { - acf.enableSubmit($(event.target)).trigger(event); - }; - } // action for 3rd party - - - acf.doAction('validation_begin', this.$el); // lock form - - acf.lockForm(this.$el); // loading callback - - args.loading(this.$el, this); // update status - - this.set('status', 'validating'); // success callback - - var onSuccess = function (json) { - // validate - if (!acf.isAjaxSuccess(json)) { - return; - } // filter - - - var data = acf.applyFilters('validation_complete', json.data, this.$el, this); // add errors - - if (!data.valid) { - this.addErrors(data.errors); - } - }; // complete - - - var onComplete = function () { - // unlock form - acf.unlockForm(this.$el); // failure - - if (this.hasErrors()) { - // update status - this.set('status', 'invalid'); // action - - acf.doAction('validation_failure', this.$el, this); // display errors - - this.showErrors(); // failure callback - - args.failure(this.$el, this); // success - } else { - // update status - this.set('status', 'valid'); // remove previous error message - - if (this.has('notice')) { - this.get('notice').update({ - type: 'success', - text: acf.__('Validation successful'), - timeout: 1000 - }); - } // action - - - acf.doAction('validation_success', this.$el, this); - acf.doAction('submit', this.$el); // success callback (submit form) - - args.success(this.$el, this); // lock form - - acf.lockForm(this.$el); // reset - - if (args.reset) { - this.reset(); - } - } // complete callback - - - args.complete(this.$el, this); // clear errors - - this.clearErrors(); - }; // serialize form data - - - var data = acf.serialize(this.$el); - data.action = 'acf/validate_save_post'; // ajax - - $.ajax({ - url: acf.get('ajaxurl'), - data: acf.prepareForAjax(data), - type: 'post', - dataType: 'json', - context: this, - success: onSuccess, - complete: onComplete - }); // return false to fail validation and allow AJAX - - return false; - }, - - /** - * setup - * - * Called during the constructor function to setup this instance - * - * @date 4/9/18 - * @since 5.7.5 - * - * @param jQuery $form The form element. - * @return void - */ - setup: function ($form) { - // set $el - this.$el = $form; - }, - - /** - * reset - * - * Rests the validation to be used again. - * - * @date 6/9/18 - * @since 5.7.5 - * - * @param void - * @return void - */ - reset: function () { - // reset data - this.set('errors', []); - this.set('notice', null); - this.set('status', ''); // unlock form - - acf.unlockForm(this.$el); - } - }); - /** - * getValidator - * - * Returns the instance for a given form element. - * - * @date 4/9/18 - * @since 5.7.5 - * - * @param jQuery $el The form element. - * @return object - */ - - var getValidator = function ($el) { - // instantiate - var validator = $el.data('acf'); - - if (!validator) { - validator = new Validator($el); - } // return - - - return validator; - }; - /** - * acf.validateForm - * - * A helper function for the Validator.validate() function. - * Returns true if form is valid, or fetches a validation request and returns false. - * - * @date 4/4/18 - * @since 5.6.9 - * - * @param object args A list of settings to customize the validation process. - * @return bool - */ - - - acf.validateForm = function (args) { - return getValidator(args.form).validate(args); - }; - /** - * acf.enableSubmit - * - * Enables a submit button and returns the element. - * - * @date 30/8/18 - * @since 5.7.4 - * - * @param jQuery $submit The submit button. - * @return jQuery - */ - - - acf.enableSubmit = function ($submit) { - return $submit.removeClass('disabled'); - }; - /** - * acf.disableSubmit - * - * Disables a submit button and returns the element. - * - * @date 30/8/18 - * @since 5.7.4 - * - * @param jQuery $submit The submit button. - * @return jQuery - */ - - - acf.disableSubmit = function ($submit) { - return $submit.addClass('disabled'); - }; - /** - * acf.showSpinner - * - * Shows the spinner element. - * - * @date 4/9/18 - * @since 5.7.5 - * - * @param jQuery $spinner The spinner element. - * @return jQuery - */ - - - acf.showSpinner = function ($spinner) { - $spinner.addClass('is-active'); // add class (WP > 4.2) - - $spinner.css('display', 'inline-block'); // css (WP < 4.2) - - return $spinner; - }; - /** - * acf.hideSpinner - * - * Hides the spinner element. - * - * @date 4/9/18 - * @since 5.7.5 - * - * @param jQuery $spinner The spinner element. - * @return jQuery - */ - - - acf.hideSpinner = function ($spinner) { - $spinner.removeClass('is-active'); // add class (WP > 4.2) - - $spinner.css('display', 'none'); // css (WP < 4.2) - - return $spinner; - }; - /** - * acf.lockForm - * - * Locks a form by disabeling its primary inputs and showing a spinner. - * - * @date 4/9/18 - * @since 5.7.5 - * - * @param jQuery $form The form element. - * @return jQuery - */ - - - acf.lockForm = function ($form) { - // vars - var $wrap = findSubmitWrap($form); - var $submit = $wrap.find('.button, [type="submit"]'); - var $spinner = $wrap.find('.spinner, .acf-spinner'); // hide all spinners (hides the preview spinner) - - acf.hideSpinner($spinner); // lock - - acf.disableSubmit($submit); - acf.showSpinner($spinner.last()); - return $form; - }; - /** - * acf.unlockForm - * - * Unlocks a form by enabeling its primary inputs and hiding all spinners. - * - * @date 4/9/18 - * @since 5.7.5 - * - * @param jQuery $form The form element. - * @return jQuery - */ - - - acf.unlockForm = function ($form) { - // vars - var $wrap = findSubmitWrap($form); - var $submit = $wrap.find('.button, [type="submit"]'); - var $spinner = $wrap.find('.spinner, .acf-spinner'); // unlock - - acf.enableSubmit($submit); - acf.hideSpinner($spinner); - return $form; - }; - /** - * findSubmitWrap - * - * An internal function to find the 'primary' form submit wrapping element. - * - * @date 4/9/18 - * @since 5.7.5 - * - * @param jQuery $form The form element. - * @return jQuery - */ - - - var findSubmitWrap = function ($form) { - // default post submit div - var $wrap = $form.find('#submitdiv'); - - if ($wrap.length) { - return $wrap; - } // 3rd party publish box - - - var $wrap = $form.find('#submitpost'); - - if ($wrap.length) { - return $wrap; - } // term, user - - - var $wrap = $form.find('p.submit').last(); - - if ($wrap.length) { - return $wrap; - } // front end form - - - var $wrap = $form.find('.acf-form-submit'); - - if ($wrap.length) { - return $wrap; - } // default - - - return $form; - }; - /** - * A debounced function to trigger a form submission. - * - * @date 15/07/2020 - * @since 5.9.0 - * - * @param type Var Description. - * @return type Description. - */ - - - var submitFormDebounced = acf.debounce(function ($form) { - $form.submit(); - }); - /** - * acf.validation - * - * Global validation logic - * - * @date 4/4/18 - * @since 5.6.9 - * - * @param void - * @return void - */ - - acf.validation = new acf.Model({ - /** @var string The model identifier. */ - id: 'validation', - - /** @var bool The active state. Set to false before 'prepare' to prevent validation. */ - active: true, - - /** @var string The model initialize time. */ - wait: 'prepare', - - /** @var object The model actions. */ - actions: { - 'ready': 'addInputEvents', - 'append': 'addInputEvents' - }, - - /** @var object The model events. */ - events: { - 'click input[type="submit"]': 'onClickSubmit', - 'click button[type="submit"]': 'onClickSubmit', - //'click #editor .editor-post-publish-button': 'onClickSubmitGutenberg', - 'click #save-post': 'onClickSave', - 'submit form#post': 'onSubmitPost', - 'submit form': 'onSubmit' - }, - - /** - * initialize - * - * Called when initializing the model. - * - * @date 4/9/18 - * @since 5.7.5 - * - * @param void - * @return void - */ - initialize: function () { - // check 'validation' setting - if (!acf.get('validation')) { - this.active = false; - this.actions = {}; - this.events = {}; - } - }, - - /** - * enable - * - * Enables validation. - * - * @date 4/9/18 - * @since 5.7.5 - * - * @param void - * @return void - */ - enable: function () { - this.active = true; - }, - - /** - * disable - * - * Disables validation. - * - * @date 4/9/18 - * @since 5.7.5 - * - * @param void - * @return void - */ - disable: function () { - this.active = false; - }, - - /** - * reset - * - * Rests the form validation to be used again - * - * @date 6/9/18 - * @since 5.7.5 - * - * @param jQuery $form The form element. - * @return void - */ - reset: function ($form) { - getValidator($form).reset(); - }, - - /** - * addInputEvents - * - * Adds 'invalid' event listeners to HTML inputs. - * - * @date 4/9/18 - * @since 5.7.5 - * - * @param jQuery $el The element being added / readied. - * @return void - */ - addInputEvents: function ($el) { - // Bug exists in Safari where custom "invalid" handeling prevents draft from saving. - if (acf.get('browser') === 'safari') return; // vars - - var $inputs = $('.acf-field [name]', $el); // check - - if ($inputs.length) { - this.on($inputs, 'invalid', 'onInvalid'); - } - }, - - /** - * onInvalid - * - * Callback for the 'invalid' event. - * - * @date 4/9/18 - * @since 5.7.5 - * - * @param object e The event object. - * @param jQuery $el The input element. - * @return void - */ - onInvalid: function (e, $el) { - // prevent default - // - prevents browser error message - // - also fixes chrome bug where 'hidden-by-tab' field throws focus error - e.preventDefault(); // vars - - var $form = $el.closest('form'); // check form exists - - if ($form.length) { - // add error to validator - getValidator($form).addError({ - input: $el.attr('name'), - message: acf.strEscape(e.target.validationMessage) - }); // trigger submit on $form - // - allows for "save", "preview" and "publish" to work - - submitFormDebounced($form); - } - }, - - /** - * onClickSubmit - * - * Callback when clicking submit. - * - * @date 4/9/18 - * @since 5.7.5 - * - * @param object e The event object. - * @param jQuery $el The input element. - * @return void - */ - onClickSubmit: function (e, $el) { - // store the "click event" for later use in this.onSubmit() - this.set('originalEvent', e); - }, - - /** - * onClickSave - * - * Set ignore to true when saving a draft. - * - * @date 4/9/18 - * @since 5.7.5 - * - * @param object e The event object. - * @param jQuery $el The input element. - * @return void - */ - onClickSave: function (e, $el) { - this.set('ignore', true); - }, - - /** - * onClickSubmitGutenberg - * - * Custom validation event for the gutenberg editor. - * - * @date 29/10/18 - * @since 5.8.0 - * - * @param object e The event object. - * @param jQuery $el The input element. - * @return void - */ - onClickSubmitGutenberg: function (e, $el) { - // validate - var valid = acf.validateForm({ - form: $('#editor'), - event: e, - reset: true, - failure: function ($form, validator) { - var $notice = validator.get('notice').$el; - $notice.appendTo('.components-notice-list'); - $notice.find('.acf-notice-dismiss').removeClass('small'); - } - }); // if not valid, stop event and allow validation to continue - - if (!valid) { - e.preventDefault(); - e.stopImmediatePropagation(); - } - }, - - /** - * onSubmitPost - * - * Callback when the 'post' form is submit. - * - * @date 5/3/19 - * @since 5.7.13 - * - * @param object e The event object. - * @param jQuery $el The input element. - * @return void - */ - onSubmitPost: function (e, $el) { - // Check if is preview. - if ($('input#wp-preview').val() === 'dopreview') { - // Ignore validation. - this.set('ignore', true); // Unlock form to fix conflict with core "submit.edit-post" event causing all submit buttons to be disabled. - - acf.unlockForm($el); - } - }, - - /** - * onSubmit - * - * Callback when the form is submit. - * - * @date 4/9/18 - * @since 5.7.5 - * - * @param object e The event object. - * @param jQuery $el The input element. - * @return void - */ - onSubmit: function (e, $el) { - // Allow form to submit if... - if ( // Validation has been disabled. - !this.active // Or this event is to be ignored. - || this.get('ignore') // Or this event has already been prevented. - || e.isDefaultPrevented()) { - // Return early and call reset function. - return this.allowSubmit(); - } // Validate form. - - - var valid = acf.validateForm({ - form: $el, - event: this.get('originalEvent') - }); // If not valid, stop event to prevent form submit. - - if (!valid) { - e.preventDefault(); - } - }, - - /** - * allowSubmit - * - * Resets data during onSubmit when the form is allowed to submit. - * - * @date 5/3/19 - * @since 5.7.13 - * - * @param void - * @return void - */ - allowSubmit: function () { - // Reset "ignore" state. - this.set('ignore', false); // Reset "originalEvent" object. - - this.set('originalEvent', false); // Return true - - return true; - } - }); - var gutenbergValidation = new acf.Model({ - wait: 'prepare', - initialize: function () { - // Bail early if not Gutenberg. - if (!acf.isGutenberg()) { - return; - } // Custommize the editor. - - - this.customizeEditor(); - }, - customizeEditor: function () { - // Extract vars. - var editor = wp.data.dispatch('core/editor'); - var editorSelect = wp.data.select('core/editor'); - var notices = wp.data.dispatch('core/notices'); // Backup original method. - - var savePost = editor.savePost; // Listen for changes to post status and perform actions: - // a) Enable validation for "publish" action. - // b) Remember last non "publish" status used for restoring after validation fail. - - var useValidation = false; - var lastPostStatus = ''; - wp.data.subscribe(function () { - var postStatus = editorSelect.getEditedPostAttribute('status'); - useValidation = postStatus === 'publish'; - lastPostStatus = postStatus !== 'publish' ? postStatus : lastPostStatus; - }); // Create validation version. - - editor.savePost = function (options) { - options = options || {}; // Backup vars. - - var _this = this; - - var _args = arguments; // Perform validation within a Promise. - - return new Promise(function (resolve, reject) { - // Bail early if is autosave or preview. - if (options.isAutosave || options.isPreview) { - return resolve('Validation ignored (autosave).'); - } // Bail early if validation is not neeed. - - - if (!useValidation) { - return resolve('Validation ignored (draft).'); - } // Validate the editor form. - - - var valid = acf.validateForm({ - form: $('#editor'), - reset: true, - complete: function ($form, validator) { - // Always unlock the form after AJAX. - editor.unlockPostSaving('acf'); - }, - failure: function ($form, validator) { - // Get validation error and append to Gutenberg notices. - var notice = validator.get('notice'); - notices.createErrorNotice(notice.get('text'), { - id: 'acf-validation', - isDismissible: true - }); - notice.remove(); // Restore last non "publish" status. - - if (lastPostStatus) { - editor.editPost({ - status: lastPostStatus - }); - } // Rejext promise and prevent savePost(). - - - reject('Validation failed.'); - }, - success: function () { - notices.removeNotice('acf-validation'); // Resolve promise and allow savePost(). - - resolve('Validation success.'); - } - }); // Resolve promise and allow savePost() if no validation is needed. - - if (valid) { - resolve('Validation bypassed.'); // Otherwise, lock the form and wait for AJAX response. - } else { - editor.lockPostSaving('acf'); - } - }).then(function () { - return savePost.apply(_this, _args); - }); - }; - } - }); +(function($, undefined){ + + /** + * Validator + * + * The model for validating forms + * + * @date 4/9/18 + * @since 5.7.5 + * + * @param void + * @return void + */ + var Validator = acf.Model.extend({ + + /** @var string The model identifier. */ + id: 'Validator', + + /** @var object The model data. */ + data: { + + /** @var array The form errors. */ + errors: [], + + /** @var object The form notice. */ + notice: null, + + /** @var string The form status. loading, invalid, valid */ + status: '' + }, + + /** @var object The model events. */ + events: { + 'changed:status': 'onChangeStatus' + }, + + /** + * addErrors + * + * Adds errors to the form. + * + * @date 4/9/18 + * @since 5.7.5 + * + * @param array errors An array of errors. + * @return void + */ + addErrors: function( errors ){ + errors.map( this.addError, this ); + }, + + /** + * addError + * + * Adds and error to the form. + * + * @date 4/9/18 + * @since 5.7.5 + * + * @param object error An error object containing input and message. + * @return void + */ + addError: function( error ){ + this.data.errors.push( error ); + }, + + /** + * hasErrors + * + * Returns true if the form has errors. + * + * @date 4/9/18 + * @since 5.7.5 + * + * @param void + * @return bool + */ + hasErrors: function(){ + return this.data.errors.length; + }, + + /** + * clearErrors + * + * Removes any errors. + * + * @date 4/9/18 + * @since 5.7.5 + * + * @param void + * @return void + */ + clearErrors: function(){ + return this.data.errors = []; + }, + + /** + * getErrors + * + * Returns the forms errors. + * + * @date 4/9/18 + * @since 5.7.5 + * + * @param void + * @return array + */ + getErrors: function(){ + return this.data.errors; + }, + + /** + * getFieldErrors + * + * Returns the forms field errors. + * + * @date 4/9/18 + * @since 5.7.5 + * + * @param void + * @return array + */ + getFieldErrors: function(){ + + // vars + var errors = []; + var inputs = []; + + // loop + this.getErrors().map(function(error){ + + // bail early if global + if( !error.input ) return; + + // update if exists + var i = inputs.indexOf(error.input); + if( i > -1 ) { + errors[ i ] = error; + + // update + } else { + errors.push( error ); + inputs.push( error.input ); + } + }); + + // return + return errors; + }, + + /** + * getGlobalErrors + * + * Returns the forms global errors (errors without a specific input). + * + * @date 4/9/18 + * @since 5.7.5 + * + * @param void + * @return array + */ + getGlobalErrors: function(){ + + // return array of errors that contain no input + return this.getErrors().filter(function(error){ + return !error.input; + }); + }, + + /** + * showErrors + * + * Displays all errors for this form. + * + * @date 4/9/18 + * @since 5.7.5 + * + * @param void + * @return void + */ + showErrors: function(){ + + // bail early if no errors + if( !this.hasErrors() ) { + return; + } + + // vars + var fieldErrors = this.getFieldErrors(); + var globalErrors = this.getGlobalErrors(); + + // vars + var errorCount = 0; + var $scrollTo = false; + + // loop + fieldErrors.map(function( error ){ + + // get input + var $input = this.$('[name="' + error.input + '"]').first(); + + // if $_POST value was an array, this $input may not exist + if( !$input.length ) { + $input = this.$('[name^="' + error.input + '"]').first(); + } + + // bail early if input doesn't exist + if( !$input.length ) { + return; + } + + // increase + errorCount++; + + // get field + var field = acf.getClosestField( $input ); + + // show error + field.showError( error.message ); + + // set $scrollTo + if( !$scrollTo ) { + $scrollTo = field.$el; + } + }, this); + + // errorMessage + var errorMessage = acf.__('Validation failed'); + globalErrors.map(function( error ){ + errorMessage += '. ' + error.message; + }); + if( errorCount == 1 ) { + errorMessage += '. ' + acf.__('1 field requires attention'); + } else if( errorCount > 1 ) { + errorMessage += '. ' + acf.__('%d fields require attention').replace('%d', errorCount); + } + + // notice + if( this.has('notice') ) { + this.get('notice').update({ + type: 'error', + text: errorMessage + }); + } else { + var notice = acf.newNotice({ + type: 'error', + text: errorMessage, + target: this.$el + }); + this.set('notice', notice); + } + + // if no $scrollTo, set to message + if( !$scrollTo ) { + $scrollTo = this.get('notice').$el; + } + + // timeout + setTimeout(function(){ + $("html, body").animate({ scrollTop: $scrollTo.offset().top - ( $(window).height() / 2 ) }, 500); + }, 10); + }, + + /** + * onChangeStatus + * + * Update the form class when changing the 'status' data + * + * @date 4/9/18 + * @since 5.7.5 + * + * @param object e The event object. + * @param jQuery $el The form element. + * @param string value The new status. + * @param string prevValue The old status. + * @return void + */ + onChangeStatus: function( e, $el, value, prevValue ){ + this.$el.removeClass('is-'+prevValue).addClass('is-'+value); + }, + + /** + * validate + * + * Vaildates the form via AJAX. + * + * @date 4/9/18 + * @since 5.7.5 + * + * @param object args A list of settings to customize the validation process. + * @return bool True if the form is valid. + */ + validate: function( args ){ + + // default args + args = acf.parseArgs(args, { + + // trigger event + event: false, + + // reset the form after submit + reset: false, + + // loading callback + loading: function(){}, + + // complete callback + complete: function(){}, + + // failure callback + failure: function(){}, + + // success callback + success: function( $form ){ + $form.submit(); + } + }); + + // return true if is valid - allows form submit + if( this.get('status') == 'valid' ) { + return true; + } + + // return false if is currently validating - prevents form submit + if( this.get('status') == 'validating' ) { + return false; + } + + // return true if no ACF fields exist (no need to validate) + if( !this.$('.acf-field').length ) { + return true; + } + + // if event is provided, create a new success callback. + if( args.event ) { + var event = $.Event(null, args.event); + args.success = function(){ + acf.enableSubmit( $(event.target) ).trigger( event ); + } + } + + // action for 3rd party + acf.doAction('validation_begin', this.$el); + + // lock form + acf.lockForm( this.$el ); + + // loading callback + args.loading( this.$el, this ); + + // update status + this.set('status', 'validating'); + + // success callback + var onSuccess = function( json ){ + + // validate + if( !acf.isAjaxSuccess(json) ) { + return; + } + + // filter + var data = acf.applyFilters('validation_complete', json.data, this.$el, this); + + // add errors + if( !data.valid ) { + this.addErrors( data.errors ); + } + }; + + // complete + var onComplete = function(){ + + // unlock form + acf.unlockForm( this.$el ); + + // failure + if( this.hasErrors() ) { + + // update status + this.set('status', 'invalid'); + + // action + acf.doAction('validation_failure', this.$el, this); + + // display errors + this.showErrors(); + + // failure callback + args.failure( this.$el, this ); + + // success + } else { + + // update status + this.set('status', 'valid'); + + // remove previous error message + if( this.has('notice') ) { + this.get('notice').update({ + type: 'success', + text: acf.__('Validation successful'), + timeout: 1000 + }); + } + + // action + acf.doAction('validation_success', this.$el, this); + acf.doAction('submit', this.$el); + + // success callback (submit form) + args.success( this.$el, this ); + + // lock form + acf.lockForm( this.$el ); + + // reset + if( args.reset ) { + this.reset(); + } + } + + // complete callback + args.complete( this.$el, this ); + + // clear errors + this.clearErrors(); + }; + + // serialize form data + var data = acf.serialize( this.$el ); + data.action = 'acf/validate_save_post'; + + // ajax + $.ajax({ + url: acf.get('ajaxurl'), + data: acf.prepareForAjax(data), + type: 'post', + dataType: 'json', + context: this, + success: onSuccess, + complete: onComplete + }); + + // return false to fail validation and allow AJAX + return false + }, + + /** + * setup + * + * Called during the constructor function to setup this instance + * + * @date 4/9/18 + * @since 5.7.5 + * + * @param jQuery $form The form element. + * @return void + */ + setup: function( $form ){ + + // set $el + this.$el = $form; + }, + + /** + * reset + * + * Rests the validation to be used again. + * + * @date 6/9/18 + * @since 5.7.5 + * + * @param void + * @return void + */ + reset: function(){ + + // reset data + this.set('errors', []); + this.set('notice', null); + this.set('status', ''); + + // unlock form + acf.unlockForm( this.$el ); + } + }); + + /** + * getValidator + * + * Returns the instance for a given form element. + * + * @date 4/9/18 + * @since 5.7.5 + * + * @param jQuery $el The form element. + * @return object + */ + var getValidator = function( $el ){ + + // instantiate + var validator = $el.data('acf'); + if( !validator ) { + validator = new Validator( $el ); + } + + // return + return validator; + }; + + /** + * acf.validateForm + * + * A helper function for the Validator.validate() function. + * Returns true if form is valid, or fetches a validation request and returns false. + * + * @date 4/4/18 + * @since 5.6.9 + * + * @param object args A list of settings to customize the validation process. + * @return bool + */ + + acf.validateForm = function( args ){ + return getValidator( args.form ).validate( args ); + }; + + /** + * acf.enableSubmit + * + * Enables a submit button and returns the element. + * + * @date 30/8/18 + * @since 5.7.4 + * + * @param jQuery $submit The submit button. + * @return jQuery + */ + acf.enableSubmit = function( $submit ){ + return $submit.removeClass('disabled'); + }; + + /** + * acf.disableSubmit + * + * Disables a submit button and returns the element. + * + * @date 30/8/18 + * @since 5.7.4 + * + * @param jQuery $submit The submit button. + * @return jQuery + */ + acf.disableSubmit = function( $submit ){ + return $submit.addClass('disabled'); + }; + + /** + * acf.showSpinner + * + * Shows the spinner element. + * + * @date 4/9/18 + * @since 5.7.5 + * + * @param jQuery $spinner The spinner element. + * @return jQuery + */ + acf.showSpinner = function( $spinner ){ + $spinner.addClass('is-active'); // add class (WP > 4.2) + $spinner.css('display', 'inline-block'); // css (WP < 4.2) + return $spinner; + }; + + /** + * acf.hideSpinner + * + * Hides the spinner element. + * + * @date 4/9/18 + * @since 5.7.5 + * + * @param jQuery $spinner The spinner element. + * @return jQuery + */ + acf.hideSpinner = function( $spinner ){ + $spinner.removeClass('is-active'); // add class (WP > 4.2) + $spinner.css('display', 'none'); // css (WP < 4.2) + return $spinner; + }; + + /** + * acf.lockForm + * + * Locks a form by disabeling its primary inputs and showing a spinner. + * + * @date 4/9/18 + * @since 5.7.5 + * + * @param jQuery $form The form element. + * @return jQuery + */ + acf.lockForm = function( $form ){ + + // vars + var $wrap = findSubmitWrap( $form ); + var $submit = $wrap.find('.button, [type="submit"]'); + var $spinner = $wrap.find('.spinner, .acf-spinner'); + + // hide all spinners (hides the preview spinner) + acf.hideSpinner( $spinner ); + + // lock + acf.disableSubmit( $submit ); + acf.showSpinner( $spinner.last() ); + return $form; + }; + + /** + * acf.unlockForm + * + * Unlocks a form by enabeling its primary inputs and hiding all spinners. + * + * @date 4/9/18 + * @since 5.7.5 + * + * @param jQuery $form The form element. + * @return jQuery + */ + acf.unlockForm = function( $form ){ + + // vars + var $wrap = findSubmitWrap( $form ); + var $submit = $wrap.find('.button, [type="submit"]'); + var $spinner = $wrap.find('.spinner, .acf-spinner'); + + // unlock + acf.enableSubmit( $submit ); + acf.hideSpinner( $spinner ); + return $form; + }; + + /** + * findSubmitWrap + * + * An internal function to find the 'primary' form submit wrapping element. + * + * @date 4/9/18 + * @since 5.7.5 + * + * @param jQuery $form The form element. + * @return jQuery + */ + var findSubmitWrap = function( $form ){ + + // default post submit div + var $wrap = $form.find('#submitdiv'); + if( $wrap.length ) { + return $wrap; + } + + // 3rd party publish box + var $wrap = $form.find('#submitpost'); + if( $wrap.length ) { + return $wrap; + } + + // term, user + var $wrap = $form.find('p.submit').last(); + if( $wrap.length ) { + return $wrap; + } + + // front end form + var $wrap = $form.find('.acf-form-submit'); + if( $wrap.length ) { + return $wrap; + } + + // default + return $form; + }; + + /** + * A debounced function to trigger a form submission. + * + * @date 15/07/2020 + * @since 5.9.0 + * + * @param type Var Description. + * @return type Description. + */ + var submitFormDebounced = acf.debounce(function( $form ){ + $form.submit(); + }); + + /** + * acf.validation + * + * Global validation logic + * + * @date 4/4/18 + * @since 5.6.9 + * + * @param void + * @return void + */ + + acf.validation = new acf.Model({ + + /** @var string The model identifier. */ + id: 'validation', + + /** @var bool The active state. Set to false before 'prepare' to prevent validation. */ + active: true, + + /** @var string The model initialize time. */ + wait: 'prepare', + + /** @var object The model actions. */ + actions: { + 'ready': 'addInputEvents', + 'append': 'addInputEvents' + }, + + /** @var object The model events. */ + events: { + 'click input[type="submit"]': 'onClickSubmit', + 'click button[type="submit"]': 'onClickSubmit', + //'click #editor .editor-post-publish-button': 'onClickSubmitGutenberg', + 'click #save-post': 'onClickSave', + 'submit form#post': 'onSubmitPost', + 'submit form': 'onSubmit', + }, + + /** + * initialize + * + * Called when initializing the model. + * + * @date 4/9/18 + * @since 5.7.5 + * + * @param void + * @return void + */ + initialize: function(){ + + // check 'validation' setting + if( !acf.get('validation') ) { + this.active = false; + this.actions = {}; + this.events = {}; + } + }, + + /** + * enable + * + * Enables validation. + * + * @date 4/9/18 + * @since 5.7.5 + * + * @param void + * @return void + */ + enable: function(){ + this.active = true; + }, + + /** + * disable + * + * Disables validation. + * + * @date 4/9/18 + * @since 5.7.5 + * + * @param void + * @return void + */ + disable: function(){ + this.active = false; + }, + + /** + * reset + * + * Rests the form validation to be used again + * + * @date 6/9/18 + * @since 5.7.5 + * + * @param jQuery $form The form element. + * @return void + */ + reset: function( $form ){ + getValidator( $form ).reset(); + }, + + /** + * addInputEvents + * + * Adds 'invalid' event listeners to HTML inputs. + * + * @date 4/9/18 + * @since 5.7.5 + * + * @param jQuery $el The element being added / readied. + * @return void + */ + addInputEvents: function( $el ){ + + // Bug exists in Safari where custom "invalid" handeling prevents draft from saving. + if( acf.get('browser') === 'safari' ) + return; + + // vars + var $inputs = $('.acf-field [name]', $el); + + // check + if( $inputs.length ) { + this.on( $inputs, 'invalid', 'onInvalid' ); + } + }, + + /** + * onInvalid + * + * Callback for the 'invalid' event. + * + * @date 4/9/18 + * @since 5.7.5 + * + * @param object e The event object. + * @param jQuery $el The input element. + * @return void + */ + onInvalid: function( e, $el ){ + + // prevent default + // - prevents browser error message + // - also fixes chrome bug where 'hidden-by-tab' field throws focus error + e.preventDefault(); + + // vars + var $form = $el.closest('form'); + + // check form exists + if( $form.length ) { + + // add error to validator + getValidator( $form ).addError({ + input: $el.attr('name'), + message: acf.strEscape( e.target.validationMessage ) + }); + + // trigger submit on $form + // - allows for "save", "preview" and "publish" to work + submitFormDebounced( $form ); + } + }, + + /** + * onClickSubmit + * + * Callback when clicking submit. + * + * @date 4/9/18 + * @since 5.7.5 + * + * @param object e The event object. + * @param jQuery $el The input element. + * @return void + */ + onClickSubmit: function( e, $el ){ + + // store the "click event" for later use in this.onSubmit() + this.set('originalEvent', e); + }, + + /** + * onClickSave + * + * Set ignore to true when saving a draft. + * + * @date 4/9/18 + * @since 5.7.5 + * + * @param object e The event object. + * @param jQuery $el The input element. + * @return void + */ + onClickSave: function( e, $el ) { + this.set('ignore', true); + }, + + /** + * onClickSubmitGutenberg + * + * Custom validation event for the gutenberg editor. + * + * @date 29/10/18 + * @since 5.8.0 + * + * @param object e The event object. + * @param jQuery $el The input element. + * @return void + */ + onClickSubmitGutenberg: function( e, $el ){ + + // validate + var valid = acf.validateForm({ + form: $('#editor'), + event: e, + reset: true, + failure: function( $form, validator ){ + var $notice = validator.get('notice').$el; + $notice.appendTo('.components-notice-list'); + $notice.find('.acf-notice-dismiss').removeClass('small'); + } + }); + + // if not valid, stop event and allow validation to continue + if( !valid ) { + e.preventDefault(); + e.stopImmediatePropagation(); + } + }, + + /** + * onSubmitPost + * + * Callback when the 'post' form is submit. + * + * @date 5/3/19 + * @since 5.7.13 + * + * @param object e The event object. + * @param jQuery $el The input element. + * @return void + */ + onSubmitPost: function( e, $el ) { + + // Check if is preview. + if( $('input#wp-preview').val() === 'dopreview' ) { + + // Ignore validation. + this.set('ignore', true); + + // Unlock form to fix conflict with core "submit.edit-post" event causing all submit buttons to be disabled. + acf.unlockForm( $el ) + } + }, + + /** + * onSubmit + * + * Callback when the form is submit. + * + * @date 4/9/18 + * @since 5.7.5 + * + * @param object e The event object. + * @param jQuery $el The input element. + * @return void + */ + onSubmit: function( e, $el ){ + + // Allow form to submit if... + if( + // Validation has been disabled. + !this.active + + // Or this event is to be ignored. + || this.get('ignore') + + // Or this event has already been prevented. + || e.isDefaultPrevented() + ) { + // Return early and call reset function. + return this.allowSubmit(); + } + + // Validate form. + var valid = acf.validateForm({ + form: $el, + event: this.get('originalEvent') + }); + + // If not valid, stop event to prevent form submit. + if( !valid ) { + e.preventDefault(); + } + }, + + /** + * allowSubmit + * + * Resets data during onSubmit when the form is allowed to submit. + * + * @date 5/3/19 + * @since 5.7.13 + * + * @param void + * @return void + */ + allowSubmit: function(){ + + // Reset "ignore" state. + this.set('ignore', false); + + // Reset "originalEvent" object. + this.set('originalEvent', false); + + // Return true + return true; + } + }); + + var gutenbergValidation = new acf.Model({ + wait: 'prepare', + initialize: function(){ + + // Bail early if not Gutenberg. + if( !acf.isGutenberg() ) { + return; + } + + // Custommize the editor. + this.customizeEditor(); + }, + customizeEditor: function(){ + + // Extract vars. + var editor = wp.data.dispatch( 'core/editor' ); + var editorSelect = wp.data.select( 'core/editor' ); + var notices = wp.data.dispatch( 'core/notices' ); + + // Backup original method. + var savePost = editor.savePost; + + // Listen for changes to post status and perform actions: + // a) Enable validation for "publish" action. + // b) Remember last non "publish" status used for restoring after validation fail. + var useValidation = false; + var lastPostStatus = ''; + wp.data.subscribe(function() { + var postStatus = editorSelect.getEditedPostAttribute( 'status' ); + useValidation = ( postStatus === 'publish' ); + lastPostStatus = ( postStatus !== 'publish' ) ? postStatus : lastPostStatus; + }); + + // Create validation version. + editor.savePost = function( options ){ + options = options || {}; + + // Backup vars. + var _this = this; + var _args = arguments; + + // Perform validation within a Promise. + return new Promise(function( resolve, reject ) { + + // Bail early if is autosave or preview. + if( options.isAutosave || options.isPreview ) { + return resolve( 'Validation ignored (autosave).' ); + } + + // Bail early if validation is not neeed. + if( !useValidation ) { + return resolve( 'Validation ignored (draft).' ); + } + + // Validate the editor form. + var valid = acf.validateForm({ + form: $('#editor'), + reset: true, + complete: function( $form, validator ){ + + // Always unlock the form after AJAX. + editor.unlockPostSaving( 'acf' ); + }, + failure: function( $form, validator ){ + + // Get validation error and append to Gutenberg notices. + var notice = validator.get('notice'); + notices.createErrorNotice( notice.get('text'), { + id: 'acf-validation', + isDismissible: true + }); + notice.remove(); + + // Restore last non "publish" status. + if( lastPostStatus ) { + editor.editPost({ + status: lastPostStatus + }); + } + + // Rejext promise and prevent savePost(). + reject( 'Validation failed.' ); + }, + success: function(){ + notices.removeNotice( 'acf-validation' ); + + // Resolve promise and allow savePost(). + resolve( 'Validation success.' ); + } + }); + + // Resolve promise and allow savePost() if no validation is needed. + if( valid ) { + resolve( 'Validation bypassed.' ); + + // Otherwise, lock the form and wait for AJAX response. + } else { + editor.lockPostSaving( 'acf' ); + } + }).then(function(){ + return savePost.apply(_this, _args); + }); + }; + } + }); + })(jQuery); +(function($, undefined){ + + /** + * refreshHelper + * + * description + * + * @date 1/7/18 + * @since 5.6.9 + * + * @param type $var Description. Default. + * @return type Description. + */ + + var refreshHelper = new acf.Model({ + priority: 90, + actions: { + 'new_field': 'refresh', + 'show_field': 'refresh', + 'hide_field': 'refresh', + 'remove_field': 'refresh', + 'unmount_field': 'refresh', + 'remount_field': 'refresh', + }, + refresh: function(){ + acf.refresh(); + } + }); + + /** + * mountHelper + * + * Adds compatiblity for the 'unmount' and 'remount' actions added in 5.8.0 + * + * @date 7/3/19 + * @since 5.7.14 + * + * @param void + * @return void + */ + var mountHelper = new acf.Model({ + priority: 1, + actions: { + 'sortstart': 'onSortstart', + 'sortstop': 'onSortstop' + }, + onSortstart: function( $item ){ + acf.doAction('unmount', $item); + }, + onSortstop: function( $item ){ + acf.doAction('remount', $item); + } + }); + + /** + * sortableHelper + * + * Adds compatibility for sorting a

            element + * + * @date 6/3/18 + * @since 5.6.9 + * + * @param void + * @return void + */ + + var sortableHelper = new acf.Model({ + actions: { + 'sortstart': 'onSortstart' + }, + onSortstart: function( $item, $placeholder ){ + + // if $item is a tr, apply some css to the elements + if( $item.is('tr') ) { + + // replace $placeholder children with a single td + // fixes "width calculation issues" due to conditional logic hiding some children + $placeholder.html(''); + + // add helper class to remove absolute positioning + $item.addClass('acf-sortable-tr-helper'); + + // set fixed widths for children + $item.children().each(function(){ + $(this).width( $(this).width() ); + }); + + // mimic height + $placeholder.height( $item.height() + 'px' ); + + // remove class + $item.removeClass('acf-sortable-tr-helper'); + } + } + }); + + /** + * duplicateHelper + * + * Fixes browser bugs when duplicating an element + * + * @date 6/3/18 + * @since 5.6.9 + * + * @param void + * @return void + */ + + var duplicateHelper = new acf.Model({ + actions: { + 'after_duplicate': 'onAfterDuplicate' + }, + onAfterDuplicate: function( $el, $el2 ){ + + // get original values + var vals = []; + $el.find('select').each(function(i){ + vals.push( $(this).val() ); + }); + + // set duplicate values + $el2.find('select').each(function(i){ + $(this).val( vals[i] ); + }); + } + }); + + /** + * tableHelper + * + * description + * + * @date 6/3/18 + * @since 5.6.9 + * + * @param type $var Description. Default. + * @return type Description. + */ + + var tableHelper = new acf.Model({ + + id: 'tableHelper', + + priority: 20, + + actions: { + 'refresh': 'renderTables' + }, + + renderTables: function( $el ){ + + // loop + var self = this; + $('.acf-table:visible').each(function(){ + self.renderTable( $(this) ); + }); + }, + + renderTable: function( $table ){ + + // vars + var $ths = $table.find('> thead > tr:visible > th[data-key]'); + var $tds = $table.find('> tbody > tr:visible > td[data-key]'); + + // bail early if no thead + if( !$ths.length || !$tds.length ) { + return false; + } + + + // visiblity + $ths.each(function( i ){ + + // vars + var $th = $(this); + var key = $th.data('key'); + var $cells = $tds.filter('[data-key="' + key + '"]'); + var $hidden = $cells.filter('.acf-hidden'); + + // always remove empty and allow cells to be hidden + $cells.removeClass('acf-empty'); + + // hide $th if all cells are hidden + if( $cells.length === $hidden.length ) { + acf.hide( $th ); + + // force all hidden cells to appear empty + } else { + acf.show( $th ); + $hidden.addClass('acf-empty'); + } + }); + + + // clear width + $ths.css('width', 'auto'); + + // get visible + $ths = $ths.not('.acf-hidden'); + + // vars + var availableWidth = 100; + var colspan = $ths.length; + + // set custom widths first + var $fixedWidths = $ths.filter('[data-width]'); + $fixedWidths.each(function(){ + var width = $(this).data('width'); + $(this).css('width', width + '%'); + availableWidth -= width; + }); + + // set auto widths + var $auoWidths = $ths.not('[data-width]'); + if( $auoWidths.length ) { + var width = availableWidth / $auoWidths.length; + $auoWidths.css('width', width + '%'); + availableWidth = 0; + } + + // avoid stretching issue + if( availableWidth > 0 ) { + $ths.last().css('width', 'auto'); + } + + + // update colspan on collapsed + $tds.filter('.-collapsed-target').each(function(){ + + // vars + var $td = $(this); + + // check if collapsed + if( $td.parent().hasClass('-collapsed') ) { + $td.attr('colspan', $ths.length); + } else { + $td.removeAttr('colspan'); + } + }); + } + }); + + + /** + * fieldsHelper + * + * description + * + * @date 6/3/18 + * @since 5.6.9 + * + * @param type $var Description. Default. + * @return type Description. + */ + + var fieldsHelper = new acf.Model({ + + id: 'fieldsHelper', + + priority: 30, + + actions: { + 'refresh': 'renderGroups' + }, + + renderGroups: function(){ + + // loop + var self = this; + $('.acf-fields:visible').each(function(){ + self.renderGroup( $(this) ); + }); + }, + + renderGroup: function( $el ){ + + // vars + var top = 0; + var height = 0; + var $row = $(); + + // get fields + var $fields = $el.children('.acf-field[data-width]:visible'); + + // bail early if no fields + if( !$fields.length ) { + return false; + } + + // bail ealry if is .-left + if( $el.hasClass('-left') ) { + $fields.removeAttr('data-width'); + $fields.css('width', 'auto'); + return false; + } + + // reset fields + $fields.removeClass('-r0 -c0').css({'min-height': 0}); + + // loop + $fields.each(function( i ){ + + // vars + var $field = $(this); + var position = $field.position(); + var thisTop = Math.ceil( position.top ); + var thisLeft = Math.ceil( position.left ); + + // detect change in row + if( $row.length && thisTop > top ) { -(function ($, undefined) { - /** - * refreshHelper - * - * description - * - * @date 1/7/18 - * @since 5.6.9 - * - * @param type $var Description. Default. - * @return type Description. - */ - var refreshHelper = new acf.Model({ - priority: 90, - actions: { - 'new_field': 'refresh', - 'show_field': 'refresh', - 'hide_field': 'refresh', - 'remove_field': 'refresh', - 'unmount_field': 'refresh', - 'remount_field': 'refresh' - }, - refresh: function () { - acf.refresh(); - } - }); - /** - * mountHelper - * - * Adds compatiblity for the 'unmount' and 'remount' actions added in 5.8.0 - * - * @date 7/3/19 - * @since 5.7.14 - * - * @param void - * @return void - */ + // set previous heights + $row.css({'min-height': height+'px'}); + + // update position due to change in row above + position = $field.position(); + thisTop = Math.ceil( position.top ); + thisLeft = Math.ceil( position.left ); + + // reset vars + top = 0; + height = 0; + $row = $(); + } + + // rtl + if( acf.get('rtl') ) { + thisLeft = Math.ceil( $field.parent().width() - (position.left + $field.outerWidth()) ); + } + + // add classes + if( thisTop == 0 ) { + $field.addClass('-r0'); + } else if( thisLeft == 0 ) { + $field.addClass('-c0'); + } + + // get height after class change + // - add 1 for subpixel rendering + var thisHeight = Math.ceil( $field.outerHeight() ) + 1; + + // set height + height = Math.max( height, thisHeight ); + + // set y + top = Math.max( top, thisTop ); + + // append + $row = $row.add( $field ); + }); + + // clean up + if( $row.length ) { + $row.css({'min-height': height+'px'}); + } + } + }); - var mountHelper = new acf.Model({ - priority: 1, - actions: { - 'sortstart': 'onSortstart', - 'sortstop': 'onSortstop' - }, - onSortstart: function ($item) { - acf.doAction('unmount', $item); - }, - onSortstop: function ($item) { - acf.doAction('remount', $item); - } - }); - /** - * sortableHelper - * - * Adds compatibility for sorting a element - * - * @date 6/3/18 - * @since 5.6.9 - * - * @param void - * @return void - */ - - var sortableHelper = new acf.Model({ - actions: { - 'sortstart': 'onSortstart' - }, - onSortstart: function ($item, $placeholder) { - // if $item is a tr, apply some css to the elements - if ($item.is('tr')) { - // replace $placeholder children with a single td - // fixes "width calculation issues" due to conditional logic hiding some children - $placeholder.html(''); // add helper class to remove absolute positioning - - $item.addClass('acf-sortable-tr-helper'); // set fixed widths for children - - $item.children().each(function () { - $(this).width($(this).width()); - }); // mimic height - - $placeholder.height($item.height() + 'px'); // remove class - - $item.removeClass('acf-sortable-tr-helper'); - } - } - }); - /** - * duplicateHelper - * - * Fixes browser bugs when duplicating an element - * - * @date 6/3/18 - * @since 5.6.9 - * - * @param void - * @return void - */ - - var duplicateHelper = new acf.Model({ - actions: { - 'after_duplicate': 'onAfterDuplicate' - }, - onAfterDuplicate: function ($el, $el2) { - // get original values - var vals = []; - $el.find('select').each(function (i) { - vals.push($(this).val()); - }); // set duplicate values - - $el2.find('select').each(function (i) { - $(this).val(vals[i]); - }); - } - }); - /** - * tableHelper - * - * description - * - * @date 6/3/18 - * @since 5.6.9 - * - * @param type $var Description. Default. - * @return type Description. - */ - - var tableHelper = new acf.Model({ - id: 'tableHelper', - priority: 20, - actions: { - 'refresh': 'renderTables' - }, - renderTables: function ($el) { - // loop - var self = this; - $('.acf-table:visible').each(function () { - self.renderTable($(this)); - }); - }, - renderTable: function ($table) { - // vars - var $ths = $table.find('> thead > tr:visible > th[data-key]'); - var $tds = $table.find('> tbody > tr:visible > td[data-key]'); // bail early if no thead - - if (!$ths.length || !$tds.length) { - return false; - } // visiblity - - - $ths.each(function (i) { - // vars - var $th = $(this); - var key = $th.data('key'); - var $cells = $tds.filter('[data-key="' + key + '"]'); - var $hidden = $cells.filter('.acf-hidden'); // always remove empty and allow cells to be hidden - - $cells.removeClass('acf-empty'); // hide $th if all cells are hidden - - if ($cells.length === $hidden.length) { - acf.hide($th); // force all hidden cells to appear empty - } else { - acf.show($th); - $hidden.addClass('acf-empty'); - } - }); // clear width - - $ths.css('width', 'auto'); // get visible - - $ths = $ths.not('.acf-hidden'); // vars - - var availableWidth = 100; - var colspan = $ths.length; // set custom widths first - - var $fixedWidths = $ths.filter('[data-width]'); - $fixedWidths.each(function () { - var width = $(this).data('width'); - $(this).css('width', width + '%'); - availableWidth -= width; - }); // set auto widths - - var $auoWidths = $ths.not('[data-width]'); - - if ($auoWidths.length) { - var width = availableWidth / $auoWidths.length; - $auoWidths.css('width', width + '%'); - availableWidth = 0; - } // avoid stretching issue - - - if (availableWidth > 0) { - $ths.last().css('width', 'auto'); - } // update colspan on collapsed - - - $tds.filter('.-collapsed-target').each(function () { - // vars - var $td = $(this); // check if collapsed - - if ($td.parent().hasClass('-collapsed')) { - $td.attr('colspan', $ths.length); - } else { - $td.removeAttr('colspan'); - } - }); - } - }); - /** - * fieldsHelper - * - * description - * - * @date 6/3/18 - * @since 5.6.9 - * - * @param type $var Description. Default. - * @return type Description. - */ - - var fieldsHelper = new acf.Model({ - id: 'fieldsHelper', - priority: 30, - actions: { - 'refresh': 'renderGroups' - }, - renderGroups: function () { - // loop - var self = this; - $('.acf-fields:visible').each(function () { - self.renderGroup($(this)); - }); - }, - renderGroup: function ($el) { - // vars - var top = 0; - var height = 0; - var $row = $(); // get fields - - var $fields = $el.children('.acf-field[data-width]:visible'); // bail early if no fields - - if (!$fields.length) { - return false; - } // bail ealry if is .-left - - - if ($el.hasClass('-left')) { - $fields.removeAttr('data-width'); - $fields.css('width', 'auto'); - return false; - } // reset fields - - - $fields.removeClass('-r0 -c0').css({ - 'min-height': 0 - }); // loop - - $fields.each(function (i) { - // vars - var $field = $(this); - var position = $field.position(); - var thisTop = Math.ceil(position.top); - var thisLeft = Math.ceil(position.left); // detect change in row - - if ($row.length && thisTop > top) { - // set previous heights - $row.css({ - 'min-height': height + 'px' - }); // update position due to change in row above - - position = $field.position(); - thisTop = Math.ceil(position.top); - thisLeft = Math.ceil(position.left); // reset vars - - top = 0; - height = 0; - $row = $(); - } // rtl - - - if (acf.get('rtl')) { - thisLeft = Math.ceil($field.parent().width() - (position.left + $field.outerWidth())); - } // add classes - - - if (thisTop == 0) { - $field.addClass('-r0'); - } else if (thisLeft == 0) { - $field.addClass('-c0'); - } // get height after class change - // - add 1 for subpixel rendering - - - var thisHeight = Math.ceil($field.outerHeight()) + 1; // set height - - height = Math.max(height, thisHeight); // set y - - top = Math.max(top, thisTop); // append - - $row = $row.add($field); - }); // clean up - - if ($row.length) { - $row.css({ - 'min-height': height + 'px' - }); - } - } - }); - /** - * Adds a body class when holding down the "shift" key. - * - * @date 06/05/2020 - * @since 5.9.0 - */ - - var bodyClassShiftHelper = new acf.Model({ - id: 'bodyClassShiftHelper', - events: { - 'keydown': 'onKeyDown', - 'keyup': 'onKeyUp' - }, - isShiftKey: function (e) { - return e.keyCode === 16; - }, - onKeyDown: function (e) { - if (this.isShiftKey(e)) { - $('body').addClass('acf-keydown-shift'); - } - }, - onKeyUp: function (e) { - if (this.isShiftKey(e)) { - $('body').removeClass('acf-keydown-shift'); - } - } - }); + /** + * Adds a body class when holding down the "shift" key. + * + * @date 06/05/2020 + * @since 5.9.0 + */ + var bodyClassShiftHelper = new acf.Model({ + id: 'bodyClassShiftHelper', + events: { + 'keydown': 'onKeyDown', + 'keyup': 'onKeyUp' + }, + isShiftKey: function( e ){ + return ( e.keyCode === 16 ); + }, + onKeyDown: function( e ){ + if( this.isShiftKey(e) ) { + $('body').addClass('acf-keydown-shift'); + } + }, + onKeyUp: function( e ){ + if( this.isShiftKey(e) ) { + $('body').removeClass('acf-keydown-shift'); + } + }, + }); + })(jQuery); - -(function ($, undefined) { - /** - * acf.newCompatibility - * - * Inserts a new __proto__ object compatibility layer - * - * @date 15/2/18 - * @since 5.6.9 - * - * @param object instance The object to modify. - * @param object compatibilty Optional. The compatibilty layer. - * @return object compatibilty - */ - acf.newCompatibility = function (instance, compatibilty) { - // defaults - compatibilty = compatibilty || {}; // inherit __proto_- - - compatibilty.__proto__ = instance.__proto__; // inject - - instance.__proto__ = compatibilty; // reference - - instance.compatibility = compatibilty; // return - - return compatibilty; - }; - /** - * acf.getCompatibility - * - * Returns the compatibility layer for a given instance - * - * @date 13/3/18 - * @since 5.6.9 - * - * @param object instance The object to look in. - * @return object|null compatibility The compatibility object or null on failure. - */ - - - acf.getCompatibility = function (instance) { - return instance.compatibility || null; - }; - /** - * acf (compatibility) - * - * Compatibility layer for the acf object - * - * @date 15/2/18 - * @since 5.6.9 - * - * @param void - * @return void - */ - - - var _acf = acf.newCompatibility(acf, { - // storage - l10n: {}, - o: {}, - fields: {}, - // changed function names - update: acf.set, - add_action: acf.addAction, - remove_action: acf.removeAction, - do_action: acf.doAction, - add_filter: acf.addFilter, - remove_filter: acf.removeFilter, - apply_filters: acf.applyFilters, - parse_args: acf.parseArgs, - disable_el: acf.disable, - disable_form: acf.disable, - enable_el: acf.enable, - enable_form: acf.enable, - update_user_setting: acf.updateUserSetting, - prepare_for_ajax: acf.prepareForAjax, - is_ajax_success: acf.isAjaxSuccess, - remove_el: acf.remove, - remove_tr: acf.remove, - str_replace: acf.strReplace, - render_select: acf.renderSelect, - get_uniqid: acf.uniqid, - serialize_form: acf.serialize, - esc_html: acf.strEscape, - str_sanitize: acf.strSanitize - }); - - _acf._e = function (k1, k2) { - // defaults - k1 = k1 || ''; - k2 = k2 || ''; // compability - - var compatKey = k2 ? k1 + '.' + k2 : k1; - var compats = { - 'image.select': 'Select Image', - 'image.edit': 'Edit Image', - 'image.update': 'Update Image' - }; - - if (compats[compatKey]) { - return acf.__(compats[compatKey]); - } // try k1 - - - var string = this.l10n[k1] || ''; // try k2 - - if (k2) { - string = string[k2] || ''; - } // return - - - return string; - }; - - _acf.get_selector = function (s) { - // vars - var selector = '.acf-field'; // bail early if no search - - if (!s) { - return selector; - } // compatibility with object - - - if ($.isPlainObject(s)) { - if ($.isEmptyObject(s)) { - return selector; - } else { - for (var k in s) { - s = s[k]; - break; - } - } - } // append - - - selector += '-' + s; // replace underscores (split/join replaces all and is faster than regex!) - - selector = acf.strReplace('_', '-', selector); // remove potential double up - - selector = acf.strReplace('field-field-', 'field-', selector); // return - - return selector; - }; - - _acf.get_fields = function (s, $el, all) { - // args - var args = { - is: s || '', - parent: $el || false, - suppressFilters: all || false - }; // change 'field_123' to '.acf-field-123' - - if (args.is) { - args.is = this.get_selector(args.is); - } // return - - - return acf.findFields(args); - }; - - _acf.get_field = function (s, $el) { - // get fields - var $fields = this.get_fields.apply(this, arguments); // return - - if ($fields.length) { - return $fields.first(); - } else { - return false; - } - }; - - _acf.get_closest_field = function ($el, s) { - return $el.closest(this.get_selector(s)); - }; - - _acf.get_field_wrap = function ($el) { - return $el.closest(this.get_selector()); - }; - - _acf.get_field_key = function ($field) { - return $field.data('key'); - }; - - _acf.get_field_type = function ($field) { - return $field.data('type'); - }; - - _acf.get_data = function ($el, defaults) { - return acf.parseArgs($el.data(), defaults); - }; - - _acf.maybe_get = function (obj, key, value) { - // default - if (value === undefined) { - value = null; - } // get keys - - - keys = String(key).split('.'); // acf.isget - - for (var i = 0; i < keys.length; i++) { - if (!obj.hasOwnProperty(keys[i])) { - return value; - } - - obj = obj[keys[i]]; - } - - return obj; - }; - /** - * hooks - * - * Modify add_action and add_filter functions to add compatibility with changed $field parameter - * Using the acf.add_action() or acf.add_filter() functions will interpret new field parameters as jQuery $field - * - * @date 12/5/18 - * @since 5.6.9 - * - * @param void - * @return void - */ - - - var compatibleArgument = function (arg) { - return arg instanceof acf.Field ? arg.$el : arg; - }; - - var compatibleArguments = function (args) { - return acf.arrayArgs(args).map(compatibleArgument); - }; - - var compatibleCallback = function (origCallback) { - return function () { - // convert to compatible arguments - if (arguments.length) { - var args = compatibleArguments(arguments); // add default argument for 'ready', 'append' and 'load' events - } else { - var args = [$(document)]; - } // return - - - return origCallback.apply(this, args); - }; - }; - - _acf.add_action = function (action, callback, priority, context) { - // handle multiple actions - var actions = action.split(' '); - var length = actions.length; - - if (length > 1) { - for (var i = 0; i < length; i++) { - action = actions[i]; - - _acf.add_action.apply(this, arguments); - } - - return this; - } // single - - - var callback = compatibleCallback(callback); - return acf.addAction.apply(this, arguments); - }; - - _acf.add_filter = function (action, callback, priority, context) { - var callback = compatibleCallback(callback); - return acf.addFilter.apply(this, arguments); - }; - /* - * acf.model - * - * This model acts as a scafold for action.event driven modules - * - * @type object - * @date 8/09/2014 - * @since 5.0.0 - * - * @param (object) - * @return (object) - */ - - - _acf.model = { - actions: {}, - filters: {}, - events: {}, - extend: function (args) { - // extend - var model = $.extend({}, this, args); // setup actions - - $.each(model.actions, function (name, callback) { - model._add_action(name, callback); - }); // setup filters - - $.each(model.filters, function (name, callback) { - model._add_filter(name, callback); - }); // setup events - - $.each(model.events, function (name, callback) { - model._add_event(name, callback); - }); // return - - return model; - }, - _add_action: function (name, callback) { - // split - var model = this, - data = name.split(' '); // add missing priority - - var name = data[0] || '', - priority = data[1] || 10; // add action - - acf.add_action(name, model[callback], priority, model); - }, - _add_filter: function (name, callback) { - // split - var model = this, - data = name.split(' '); // add missing priority - - var name = data[0] || '', - priority = data[1] || 10; // add action - - acf.add_filter(name, model[callback], priority, model); - }, - _add_event: function (name, callback) { - // vars - var model = this, - i = name.indexOf(' '), - event = i > 0 ? name.substr(0, i) : name, - selector = i > 0 ? name.substr(i + 1) : ''; // event - - var fn = function (e) { - // append $el to event object - e.$el = $(this); // append $field to event object (used in field group) - - if (acf.field_group) { - e.$field = e.$el.closest('.acf-field-object'); - } // event - - - if (typeof model.event === 'function') { - e = model.event(e); - } // callback - - - model[callback].apply(model, arguments); - }; // add event - - - if (selector) { - $(document).on(event, selector, fn); - } else { - $(document).on(event, fn); - } - }, - get: function (name, value) { - // defaults - value = value || null; // get - - if (typeof this[name] !== 'undefined') { - value = this[name]; - } // return - - - return value; - }, - set: function (name, value) { - // set - this[name] = value; // function for 3rd party - - if (typeof this['_set_' + name] === 'function') { - this['_set_' + name].apply(this); - } // return for chaining - - - return this; - } - }; - /* - * field - * - * This model sets up many of the field's interactions - * - * @type function - * @date 21/02/2014 - * @since 3.5.1 - * - * @param n/a - * @return n/a - */ - - _acf.field = acf.model.extend({ - type: '', - o: {}, - $field: null, - _add_action: function (name, callback) { - // vars - var model = this; // update name - - name = name + '_field/type=' + model.type; // add action - - acf.add_action(name, function ($field) { - // focus - model.set('$field', $field); // callback - - model[callback].apply(model, arguments); - }); - }, - _add_filter: function (name, callback) { - // vars - var model = this; // update name - - name = name + '_field/type=' + model.type; // add action - - acf.add_filter(name, function ($field) { - // focus - model.set('$field', $field); // callback - - model[callback].apply(model, arguments); - }); - }, - _add_event: function (name, callback) { - // vars - var model = this, - event = name.substr(0, name.indexOf(' ')), - selector = name.substr(name.indexOf(' ') + 1), - context = acf.get_selector(model.type); // add event - - $(document).on(event, context + ' ' + selector, function (e) { - // vars - var $el = $(this); - var $field = acf.get_closest_field($el, model.type); // bail early if no field - - if (!$field.length) return; // focus - - if (!$field.is(model.$field)) { - model.set('$field', $field); - } // append to event - - - e.$el = $el; - e.$field = $field; // callback - - model[callback].apply(model, [e]); - }); - }, - _set_$field: function () { - // callback - if (typeof this.focus === 'function') { - this.focus(); - } - }, - // depreciated - doFocus: function ($field) { - return this.set('$field', $field); - } - }); - /** - * validation - * - * description - * - * @date 15/2/18 - * @since 5.6.9 - * - * @param type $var Description. Default. - * @return type Description. - */ - - var _validation = acf.newCompatibility(acf.validation, { - remove_error: function ($field) { - acf.getField($field).removeError(); - }, - add_warning: function ($field, message) { - acf.getField($field).showNotice({ - text: message, - type: 'warning', - timeout: 1000 - }); - }, - fetch: acf.validateForm, - enableSubmit: acf.enableSubmit, - disableSubmit: acf.disableSubmit, - showSpinner: acf.showSpinner, - hideSpinner: acf.hideSpinner, - unlockForm: acf.unlockForm, - lockForm: acf.lockForm - }); - /** - * tooltip - * - * description - * - * @date 15/2/18 - * @since 5.6.9 - * - * @param type $var Description. Default. - * @return type Description. - */ - - - _acf.tooltip = { - tooltip: function (text, $el) { - var tooltip = acf.newTooltip({ - text: text, - target: $el - }); // return - - return tooltip.$el; - }, - temp: function (text, $el) { - var tooltip = acf.newTooltip({ - text: text, - target: $el, - timeout: 250 - }); - }, - confirm: function ($el, callback, text, button_y, button_n) { - var tooltip = acf.newTooltip({ - confirm: true, - text: text, - target: $el, - confirm: function () { - callback(true); - }, - cancel: function () { - callback(false); - } - }); - }, - confirm_remove: function ($el, callback) { - var tooltip = acf.newTooltip({ - confirmRemove: true, - target: $el, - confirm: function () { - callback(true); - }, - cancel: function () { - callback(false); - } - }); - } - }; - /** - * tooltip - * - * description - * - * @date 15/2/18 - * @since 5.6.9 - * - * @param type $var Description. Default. - * @return type Description. - */ - - _acf.media = new acf.Model({ - activeFrame: false, - actions: { - 'new_media_popup': 'onNewMediaPopup' - }, - frame: function () { - return this.activeFrame; - }, - onNewMediaPopup: function (popup) { - this.activeFrame = popup.frame; - }, - popup: function (props) { - // update props - if (props.mime_types) { - props.allowedTypes = props.mime_types; - } - - if (props.id) { - props.attachment = props.id; - } // new - - - var popup = acf.newMediaPopup(props); // append - - /* - if( props.selected ) { - popup.selected = props.selected; - } - */ - // return - - return popup.frame; - } - }); - /** - * Select2 - * - * description - * - * @date 11/6/18 - * @since 5.6.9 - * - * @param type $var Description. Default. - * @return type Description. - */ - - _acf.select2 = { - init: function ($select, args, $field) { - // compatible args - if (args.allow_null) { - args.allowNull = args.allow_null; - } - - if (args.ajax_action) { - args.ajaxAction = args.ajax_action; - } - - if ($field) { - args.field = acf.getField($field); - } // return - - - return acf.newSelect2($select, args); - }, - destroy: function ($select) { - return acf.getInstance($select).destroy(); - } - }; - /** - * postbox - * - * description - * - * @date 11/6/18 - * @since 5.6.9 - * - * @param type $var Description. Default. - * @return type Description. - */ - - _acf.postbox = { - render: function (args) { - // compatible args - if (args.edit_url) { - args.editLink = args.edit_url; - } - - if (args.edit_title) { - args.editTitle = args.edit_title; - } // return - - - return acf.newPostbox(args); - } - }; - /** - * acf.screen - * - * description - * - * @date 11/6/18 - * @since 5.6.9 - * - * @param type $var Description. Default. - * @return type Description. - */ - - acf.newCompatibility(acf.screen, { - update: function () { - return this.set.apply(this, arguments); - }, - fetch: acf.screen.check - }); - _acf.ajax = acf.screen; +(function($, undefined){ + + /** + * acf.newCompatibility + * + * Inserts a new __proto__ object compatibility layer + * + * @date 15/2/18 + * @since 5.6.9 + * + * @param object instance The object to modify. + * @param object compatibilty Optional. The compatibilty layer. + * @return object compatibilty + */ + + acf.newCompatibility = function( instance, compatibilty ){ + + // defaults + compatibilty = compatibilty || {}; + + // inherit __proto_- + compatibilty.__proto__ = instance.__proto__; + + // inject + instance.__proto__ = compatibilty; + + // reference + instance.compatibility = compatibilty; + + // return + return compatibilty; + }; + + /** + * acf.getCompatibility + * + * Returns the compatibility layer for a given instance + * + * @date 13/3/18 + * @since 5.6.9 + * + * @param object instance The object to look in. + * @return object|null compatibility The compatibility object or null on failure. + */ + + acf.getCompatibility = function( instance ) { + return instance.compatibility || null; + }; + + /** + * acf (compatibility) + * + * Compatibility layer for the acf object + * + * @date 15/2/18 + * @since 5.6.9 + * + * @param void + * @return void + */ + + var _acf = acf.newCompatibility(acf, { + + // storage + l10n: {}, + o: {}, + fields: {}, + + // changed function names + update: acf.set, + add_action: acf.addAction, + remove_action: acf.removeAction, + do_action: acf.doAction, + add_filter: acf.addFilter, + remove_filter: acf.removeFilter, + apply_filters: acf.applyFilters, + parse_args: acf.parseArgs, + disable_el: acf.disable, + disable_form: acf.disable, + enable_el: acf.enable, + enable_form: acf.enable, + update_user_setting: acf.updateUserSetting, + prepare_for_ajax: acf.prepareForAjax, + is_ajax_success: acf.isAjaxSuccess, + remove_el: acf.remove, + remove_tr: acf.remove, + str_replace: acf.strReplace, + render_select: acf.renderSelect, + get_uniqid: acf.uniqid, + serialize_form: acf.serialize, + esc_html: acf.strEscape, + str_sanitize: acf.strSanitize, + + }); + + _acf._e = function( k1, k2 ){ + + // defaults + k1 = k1 || ''; + k2 = k2 || ''; + + // compability + var compatKey = k2 ? k1 + '.' + k2 : k1; + var compats = { + 'image.select': 'Select Image', + 'image.edit': 'Edit Image', + 'image.update': 'Update Image' + }; + if( compats[compatKey] ) { + return acf.__(compats[compatKey]); + } + + // try k1 + var string = this.l10n[ k1 ] || ''; + + // try k2 + if( k2 ) { + string = string[ k2 ] || ''; + } + + // return + return string; + }; + + _acf.get_selector = function( s ) { + + // vars + var selector = '.acf-field'; + + // bail early if no search + if( !s ) { + return selector; + } + + // compatibility with object + if( $.isPlainObject(s) ) { + if( $.isEmptyObject(s) ) { + return selector; + } else { + for( var k in s ) { s = s[k]; break; } + } + } + + // append + selector += '-' + s; + + // replace underscores (split/join replaces all and is faster than regex!) + selector = acf.strReplace('_', '-', selector); + + // remove potential double up + selector = acf.strReplace('field-field-', 'field-', selector); + + // return + return selector; + }; + + _acf.get_fields = function( s, $el, all ){ + + // args + var args = { + is: s || '', + parent: $el || false, + suppressFilters: all || false, + }; + + // change 'field_123' to '.acf-field-123' + if( args.is ) { + args.is = this.get_selector( args.is ); + } + + // return + return acf.findFields(args); + }; + + _acf.get_field = function( s, $el ){ + + // get fields + var $fields = this.get_fields.apply(this, arguments); + + // return + if( $fields.length ) { + return $fields.first(); + } else { + return false; + } + }; + + _acf.get_closest_field = function( $el, s ){ + return $el.closest( this.get_selector(s) ); + }; + + _acf.get_field_wrap = function( $el ){ + return $el.closest( this.get_selector() ); + }; + + _acf.get_field_key = function( $field ){ + return $field.data('key'); + }; + + _acf.get_field_type = function( $field ){ + return $field.data('type'); + }; + + _acf.get_data = function( $el, defaults ){ + return acf.parseArgs( $el.data(), defaults ); + }; + + _acf.maybe_get = function( obj, key, value ){ + + // default + if( value === undefined ) { + value = null; + } + + // get keys + keys = String(key).split('.'); + + // acf.isget + for( var i = 0; i < keys.length; i++ ) { + if( !obj.hasOwnProperty(keys[i]) ) { + return value; + } + obj = obj[ keys[i] ]; + } + return obj; + }; + + + /** + * hooks + * + * Modify add_action and add_filter functions to add compatibility with changed $field parameter + * Using the acf.add_action() or acf.add_filter() functions will interpret new field parameters as jQuery $field + * + * @date 12/5/18 + * @since 5.6.9 + * + * @param void + * @return void + */ + + var compatibleArgument = function( arg ){ + return ( arg instanceof acf.Field ) ? arg.$el : arg; + }; + + var compatibleArguments = function( args ){ + return acf.arrayArgs( args ).map( compatibleArgument ); + } + + var compatibleCallback = function( origCallback ){ + return function(){ + + // convert to compatible arguments + if( arguments.length ) { + var args = compatibleArguments(arguments); + + // add default argument for 'ready', 'append' and 'load' events + } else { + var args = [ $(document) ]; + } + + // return + return origCallback.apply(this, args); + } + } + + _acf.add_action = function( action, callback, priority, context ){ + + // handle multiple actions + var actions = action.split(' '); + var length = actions.length; + if( length > 1 ) { + for( var i = 0; i < length; i++) { + action = actions[i]; + _acf.add_action.apply(this, arguments); + } + return this; + } + + // single + var callback = compatibleCallback(callback); + return acf.addAction.apply(this, arguments); + }; + + _acf.add_filter = function( action, callback, priority, context ){ + var callback = compatibleCallback(callback); + return acf.addFilter.apply(this, arguments); + }; + + /* + * acf.model + * + * This model acts as a scafold for action.event driven modules + * + * @type object + * @date 8/09/2014 + * @since 5.0.0 + * + * @param (object) + * @return (object) + */ + + _acf.model = { + actions: {}, + filters: {}, + events: {}, + extend: function( args ){ + + // extend + var model = $.extend( {}, this, args ); + + // setup actions + $.each(model.actions, function( name, callback ){ + model._add_action( name, callback ); + }); + + // setup filters + $.each(model.filters, function( name, callback ){ + model._add_filter( name, callback ); + }); + + // setup events + $.each(model.events, function( name, callback ){ + model._add_event( name, callback ); + }); + + // return + return model; + }, + + _add_action: function( name, callback ) { + + // split + var model = this, + data = name.split(' '); + + // add missing priority + var name = data[0] || '', + priority = data[1] || 10; + + // add action + acf.add_action(name, model[ callback ], priority, model); + + }, + + _add_filter: function( name, callback ) { + + // split + var model = this, + data = name.split(' '); + + // add missing priority + var name = data[0] || '', + priority = data[1] || 10; + + // add action + acf.add_filter(name, model[ callback ], priority, model); + }, + + _add_event: function( name, callback ) { + + // vars + var model = this, + i = name.indexOf(' '), + event = (i > 0) ? name.substr(0,i) : name, + selector = (i > 0) ? name.substr(i+1) : ''; + + // event + var fn = function( e ){ + + // append $el to event object + e.$el = $(this); + + // append $field to event object (used in field group) + if( acf.field_group ) { + e.$field = e.$el.closest('.acf-field-object'); + } + + // event + if( typeof model.event === 'function' ) { + e = model.event( e ); + } + + // callback + model[ callback ].apply(model, arguments); + + }; + + // add event + if( selector ) { + $(document).on(event, selector, fn); + } else { + $(document).on(event, fn); + } + }, + + get: function( name, value ){ + + // defaults + value = value || null; + + // get + if( typeof this[ name ] !== 'undefined' ) { + value = this[ name ]; + } + + // return + return value; + }, + + set: function( name, value ){ + + // set + this[ name ] = value; + + // function for 3rd party + if( typeof this[ '_set_' + name ] === 'function' ) { + this[ '_set_' + name ].apply(this); + } + + // return for chaining + return this; + } + }; + + /* + * field + * + * This model sets up many of the field's interactions + * + * @type function + * @date 21/02/2014 + * @since 3.5.1 + * + * @param n/a + * @return n/a + */ + + _acf.field = acf.model.extend({ + type: '', + o: {}, + $field: null, + _add_action: function( name, callback ) { + + // vars + var model = this; + + // update name + name = name + '_field/type=' + model.type; + + // add action + acf.add_action(name, function( $field ){ + + // focus + model.set('$field', $field); + + // callback + model[ callback ].apply(model, arguments); + }); + }, + + _add_filter: function( name, callback ) { + + // vars + var model = this; + + // update name + name = name + '_field/type=' + model.type; + + // add action + acf.add_filter(name, function( $field ){ + + // focus + model.set('$field', $field); + + // callback + model[ callback ].apply(model, arguments); + }); + }, + + _add_event: function( name, callback ) { + + // vars + var model = this, + event = name.substr(0,name.indexOf(' ')), + selector = name.substr(name.indexOf(' ')+1), + context = acf.get_selector(model.type); + + // add event + $(document).on(event, context + ' ' + selector, function( e ){ + + // vars + var $el = $(this); + var $field = acf.get_closest_field( $el, model.type ); + + // bail early if no field + if( !$field.length ) return; + + // focus + if( !$field.is(model.$field) ) { + model.set('$field', $field); + } + + // append to event + e.$el = $el; + e.$field = $field; + + // callback + model[ callback ].apply(model, [e]); + }); + }, + + _set_$field: function(){ + + // callback + if( typeof this.focus === 'function' ) { + this.focus(); + } + }, + + // depreciated + doFocus: function( $field ){ + return this.set('$field', $field); + } + }); + + + /** + * validation + * + * description + * + * @date 15/2/18 + * @since 5.6.9 + * + * @param type $var Description. Default. + * @return type Description. + */ + + var _validation = acf.newCompatibility(acf.validation, { + remove_error: function( $field ){ + acf.getField( $field ).removeError(); + }, + add_warning: function( $field, message ){ + acf.getField( $field ).showNotice({ + text: message, + type: 'warning', + timeout: 1000 + }); + }, + fetch: acf.validateForm, + enableSubmit: acf.enableSubmit, + disableSubmit: acf.disableSubmit, + showSpinner: acf.showSpinner, + hideSpinner: acf.hideSpinner, + unlockForm: acf.unlockForm, + lockForm: acf.lockForm + }); + + + /** + * tooltip + * + * description + * + * @date 15/2/18 + * @since 5.6.9 + * + * @param type $var Description. Default. + * @return type Description. + */ + + _acf.tooltip = { + + tooltip: function( text, $el ){ + + var tooltip = acf.newTooltip({ + text: text, + target: $el + }); + + // return + return tooltip.$el; + }, + + temp: function( text, $el ){ + + var tooltip = acf.newTooltip({ + text: text, + target: $el, + timeout: 250 + }); + }, + + confirm: function( $el, callback, text, button_y, button_n ){ + + var tooltip = acf.newTooltip({ + confirm: true, + text: text, + target: $el, + confirm: function(){ + callback(true); + }, + cancel: function(){ + callback(false); + } + }); + }, + + confirm_remove: function( $el, callback ){ + + var tooltip = acf.newTooltip({ + confirmRemove: true, + target: $el, + confirm: function(){ + callback(true); + }, + cancel: function(){ + callback(false); + } + }); + }, + }; + + /** + * tooltip + * + * description + * + * @date 15/2/18 + * @since 5.6.9 + * + * @param type $var Description. Default. + * @return type Description. + */ + + _acf.media = new acf.Model({ + activeFrame: false, + actions: { + 'new_media_popup': 'onNewMediaPopup' + }, + + frame: function(){ + return this.activeFrame; + }, + + onNewMediaPopup: function( popup ){ + this.activeFrame = popup.frame; + }, + + popup: function( props ){ + + // update props + if( props.mime_types ) { + props.allowedTypes = props.mime_types; + } + if( props.id ) { + props.attachment = props.id; + } + + // new + var popup = acf.newMediaPopup( props ); + + // append +/* + if( props.selected ) { + popup.selected = props.selected; + } +*/ + + // return + return popup.frame; + } + }); + + + /** + * Select2 + * + * description + * + * @date 11/6/18 + * @since 5.6.9 + * + * @param type $var Description. Default. + * @return type Description. + */ + + _acf.select2 = { + init: function( $select, args, $field ){ + + // compatible args + if( args.allow_null ) { + args.allowNull = args.allow_null; + } + if( args.ajax_action ) { + args.ajaxAction = args.ajax_action; + } + if( $field ) { + args.field = acf.getField($field); + } + + // return + return acf.newSelect2( $select, args ); + }, + + destroy: function( $select ){ + return acf.getInstance( $select ).destroy(); + + }, + }; + + /** + * postbox + * + * description + * + * @date 11/6/18 + * @since 5.6.9 + * + * @param type $var Description. Default. + * @return type Description. + */ + + _acf.postbox = { + render: function( args ){ + + // compatible args + if( args.edit_url ) { + args.editLink = args.edit_url; + } + if( args.edit_title ) { + args.editTitle = args.edit_title; + } + + // return + return acf.newPostbox( args ); + } + }; + + /** + * acf.screen + * + * description + * + * @date 11/6/18 + * @since 5.6.9 + * + * @param type $var Description. Default. + * @return type Description. + */ + + acf.newCompatibility(acf.screen, { + update: function(){ + return this.set.apply(this, arguments); + }, + fetch: acf.screen.check + }); + _acf.ajax = acf.screen; + })(jQuery); \ No newline at end of file diff --git a/assets/build/js/acf.js b/assets/build/js/acf.js index 7ed74e0..e1f08e9 100644 --- a/assets/build/js/acf.js +++ b/assets/build/js/acf.js @@ -1,2263 +1,2375 @@ -(function ($, undefined) { - /** - * acf - * - * description - * - * @date 14/12/17 - * @since 5.6.5 - * - * @param type $var Description. Default. - * @return type Description. - */ - // The global acf object - var acf = {}; // Set as a browser global - - window.acf = acf; - /** @var object Data sent from PHP */ - - acf.data = {}; - /** - * get - * - * Gets a specific data value - * - * @date 14/12/17 - * @since 5.6.5 - * - * @param string name - * @return mixed - */ - - acf.get = function (name) { - return this.data[name] || null; - }; - /** - * has - * - * Returns `true` if the data exists and is not null - * - * @date 14/12/17 - * @since 5.6.5 - * - * @param string name - * @return boolean - */ - - - acf.has = function (name) { - return this.get(name) !== null; - }; - /** - * set - * - * Sets a specific data value - * - * @date 14/12/17 - * @since 5.6.5 - * - * @param string name - * @param mixed value - * @return this - */ - - - acf.set = function (name, value) { - this.data[name] = value; - return this; - }; - /** - * uniqueId - * - * Returns a unique ID - * - * @date 9/11/17 - * @since 5.6.3 - * - * @param string prefix Optional prefix. - * @return string - */ - - - var idCounter = 0; - - acf.uniqueId = function (prefix) { - var id = ++idCounter + ''; - return prefix ? prefix + id : id; - }; - /** - * acf.uniqueArray - * - * Returns a new array with only unique values - * Credit: https://stackoverflow.com/questions/1960473/get-all-unique-values-in-an-array-remove-duplicates - * - * @date 23/3/18 - * @since 5.6.9 - * - * @param type $var Description. Default. - * @return type Description. - */ - - - acf.uniqueArray = function (array) { - function onlyUnique(value, index, self) { - return self.indexOf(value) === index; - } - - return array.filter(onlyUnique); - }; - /** - * uniqid - * - * Returns a unique ID (PHP version) - * - * @date 9/11/17 - * @since 5.6.3 - * @source http://locutus.io/php/misc/uniqid/ - * - * @param string prefix Optional prefix. - * @return string - */ - - - var uniqidSeed = ''; - - acf.uniqid = function (prefix, moreEntropy) { - // discuss at: http://locutus.io/php/uniqid/ - // original by: Kevin van Zonneveld (http://kvz.io) - // revised by: Kankrelune (http://www.webfaktory.info/) - // note 1: Uses an internal counter (in locutus global) to avoid collision - // example 1: var $id = uniqid() - // example 1: var $result = $id.length === 13 - // returns 1: true - // example 2: var $id = uniqid('foo') - // example 2: var $result = $id.length === (13 + 'foo'.length) - // returns 2: true - // example 3: var $id = uniqid('bar', true) - // example 3: var $result = $id.length === (23 + 'bar'.length) - // returns 3: true - if (typeof prefix === 'undefined') { - prefix = ''; - } - - var retId; - - var formatSeed = function (seed, reqWidth) { - seed = parseInt(seed, 10).toString(16); // to hex str - - if (reqWidth < seed.length) { - // so long we split - return seed.slice(seed.length - reqWidth); - } - - if (reqWidth > seed.length) { - // so short we pad - return Array(1 + (reqWidth - seed.length)).join('0') + seed; - } - - return seed; - }; - - if (!uniqidSeed) { - // init seed with big random int - uniqidSeed = Math.floor(Math.random() * 0x75bcd15); - } - - uniqidSeed++; - retId = prefix; // start with prefix, add current milliseconds hex string - - retId += formatSeed(parseInt(new Date().getTime() / 1000, 10), 8); - retId += formatSeed(uniqidSeed, 5); // add seed hex string - - if (moreEntropy) { - // for more entropy we add a float lower to 10 - retId += (Math.random() * 10).toFixed(8).toString(); - } - - return retId; - }; - /** - * strReplace - * - * Performs a string replace - * - * @date 14/12/17 - * @since 5.6.5 - * - * @param string search - * @param string replace - * @param string subject - * @return string - */ - - - acf.strReplace = function (search, replace, subject) { - return subject.split(search).join(replace); - }; - /** - * strCamelCase - * - * Converts a string into camelCase - * Thanks to https://stackoverflow.com/questions/2970525/converting-any-string-into-camel-case - * - * @date 14/12/17 - * @since 5.6.5 - * - * @param string str - * @return string - */ - - - acf.strCamelCase = function (str) { - var matches = str.match(/([a-zA-Z0-9]+)/g); - return matches ? matches.map(function (s, i) { - var c = s.charAt(0); - return (i === 0 ? c.toLowerCase() : c.toUpperCase()) + s.slice(1); - }).join('') : ''; - }; - /** - * strPascalCase - * - * Converts a string into PascalCase - * Thanks to https://stackoverflow.com/questions/1026069/how-do-i-make-the-first-letter-of-a-string-uppercase-in-javascript - * - * @date 14/12/17 - * @since 5.6.5 - * - * @param string str - * @return string - */ - - - acf.strPascalCase = function (str) { - var camel = acf.strCamelCase(str); - return camel.charAt(0).toUpperCase() + camel.slice(1); - }; - /** - * acf.strSlugify - * - * Converts a string into a HTML class friendly slug - * - * @date 21/3/18 - * @since 5.6.9 - * - * @param string str - * @return string - */ - - - acf.strSlugify = function (str) { - return acf.strReplace('_', '-', str.toLowerCase()); - }; - - acf.strSanitize = function (str) { - // chars (https://jsperf.com/replace-foreign-characters) - var map = { - "À": "A", - "Á": "A", - "Â": "A", - "Ã": "A", - "Ä": "A", - "Å": "A", - "Æ": "AE", - "Ç": "C", - "È": "E", - "É": "E", - "Ê": "E", - "Ë": "E", - "Ì": "I", - "Í": "I", - "Î": "I", - "Ï": "I", - "Ð": "D", - "Ñ": "N", - "Ò": "O", - "Ó": "O", - "Ô": "O", - "Õ": "O", - "Ö": "O", - "Ø": "O", - "Ù": "U", - "Ú": "U", - "Û": "U", - "Ü": "U", - "Ý": "Y", - "ß": "s", - "à": "a", - "á": "a", - "â": "a", - "ã": "a", - "ä": "a", - "å": "a", - "æ": "ae", - "ç": "c", - "è": "e", - "é": "e", - "ê": "e", - "ë": "e", - "ì": "i", - "í": "i", - "î": "i", - "ï": "i", - "ñ": "n", - "ò": "o", - "ó": "o", - "ô": "o", - "õ": "o", - "ö": "o", - "ø": "o", - "ù": "u", - "ú": "u", - "û": "u", - "ü": "u", - "ý": "y", - "ÿ": "y", - "Ā": "A", - "ā": "a", - "Ă": "A", - "ă": "a", - "Ą": "A", - "ą": "a", - "Ć": "C", - "ć": "c", - "Ĉ": "C", - "ĉ": "c", - "Ċ": "C", - "ċ": "c", - "Č": "C", - "č": "c", - "Ď": "D", - "ď": "d", - "Đ": "D", - "đ": "d", - "Ē": "E", - "ē": "e", - "Ĕ": "E", - "ĕ": "e", - "Ė": "E", - "ė": "e", - "Ę": "E", - "ę": "e", - "Ě": "E", - "ě": "e", - "Ĝ": "G", - "ĝ": "g", - "Ğ": "G", - "ğ": "g", - "Ġ": "G", - "ġ": "g", - "Ģ": "G", - "ģ": "g", - "Ĥ": "H", - "ĥ": "h", - "Ħ": "H", - "ħ": "h", - "Ĩ": "I", - "ĩ": "i", - "Ī": "I", - "ī": "i", - "Ĭ": "I", - "ĭ": "i", - "Į": "I", - "į": "i", - "İ": "I", - "ı": "i", - "IJ": "IJ", - "ij": "ij", - "Ĵ": "J", - "ĵ": "j", - "Ķ": "K", - "ķ": "k", - "Ĺ": "L", - "ĺ": "l", - "Ļ": "L", - "ļ": "l", - "Ľ": "L", - "ľ": "l", - "Ŀ": "L", - "ŀ": "l", - "Ł": "l", - "ł": "l", - "Ń": "N", - "ń": "n", - "Ņ": "N", - "ņ": "n", - "Ň": "N", - "ň": "n", - "ʼn": "n", - "Ō": "O", - "ō": "o", - "Ŏ": "O", - "ŏ": "o", - "Ő": "O", - "ő": "o", - "Œ": "OE", - "œ": "oe", - "Ŕ": "R", - "ŕ": "r", - "Ŗ": "R", - "ŗ": "r", - "Ř": "R", - "ř": "r", - "Ś": "S", - "ś": "s", - "Ŝ": "S", - "ŝ": "s", - "Ş": "S", - "ş": "s", - "Š": "S", - "š": "s", - "Ţ": "T", - "ţ": "t", - "Ť": "T", - "ť": "t", - "Ŧ": "T", - "ŧ": "t", - "Ũ": "U", - "ũ": "u", - "Ū": "U", - "ū": "u", - "Ŭ": "U", - "ŭ": "u", - "Ů": "U", - "ů": "u", - "Ű": "U", - "ű": "u", - "Ų": "U", - "ų": "u", - "Ŵ": "W", - "ŵ": "w", - "Ŷ": "Y", - "ŷ": "y", - "Ÿ": "Y", - "Ź": "Z", - "ź": "z", - "Ż": "Z", - "ż": "z", - "Ž": "Z", - "ž": "z", - "ſ": "s", - "ƒ": "f", - "Ơ": "O", - "ơ": "o", - "Ư": "U", - "ư": "u", - "Ǎ": "A", - "ǎ": "a", - "Ǐ": "I", - "ǐ": "i", - "Ǒ": "O", - "ǒ": "o", - "Ǔ": "U", - "ǔ": "u", - "Ǖ": "U", - "ǖ": "u", - "Ǘ": "U", - "ǘ": "u", - "Ǚ": "U", - "ǚ": "u", - "Ǜ": "U", - "ǜ": "u", - "Ǻ": "A", - "ǻ": "a", - "Ǽ": "AE", - "ǽ": "ae", - "Ǿ": "O", - "ǿ": "o", - // extra - ' ': '_', - "'": '', - '?': '', - '/': '', - '\\': '', - '.': '', - ',': '', - '`': '', - '>': '', - '<': '', - '"': '', - '[': '', - ']': '', - '|': '', - '{': '', - '}': '', - '(': '', - ')': '' - }; // vars - - var nonWord = /\W/g; - - var mapping = function (c) { - return map[c] !== undefined ? map[c] : c; - }; // replace - - - str = str.replace(nonWord, mapping); // lowercase - - str = str.toLowerCase(); // return - - return str; - }; - /** - * acf.strMatch - * - * Returns the number of characters that match between two strings - * - * @date 1/2/18 - * @since 5.6.5 - * - * @param type $var Description. Default. - * @return type Description. - */ - - - acf.strMatch = function (s1, s2) { - // vars - var val = 0; - var min = Math.min(s1.length, s2.length); // loop - - for (var i = 0; i < min; i++) { - if (s1[i] !== s2[i]) { - break; - } - - val++; - } // return - - - return val; - }; - /** - * Escapes HTML entities from a string. - * - * @date 08/06/2020 - * @since 5.9.0 - * - * @param string string The input string. - * @return string - */ - - - acf.strEscape = function (string) { - var htmlEscapes = { - '&': '&', - '<': '<', - '>': '>', - '"': '"', - "'": ''' - }; - return ('' + string).replace(/[&<>"']/g, function (chr) { - return htmlEscapes[chr]; - }); - }; // Tests. - //console.log( acf.strEscape('Test 1') ); - //console.log( acf.strEscape('Test & 1') ); - //console.log( acf.strEscape('Test\'s & 1') ); - //console.log( acf.strEscape('') ); - - /** - * Unescapes HTML entities from a string. - * - * @date 08/06/2020 - * @since 5.9.0 - * - * @param string string The input string. - * @return string - */ - - - acf.strUnescape = function (string) { - var htmlUnescapes = { - '&': '&', - '<': '<', - '>': '>', - '"': '"', - ''': "'" - }; - return ('' + string).replace(/&|<|>|"|'/g, function (entity) { - return htmlUnescapes[entity]; - }); - }; // Tests. - //console.log( acf.strUnescape( acf.strEscape('Test 1') ) ); - //console.log( acf.strUnescape( acf.strEscape('Test & 1') ) ); - //console.log( acf.strUnescape( acf.strEscape('Test\'s & 1') ) ); - //console.log( acf.strUnescape( acf.strEscape('') ) ); - - /** - * Escapes HTML entities from a string. - * - * @date 08/06/2020 - * @since 5.9.0 - * - * @param string string The input string. - * @return string - */ - - - acf.escAttr = acf.strEscape; - /** - * Encodes ') ); - //console.log( acf.escHtml( acf.strEscape('') ) ); - //console.log( acf.escHtml( '' ) ); - - /** - * acf.decode - * - * description - * - * @date 13/1/18 - * @since 5.6.5 - * - * @param type $var Description. Default. - * @return type Description. - */ - - - acf.decode = function (string) { - return $('