Adding a sortable/draggable multi-checkbox to WP Customizer
<h2>What this is about</h2>

<p>I found <a href="http://scottfennell.org/2015/05/28/adding-a-draggablesortable-multi-checkbox-control-to-the-wordpress-customizer/" rel="nofollow">this post</a> by @scottfennell about adding a sortable/draggable multi-checkbox control to the WP Customizer. 
His post was inspired by the <a href="http://justintadlock.com/archives/2015/05/26/multiple-checkbox-customizer-control" rel="nofollow">previous work</a> of @JustinTadlock which is about adding a customizer control for multi checkbox.</p>

<p>I would like to use the sortable/draggable multi-checkbox control in my customizer but I have problems on implementing it. Your help is very welcome and much appreciated. I think this plugin could be useful to others too, so I hope this encounters some positive interest. </p>

<p><strong>Edit: Find sample code below</strong></p>

<h2>What I have done so far</h2>

<ol>
<li>Create a file with the code for the class (myclass.php)</li>
<li>Create a file with the code for the jquery plugin (customizer-drag-boxes.js)</li>
<li><p>Include a file "customizer.php" in my theme and add the following code:</p>

<pre><code>function customizer_setup( $wp_customize ) {
// Include the class
require_once( get_template_directory() . '/inc/functions/admin/myclass.php' );

// Add a section
$wp_customize-&gt;add_section( 'example_section', array(
'title'          =&gt; __( 'Example Section', 'mytheme' ),
'priority'       =&gt; 35,
) );

// Add settings
$wp_customize-&gt;add_setting(
    'example_settings',
    array(
        'default'           =&gt; array( 'apple', 'orange' )
    )
);
// Add control  
$wp_customize-&gt;add_control(
    new LXB_AF_Checkbox(
        $wp_customize,
        'example_settings',
        array(
            'section'   =&gt; 'example_section',
            'label'     =&gt; __( 'My new Control', 'mytheme' ),
            'choices' =&gt; array(
                'apple'      =&gt; __( 'Apple',      'mytheme' ),
                'banana'     =&gt; __( 'Banana',     'mytheme' ),
                'date'       =&gt; __( 'Date',       'mytheme' ),
                'orange'     =&gt; __( 'Orange',     'mytheme' ),
                'watermelon' =&gt; __( 'Watermelon', 'mytheme' )
            )
        )
    )
);
}
add_action( 'customize_register', 'customizer_setup' );
</code></pre></li>
<li><p>Add this to myclass.php after <code>public $type = 'checkbox_group';</code></p>

<pre><code>public function enqueue() {
wp_enqueue_script( 'customizer-drag-boxes-control', get_template_directory_uri() . '/inc/js/customizer-drag-boxes.js', array( 'jquery' ) );
}
</code></pre>

<p>I did this because I think the jquery file was not yet enqueued.</p></li>
<li><p>I changed in myclass.php 
<code>$class = LXB_AF . '-checkbox_group-hidden';</code> to <code>$class = 'LXB_AF' . '-checkbox_group-hidden';</code>. I did this beacuse of an error due to missing <code>''</code>.</p></li>
</ol>

<h2>The result</h2>

<p>The customizer screen shows my section and the test field with the default values. The draggable list does not appear, I get no wordpress errors. In my console I get this error message:</p>

<blockquote>
  <p>Uncaught SyntaxError: Unexpected token u in JSON at position 0</p>
  
  <ul>
  <li>create @ customizer-drag-boxes.js?ver=4.5.2:197 </li>
  <li><p>(anonymous function) @ customizer-drag-boxes.js?ver=4.5.2:327</p></li>
  <li><p>n.extend.each @ load-scripts.php?c=0&amp;load[]=jquery-core,jquery-migrate,jquery-ui-core,jquery-ui-widget,jquery-ui-mo…:2</p></li>
  <li><p>n.fn.n.each @ load-scripts.php?c=0&amp;load[]=jquery-core,jquery-migrate,jquery-ui-core,jquery-ui-widget,jquery-ui-mo…:2</p></li>
  <li><p>$.fn.lxbAfCheckboxGroup @ customizer-drag-boxes.js?ver=4.5.2:316</p></li>
  <li><p>(anonymous function) @ customizer-drag-boxes.js?ver=4.5.2:9</p></li>
  <li><p>n.event.dispatch @ load-scripts.php?c=0&amp;load[]=jquery-core,jquery-migrate,jquery-ui-core,jquery-ui-widget,jquery-ui-mo…:3</p></li>
  <li>r.handle @ load-scripts.php?c=0&amp;load[]=jquery-core,jquery-migrate,jquery-ui-core,jquery-ui-widget,jquery-ui-mo…:3</li>
  </ul>
