Instantiate TinyMCE via JS ( rather than PHP/wp_editor )

Updated for TinyMCE 4

I’ve made some progress

Load a wp_editor in the initial page load in a hidden div, then do the AJAX call to grab the editor markup.

While in PHP, you will need to retrieve the IDs of the editors created via wp_editor calls. Unfortunately, the variables on the _WP_Editors class are private, but wecan get around this by using filters. To do this we start by calling the editor_js hook inside an output buffer. It will attempt to generate the needed settings and output the required JS, although this JS will not work when sent back via AJAX and inserted.

At the end of this call, the _WP_Editors class pases the mcesettings array through a filter, which we will use to make a copy that we can pull the editor IDs out of:

add_action( 'after_wp_tiny_mce', array( $this, 'steal_away_mcesettings' ) );

function steal_away_mcesettings( $mcesettings ) {
    $this->mcesettings = $mcesettings;
}

So after we’ve added this filter, we call out editor_js function:

ob_start();
_WP_Editors::editor_js();
ob_end_clean(); 

We can now use our $mcesettings copy to fill in a list of IDs:

$ids = array();

foreach ( $this->mcesettings as $editor_id => $init ) {
    $ids[] = $editor_id;
}

The ids array can now be sent back in the AJAX response along with the html.

Once in the browser, we can insert the html into the page, and then using our editor_ids, we can instantiate the TinyMCE instances like this:

function tinyMCE_bulk_init( editor_ids ) {
    var init, ed, qt, first_init, DOM, el, i;

    if ( typeof(tinymce) == 'object' ) {

        var editor;
        for ( e in tinyMCEPreInit.mceInit ) {
            editor = e;
            break;
        }
        for ( i in editor_ids ) {
            var ed_id = editor_ids[i];
            tinyMCEPreInit.mceInit[ed_id] = tinyMCEPreInit.mceInit[editor];
            tinyMCEPreInit.mceInit[ed_id]['elements'] = ed_id;
            tinyMCEPreInit.mceInit[ed_id]['body_class'] = ed_id;
            tinyMCEPreInit.mceInit[ed_id]['succesful'] =  false;
        }

        for ( ed in tinyMCEPreInit.mceInit ) {
            // check if there is an adjacent span with the class mceEditor
            if ( ! jQuery('#'+ed).next().hasClass('mceEditor') ) {
                init = tinyMCEPreInit.mceInit[ed];
                try {
                    tinymce.init(init);
                    tinymce.execCommand( 'mceAddEditor', true, ed_id );
                } catch(e){
                    console.log('failed');
                    console.log( e );
                }
            }
        }

    }
}

Because we don’t have the ability to generate the necessary TinyMCE settings ourselves to match the WordPress style, we instead steal them from the instance we created in the initial page load, and make copies. We then modify the copies to use the correct element IDs and proceed as usual, checking that the elements dont already have an instance.

enter image description here

Issues

  • The tabs for plain text vs visual content do not work
  • No quick tags on the plaintext view
  • The extra weight of the initial TinyMCE instance
  • No Media uploader button
  • Adding 2 TinyMCE instances or more in an AJAX request leads to one working instance, and the others being broken. I’m not sure why this is the case, yet 2 AJAX requests with 1 instance each, gives you 2 working instances.
  • The JS could be modified to not modify the TinyMCEPreInit structure, and pass the copy straight into the initialiser, removing a DOM traversal in the process

Leave a Comment