aristath/kirki

View on GitHub
packages/kirki-framework/control-base/src/dynamic-control.js

Summary

Maintainability
B
5 hrs
Test Coverage
/**
 * The majority of the code in this file
 * is derived from the wp-customize-posts plugin
 * and the work of @westonruter to whom I am very grateful.
 *
 * @see https://github.com/xwp/wp-customize-posts
 */

(function () {
    'use strict';

    /**
     * A dynamic color-alpha control.
     *
     * @class
     * @augments wp.customize.Control
     * @augments wp.customize.Class
     */
    wp.customize.kirkiDynamicControl = wp.customize.Control.extend({

        initialize: function (id, options) {
            var control = this,
                args = options || {};

            args.params = args.params || {};
            if (!args.params.type) {
                args.params.type = 'kirki-generic';
            }

            let className;

            if (args.content) {
                let splits = args.content.split('class="');
                splits = splits[1].split('"');
                className = splits[0];
            } else {
                className = 'customize-control customize-control-' + args.params.type;
            }

            if (!args.params.wrapper_attrs && args.params.wrapper_atts) {
                args.params.wrapper_attrs = args.params.wrapper_atts;
            }

            // Hijack the container to add wrapper_attrs.
      args.params.content = jQuery("<li></li>");
            args.params.content.attr('id', 'customize-control-' + id.replace(/]/g, '').replace(/\[/g, '-'));
            args.params.content.attr('class', className);

            _.each(args.params.wrapper_attrs, function (val, key) {
                if ('class' === key) {
                    val = val.replace('{default_class}', className);
                }

                args.params.content.attr(key, val);
            });

            control.propertyElements = [];
            wp.customize.Control.prototype.initialize.call(control, id, args);
            wp.hooks.doAction('kirki.dynamicControl.init.after', id, control, args);
        },

        /**
         * Add bidirectional data binding links between inputs and the setting(s).
         *
         * This is copied from wp.customize.Control.prototype.initialize(). It
         * should be changed in Core to be applied once the control is embedded.
         *
         * @private
         * @returns {void}
         */
        _setUpSettingRootLinks: function () {
            var control = this,
                nodes = control.container.find('[data-customize-setting-link]');

            nodes.each(function () {
                var node = jQuery(this);

                wp.customize(node.data('customizeSettingLink'), function (setting) {
                    var element = new wp.customize.Element(node);
                    control.elements.push(element);
                    element.sync(setting);
                    element.set(setting());
                });
            });
        },

        /**
         * Add bidirectional data binding links between inputs and the setting properties.
         *
         * @private
         * @returns {void}
         */
        _setUpSettingPropertyLinks: function () {
            var control = this,
                nodes;

            if (!control.setting) {
                return;
            }

            nodes = control.container.find('[data-customize-setting-property-link]');

            nodes.each(function () {
                var node = jQuery(this),
                    element,
                    propertyName = node.data('customizeSettingPropertyLink');

                element = new wp.customize.Element(node);
                control.propertyElements.push(element);
                element.set(control.setting()[propertyName]);

                element.bind(function (newPropertyValue) {
                    var newSetting = control.setting();
                    if (newPropertyValue === newSetting[propertyName]) {
                        return;
                    }
                    newSetting = _.clone(newSetting);
                    newSetting[propertyName] = newPropertyValue;
                    control.setting.set(newSetting);
                });
                control.setting.bind(function (newValue) {
                    if (newValue[propertyName] !== element.get()) {
                        element.set(newValue[propertyName]);
                    }
                });
            });
        },

        /**
         * @inheritdoc
         */
        ready: function () {
            var control = this;

            control._setUpSettingRootLinks();
            control._setUpSettingPropertyLinks();

            wp.customize.Control.prototype.ready.call(control);

            control.deferred.embedded.done(function () {
                control.initKirkiControl();
                wp.hooks.doAction('kirki.dynamicControl.ready.deferred.embedded.done', control);
            });
            wp.hooks.doAction('kirki.dynamicControl.ready.after', control);
        },

        /**
         * Embed the control in the document.
         *
         * Override the embed() method to do nothing,
         * so that the control isn't embedded on load,
         * unless the containing section is already expanded.
         *
         * @returns {void}
         */
        embed: function () {
            var control = this,
                sectionId = control.section();

            if (!sectionId) {
                return;
            }

            wp.customize.section(sectionId, function (section) {
                if ('kirki-expanded' === section.params.type || section.expanded() || wp.customize.settings.autofocus.control === control.id) {
                    control.actuallyEmbed();
                } else {
                    section.expanded.bind(function (expanded) {
                        if (expanded) {
                            control.actuallyEmbed();
                        }
                    });
                }
            });
            wp.hooks.doAction('kirki.dynamicControl.embed.after', control);
        },

        /**
         * Deferred embedding of control when actually
         *
         * This function is called in Section.onChangeExpanded() so the control
         * will only get embedded when the Section is first expanded.
         *
         * @returns {void}
         */
        actuallyEmbed: function () {
            var control = this;
            if ('resolved' === control.deferred.embedded.state()) {
                return;
            }
            control.renderContent();
            control.deferred.embedded.resolve(); // This triggers control.ready().
            wp.hooks.doAction('kirki.dynamicControl.actuallyEmbed.after', control);
        },

        /**
         * This is not working with autofocus.
         *
         * @param {object} [args] Args.
         * @returns {void}
         */
        focus: function (args) {
            var control = this;
            control.actuallyEmbed();
            wp.customize.Control.prototype.focus.call(control, args);
            wp.hooks.doAction('kirki.dynamicControl.focus.after', control);
        },

        /**
         * Additional actions that run on ready.
         *
         * @param {object} control - The control object. If undefined fallsback to the this.
         * @returns {void}
         */
        initKirkiControl: function (control) {
            control = control || this;
            wp.hooks.doAction('kirki.dynamicControl.initKirkiControl', this);

            // Save the value
            control.container.on('change keyup paste click', 'input', function () {
                control.setting.set(jQuery(this).val());
            });
        }
    });
}());

(function (api) {

    /**
     * Set the value and trigger all bound callbacks.
     *
     * @since 1.0
     * @param {object} to - New value.
     * @returns {Object} - this
     */
    api.Value.prototype.set = function (to) {
        var from = this._value,
            parentSetting,
            newVal;

        to = this._setter.apply(this, arguments);
        to = this.validate(to);

        // Bail if the sanitized value is null or unchanged.
        if (null === to || _.isEqual(from, to)) {
            return this;
        }

        /**
         * Start Kirki mod.
         */
        if (this.id && api.control(this.id) && api.control(this.id).params && api.control(this.id).params.parent_setting) {
            parentSetting = api.control(this.id).params.parent_setting;
            newVal = {};
            newVal[this.id.replace(parentSetting + '[', '').replace(']', '')] = to;
            api.control(parentSetting).setting.set(jQuery.extend({}, api.control(parentSetting).setting._value, newVal));
        }

        /**
         * End Kirki mod.
         */

        this._value = to;
        this._dirty = true;

        this.callbacks.fireWith(this, [to, from]);

        return this;
    };

    /**
     * Get the value.
     *
     * @returns {mixed} - Returns the value.
     */
    api.Value.prototype.get = function () {

        // Start Kirki mod.
        var parentSetting;
        if (this.id && api.control(this.id) && api.control(this.id).params && api.control(this.id).params.parent_setting) {
            parentSetting = api.control(this.id).params.parent_setting;
            return api.control(parentSetting).setting.get()[this.id.replace(parentSetting + '[', '').replace(']', '')];
        }
        // End Kirki mod.

        return this._value;
    };
}(wp.customize));