</blockquote>

<p>Can you help me with implementation? Do you see any flaws in the code? Thanks in advance! :)</p>

<h2>Sample code</h2>

<p><strong>The class</strong></p>

<pre><code>class LXB_AF_Checkbox extends WP_Customize_Control {

    // I'm not sure where this gets used but it's in the codex.
    public $type = 'checkbox_group';

    /**
     * This method must be named, exactly, 'render_content', as that's
     * expected by the WP core cusotmization class, which it extends.  It
     * outputs the HTML for our custom input.
     * 
     * In this case, all it does is output a hidden input with some
     * important data attributes.  Our JS reads the data attributes and
     * creates the markup. The reason for this is because the customizer 
     * only updates via JS, so we might as well create it via JS, since the 
     * logic for creating and updating is very similar.
     */
    public function render_content() {

        $out = '';

        $class = LXB_AF . '-checkbox_group-hidden';
        $class = " class='$class' ";
        $id    = esc_attr( $this -&gt; id );
        $name  = " name='$id' ";
        $label = esc_html( $this -&gt; label );

        $value = '';
        if( isset( $this -&gt; value ) ) {
            $value = $this -&gt; value;
        }

        // WP uses this to create the value of the input.
        $link = 'data-customize-setting-link="' . $id . '"';

        /**
         * Store the choices for this setting in a data attr. In our JS,
         * we'll use these to make the checkboxes.
         */
        $choices      = $this -&gt; choices;
        $choices_data = esc_attr( json_encode( $choices ) );
        $choices_data = " data-choices='$choices_data' ";

        // The type of input.  Text for debugging purposes.  Switch to hidden for production.
        $type = " 'type='text' ";

        $out .= "&lt;span class='customize-control-title'&gt;$label&lt;/span&gt;";
        $out .= "&lt;input $choices_data $link $type $name $class&gt;";

        echo $out;

    }

}
</code></pre>

<p><strong>jQuery plugin</strong></p>

<pre><code>/**
 * A jQuery plugin for making a sortable/draggable multi-checkbox.
 */
jQuery( window ).load(function() {

    // The li that wraps this whole section of the customizer. Sort of like a fieldset.
    var el = jQuery( '.customize-control-checkbox_group' );

    jQuery( el ).lxbAfCheckboxGroup();

});

