Is it possible to add a repeater field to TinyMCE’s option window?

I wasn’t able to directly add a repeating field (with let’s say a + button) but I was able to create a 2 step process whereby the end-user can use a numeric field to set the number of repeatable areas on the next screen:

Step 1 - Select Shortcode
Step 2 - Shortcode Builder Step 1/2
Step 3 - Shortcode Builder Step 2/2
Step 4 - Shortcode Output into WYSIWYG Editor

Step 1 – Select Shortcode
Step 2 – Shortcode Builder Step 1/2
Step 3 – Shortcode Builder Step 2/2
Step 4 – Shortcode Output into WYSIWYG Editor

Code:

(function() {
    tinymce.PluginManager.add('shortcode_mce_button', function( editor, url ) {
        editor.addButton( 'shortcode_mce_button', {
            tooltip: 'Add custom shortcode',
            image: '/wp-content/plugins/wdst-shortcodes/public/images/shortcodes.gif',
            type: 'menubutton',
            menu: [
                {
                    text: 'accordion',
                    onclick: function() {
                        editor.execCommand('accordion_shortcode_mce_button_popup','',{
                            type:           'default',
                            accordionclass: '',
                            numberofitems:  '2',
                        });
                    }
                },
            ]
        });

        var accordion_tag = 'accordion'; //Add to the accordion shortcode naming; in our case accordion works fine.

        //add accordion popup
        editor.addCommand('accordion_shortcode_mce_button_popup', function(ui, v) {
            //setup defaults
            var type="default";
            if (v.type)
                type = v.type;
            var accordionclass="";
            if (v.accordionclass)
                accordionclass = v.accordionclass;
            var numberofitems="";
            if (v.numberofitems)
                numberofitems = v.numberofitems;
            //open the popup
            editor.windowManager.open( {
                title: 'Accordion Shortcode Builder (Step 1 of 2)',
                classes: 'items-panel',
                body: [
                    {//add type select
                        type: 'listbox',
                        name: 'type',
                        label: 'Type',
                        value: type,
                        minWidth:300,
                        'values': [
                            {text: 'Default', value: 'default'},
                        ],
                        tooltip: 'Select the type of accordion you want.'
                    },
                    {//add class input
                        type: 'textbox',
                        name: 'accordionclass',
                        label: 'Class',
                        value: accordionclass,
                        minWidth:300,
                        tooltip: 'Add custom classes to the accordion wrapper.'
                    },
                    {//add class input
                        type: 'textbox',
                        name: 'numberofitems',
                        label: 'Number of Items',
                        value: numberofitems,
                        minWidth:300,
                        tooltip: 'Set the number of accordion items needed.'
                    },
                ],
                onsubmit: function(e) { // when the ok button is clicked
                    var finaltype = e.data.type;
                    var finalclass = e.data.accordionclass;
                    var totalitems = e.data.numberofitems;
                    var generalFormItems = [];
                    for(var i = 1; i <= totalitems; i++)
                    {
                        generalFormItems.push(
                            {
                                title: 'Accordion-Item '+i,
                                name:'item'+i,
                                type: 'form',
                                items:[
                                    {
                                        label: 'Accordion-Item ' +i,
                                        name: 'itemhtml'+i,
                                        type: 'container',
                                        minWidth:300
                                    },
                                    {
                                        label: 'Title',
                                        name: String('title'+i),
                                        type: 'textbox',
                                        minWidth:300,
                                        tooltip: 'Leave blank for none.'
                                    },
                                    {
                                        label: 'Content',
                                        name: 'content'+i,
                                        multiline: true,
                                        type: 'textbox',
                                        minWidth:300
                                    }]
                            })
                    }
                    win = editor.windowManager.open({
                        autoScroll: true,
                        minWidth: 600,
                        resizable: true,
                        classes: 'largemce-panel',
                        title: 'Insert Accordion-Items (Step 2 of 2)',
                        body: generalFormItems,
                        onsubmit: function( e ) { // when the ok button is clicked
                            //start the shortcode tag
                            var accordion_str="<p>[" + accordion_tag + ' type="'+finaltype+'" class="'+finalclass+'"]</p>';

                            for(var i = 1; i <= totalitems; i++) {
                                var gettitle = String('e.data.title' + i);
                                var title = eval(gettitle);
                                var getcontent = String('e.data.content' + i);
                                var content = eval(getcontent);
                                accordion_str += '<p>[' + accordion_tag + '-header]';
                                accordion_str += '[' + accordion_tag + '-title]' + title + '[/' + accordion_tag + '-title]';
                                accordion_str += '[/' + accordion_tag + '-header]</p>';

                                accordion_str += '<p>[' + accordion_tag + '-content]' + content + '[/' + accordion_tag + '-content]</p>';
                            }

                            //start the shortcode tag
                            accordion_str += '<p>[/' + accordion_tag + ']</p>';

                            //insert shortcode to TinyMCE
                            editor.insertContent( accordion_str );
                        }
                    });
                }

            });

        });
    });
})();

Additional Notes:

On the second option screen, I used the autoScroll parameter to create a scrollbar but in WordPress, this conflicts with their TinyMCE styles so I had to add the ‘classes’ parameter to set a class on this window and then use css to fix the display issues.

/*Fix Accordion Window scroll effect*/
.mce-largemce-panel {
    top: 22% !important;
    max-height: 500px !important;
}
.mce-largemce-panel .mce-reset {
    height: 500px !important;;
    overflow: auto !important;
    margin-left: -16px !important;
    margin-right: -16px !important;
}

.mce-largemce-panel .mce-window-head {
    margin-left: 16px !important;
    margin-right: 16px !important;
}

.mce-largemce-panel .mce-foot {
    margin-left: 15px !important;
}

I also used some jquery validation on the number field on the first pop-up window. It’s not the prettiest code, and I’m sure someone else could find a better way to target this window, but this is what I am currently using to validate that a number has been input.

$(document).on('keyup', '.mce-items-panel .mce-reset .mce-container-body .mce-last .mce-container-body .mce-last .mce-container-body input.mce-last', function(event) {
    var v = this.value;
    if($.isNumeric(v) === false) {
        //chop off the last char entered
        this.value = this.value.slice(0,-1);
    }
});

So, this just about covers everything. If there are any further updates, I’ll post those at that time. Hopefully this will atleast help some people who need a repeating field solution and can improve upon what I have shown here.