(function ( $ ) {

    $.fn.lxbAfCheckboxGroup = function( options ) {

        /**
         * Create an HTML &lt;select&gt; menu for choosing some value related to this checkbox.
         * 
         * @param  {string}  type         What type of options we want: linkCategories, customMenu, etc...
         * @param  {boolean} visible      Should the menu be visible?  If the checkbox is not checked, the select menu should not be visible.
         * @param  {string}  hiddenValArr The JSON currently being stored for this fieldset.
         * @return {string}  The HTML for a &lt;select&gt; menu.
         */
        function dropdown( type, visible, hiddenValArr ) {

            // Create a select menu.
            var select = jQuery( '&lt;select&gt;' ).addClass( 'lxbAfCheckboxGroup-select' ).attr( 'data-type', type );

            // Maybe hide it.
            if( ! visible ) {
                jQuery( select ).hide();
            }

            // Start it with an empty option.
            var emptyOpt = jQuery( '&lt;option&gt;' ).val( '' ).appendTo( select );

            // Grab our php variables.
            var localize = lxbAfCustomizeLocalize;

            // Grab the current value for this menu.
            var current = '';
            if( hiddenValArr != null ) {

                //Is the checkbox for this item checked?
                if( hiddenValArr[ '###' + type + '###' ] != null ) {

                    // If so, determine which option is selected.
                    var current = hiddenValArr[ '###' + type + '###' ];
                }
            }

            // Get a list of &lt;option&gt;'s for the &lt;select&gt;.
            var choices = localize[type];

            // For each &lt;option&gt;...
            jQuery( choices ).each( function( index, value ) {

                // The value.
                var slug = value.slug;

                // The label.
                var name = value.name;

                // Build the &lt;option&gt;.
                var option = jQuery( '&lt;option/&gt;', {
                    html: name,
                    'value' : slug
                });

                // Maybe make it sticky.
                if( slug == current ) {
                    jQuery( option ).attr( 'selected', 'selected' );
                }

                // Add it to the select menu.
                jQuery( option ).appendTo( select );

            });

            var out = jQuery( select );

            return out;

        }

        /**
         * A function to make a draggable checkbox input.
         * 
         * @param  {string}   key          The name of the option controlled by this checkbox.
         * @param  {string}   hiddenValArr The JSON currently being stored in this fieldset.
         * @param  {string}   fieldsetID   The HTML ID for this fieldset.
         * @param  {boolean}  checked      Whether or not this checkbox hould be checked.
         * @return {string}   The HTML for a draggable checkbox input.
         */
        function checkboxListItem( key, hiddenValArr, fieldsetID,  checked ) {

            var out = '';

            // If the key is empty, skip it.
            if( key === '' ) { return out; }

            // If we are doing an empty checkbox, make sure it's not already on the list.
            if( ! checked ) {

                // If there is actually some value in the hidden input...
                if( hiddenValArr !== null ) {

                    // If the checkbox for this item is already checked, skip it.
                    if( hiddenValArr[key] == true ) {
                        return out;

                    // Else, if it's a non empty string, skip it.
                    } else if( ( typeof hiddenValArr[key] == 'string' ) &amp;&amp; hiddenValArr[key] != '' ){
                        return out;
                    }
                }
            }

            // This might end up holding an HTML &lt;select&gt; menu.
            var select = '';

            // The might hold the current value of the select menu.
            var selected = false;

            // Does this key contain a magic hash code?
            if( key.indexOf( '###' ) != -1 ) {

                // If so, remove the magic hash codes in order to arrive at what type of &lt;select&gt; it is.
                var type = key.replace( new RegExp( '#', 'g' ), '' );

                // Grab the select menu. We'll append it later.
                select = dropdown( type, checked, hiddenValArr );

                // Grab the current value of the select menu.
                selected = jQuery( select ).val();

            }

            // Create the checkbox.
            var input = jQuery( '&lt;input/&gt;', {
                'id'    : fieldsetID + key,
                'name'  : key,
                'type'  : 'checkbox',
                'value' : key
            });

            // If the checkbox is checked and has a select menu, record the value of the select menu in a data attribute.
            if( checked &amp;&amp; selected ) {
                jQuery( input ).attr( 'data-which', selected );
            }

            // Wrap a label around the input.
            var label = jQuery( '&lt;label/&gt;', {
                html  : key,
                'for' : fieldsetID + key
            });
            jQuery( input ).prependTo( label );

            // Make a list item to wrap the whole darn thing.
            var out = jQuery( '&lt;li/&gt;' );

            // Add the label to the list item.
            jQuery( label ).appendTo( out );

            // Add the select menu to the list item.
            jQuery( select ).appendTo( out );

            return out;

        }

        /**
         * Create the list of checkboxes.
         * 
         * @param  {string}   fieldsetID       The ID for this fieldset.
         * @param  {object}   hidden           The input that stores the value of the checkboxes.
         */
        function create( fieldsetID, hidden ) {

            // The JSON string in the hidden value.
            var hiddenVal = hidden.val();

            // Grab the hidden value as an object.
            if( typeof hiddenVal !== 'object' ) {
                var hiddenValArr = jQuery.parseJSON( hiddenVal );
            } else {
                var hiddenValArr = hiddenVal;
            }

            /**
             * The availabe options for this fieldset, stored in the DOM as a
             * data attribute on the input that stores the JSON.
             */ 
            var choices    = hidden.data( 'choices' );
            choices        = JSON.stringify( choices );
            var choicesArr = JSON.parse( choices );

            // Start a list to hold the checkboxes, which we'll draw shortly.
            var sortable = jQuery( '&lt;ul&gt;' ).addClass( 'ui-sortable' ).insertBefore( hidden );   

            // Make the checkboxes sortable.
            jQuery( sortable ).sortable({

                // Once an item is dragged and sorted, update the preview and save the JSON.
                stop: function( event, ui ) {
                    update( hidden  );
                }
            });

            // Loop through the current values and output checked checkboxes.
            if( ! jQuery.isEmptyObject( hiddenValArr ) ) {

                // For each of the current values...
                jQuery.each( hiddenValArr, function( key, value ) {

                    // Draw a checkbox with the dropdown menu visible.
                    var listItem = checkboxListItem( key, hiddenValArr, fieldsetID,  true );

                    // Add it to the DOM and check the box.
                    jQuery( listItem ).appendTo( sortable ).find( 'input' ).prop( 'checked', true );

                });


            }

            // For all the available choices on this fieldset...
            jQuery.each( choicesArr, function( key, value ) {

                // Draw a checkbox with the dropdown menu hidden.
                var listItem = checkboxListItem( key, hiddenValArr, fieldsetID,  false );

                // Add it to the DOM.
                jQuery( listItem ).appendTo( sortable ).find( 'input' );

            });

        }

        /**
         * Update the value for this fieldset, perhaps when a checkbox is checked or sorted.
         * 
         * @param  {object} hidden The hidden form field that stores the values for this fieldset.
         */
        function update( hidden ) {

            var out = {};

            // Grab the name of the hidden field.
            var hiddenName = jQuery( hidden ).attr( 'name' );

            // Grab all the checkboxes in this fieldset.
            var checkboxes = jQuery( hidden ).closest( 'li' ).find( '[type="checkbox"]:checked' );

            // Grab all the select menus in this fieldset.
            var selects = jQuery( hidden ).closest( 'li' ).find( 'select' );

            // For each checkbox...
            jQuery( checkboxes ).each( function( index, value ) {

                // Grab the value.
                var val = jQuery( value ).val();

                // Let's see if this checkbox carries a select menu as well.
                var which = jQuery( value ).attr( 'data-which' );

                // If it has a select menu, that acts as the value for the checkbox.
                if( typeof which != 'undefined' ) {
                    out[val] = which;

                // If not, the checkbox is just boolean true.
                } else {
                    out[val] = true;

                }

            });

            // Turn the output into JSON.
            var outStr  = JSON.stringify( out );
            var outJSON = JSON.parse( outStr );

            // Update the hidden field.
            jQuery( hidden ).val( outStr );

            // Grab the core WP customization object and update it.
            var api = wp.customize;
            api.instance( hiddenName ).set( outStr );

            // Show or hide the select menus based on whether the parent menu item is checked.
            jQuery( selects ).each( function( index, value ) {

                // Find out if the checkbox is checked.  If so, reveal the select menu.
                var checkbox = jQuery( value ).closest( 'li' ).find( '[type="checkbox"]' );
                if( jQuery( checkbox ).is( ':checked' ) ) {

                    jQuery( value ).slideDown();

                // Else, hide the select menu.
                } else {

                    jQuery( value ).slideUp();

                }

            });

        }

        /**
         * The main part of the jQuery plugin that actually returns the selected items.
         * 
         * @return {object} The items that this plugin applies to.
         */
        return this.each(function() {

            var that = this;

            // The ID for this section of the customizer.
            var fieldsetID = jQuery( that ).attr( 'id' );

            // The input that stores JSON as the checkboxes toggle and move.
            var hidden = jQuery( that ).find( '[type="text"]' );

            // Okay! Create the list of checkboxes!
            create( fieldsetID, hidden );

            // Whenever the checkboxes are checked, update the json and redraw the preview.
            jQuery( that ).on( 'change', '[type="checkbox"]', function( event ) { 
                update( hidden );
            });

            /**
             * Whenever the dropdowns are selected, trigger a change on the
             * corresponding checkbox.
             */ 
            jQuery( that ).on( 'change', 'select', function( event ) { 

                /**
                 * What type of dropdown is this?  Maybe to get links by category, or get a custom menu?
                 */
                var type = jQuery( this ).attr( 'data-type' );

                // Find the corresponding checkbox.
                var checkbox = jQuery( this ).closest( 'li' ).find( '[type="checkbox"]:checked' );

                // Find out which &lt;option&gt; is selected.
                var selected = jQuery( this ).val();

                // Trigger a change on that checkbox.
                jQuery( checkbox ).attr( 'data-which', selected ).trigger( 'change' );

            });

            return that;

        });

    }

}( jQuery ));
</code></pre>