Hook for image edit popup

The answer is there is no damn hook for the edit button.

It’s just a bunch of JS contained within wp-includes/js/tinymce/plugins/wpeditimage/plugin.js.

I’ve included the barebones of what you need below. Key points:

a. Clicking an element with a data-wp-imgselect attribute will open the image edit dialogue. You need to change that to something else if you don’t want that to happen (data-wp-chartselect in my example).

b. A good way of preventing wpeditimage from conflicting is to give whatever element you’re editing a mceItem class. This will make WordPress think it’s a placeholder and thus not select it.

c. The bit you can’t see is my Angular app loaded from the datacharts.cb_url global. I have a button in that which does the following when clicked:

parent.tinymce.activeEditor.insertContent('<div class="mceNonEditable"><img src="' + angular.element('.savePNG').attr('href') + '" data-llama=\'' + window.btoa(angular.toJson(scope.config)) + '\' class="mceItem" /></div><br />');

parent.tinymce.activeEditor.windowManager.close();

The key thing here is that it creates an image with a data attribute (data-llama) containing a Base64-encoded representation of my chart configuration. This is decoded and deserialised when it gets passed back to my Angular app via TinyMCE, which is then used to populate the chart. I’m open-sourcing my entire codebase and will link to it here once I’ve done so, in case you want to see a complete implementation.

Without any further ado, here’s my TinyMCE plugin:

/**
 *  datacharts TinyMCE plugin
 */

tinymce.PluginManager.add('datacharts', function(editor, url) {
  var toolbarActive = false;

  // Add a button that opens a window. This is just the toolbar.
  editor.addButton('datacharts', {
    text: false,
    icon: 'icon dashicons-chart-area',
    onclick: function() {
      // Open window
      editor.windowManager.open({
        title: 'datacharts',
        width: jQuery(window).width() - 100,
        height: jQuery(window).height() - 100,
        url: datacharts.cb_url,
        buttons: [
          {
            text: 'Cancel',
            onclick: 'close'
          }
        ]
      });
    }
  });

  function editImage( img ) {
    // Open window
    editor.windowManager.open({
      title: 'datacharts',
      width: jQuery(window).width() - 100,
      height: jQuery(window).height() - 100,
      url: datacharts.cb_url,
      buttons:
        [
          {
            text: 'Cancel',
            onclick: 'close'
          }
        ]
      },
      { // This object is passed to the receiving URL via parent.tinymce.activeEditor.windowManager.getParams()
        llama: img.dataset.llama
      }
    );
  }

  // Remove the element if the "delete" button is clicked.
  function removeImage( node ) {
    var wrap;

    if ( node.nodeName === 'DIV' && editor.dom.hasClass( node, 'mceTemp' ) ) {
      wrap = node;
    } else if ( node.nodeName === 'IMG' || node.nodeName === 'DT' || node.nodeName === 'A' ) {
      wrap = editor.dom.getParent( node, 'div.mceTemp' );
    }

    if ( wrap ) {
      if ( wrap.nextSibling ) {
        editor.selection.select( wrap.nextSibling );
      } else if ( wrap.previousSibling ) {
        editor.selection.select( wrap.previousSibling );
      } else {
        editor.selection.select( wrap.parentNode );
      }

      editor.selection.collapse( true );
      editor.nodeChanged();
      editor.dom.remove( wrap );
    } else {
      editor.dom.remove( node );
    }
    removeToolbar();
  }

  // This adds the "edit" and "delete" buttons.
  function addToolbar( node ) {
    var rectangle, toolbarHtml, toolbar, left,
      dom = editor.dom;

    removeToolbar();

    // Don't add to placeholders
    if ( ! node || node.nodeName !== 'IMG' || isPlaceholder( node ) ) {
      return;
    }

    dom.setAttrib( node, 'data-wp-chartselect', 1 );
    rectangle = dom.getRect( node );

    toolbarHtml="<div class="dashicons dashicons-edit edit" data-mce-bogus="1"></div>" +
      '<div class="dashicons dashicons-no-alt remove" data-mce-bogus="1"></div>';

    toolbar = dom.create( 'div', {
      'id': 'wp-image-toolbar',
      'data-mce-bogus': '1',
      'contenteditable': false
    }, toolbarHtml );

    if ( editor.rtl ) {
      left = rectangle.x + rectangle.w - 82;
    } else {
      left = rectangle.x;
    }

    editor.getBody().appendChild( toolbar );
    dom.setStyles( toolbar, {
      top: rectangle.y,
      left: left
    });

    toolbarActive = true;
  }

  // This removes the edit and delete buttons.
  function removeToolbar() {
    var toolbar = editor.dom.get( 'wp-image-toolbar' );

    if ( toolbar ) {
      editor.dom.remove( toolbar );
    }

    editor.dom.setAttrib( editor.dom.select( 'img[data-wp-chartselect]' ), 'data-wp-chartselect', null );

    toolbarActive = false;
  }

  function isPlaceholder( node ) {
    var dom = editor.dom;

    if ( /*dom.hasClass( node, 'mceItem' ) ||*/ dom.getAttrib( node, 'data-mce-placeholder' ) ||
      dom.getAttrib( node, 'data-mce-object' ) ) {

      return true;
    }

    return false;
  }

  editor.on( 'mousedown', function( event ) {
    if ( editor.dom.getParent( event.target, '#wp-image-toolbar' ) ) {
      if ( tinymce.Env.ie ) {
        // Stop IE > 8 from making the wrapper resizable on mousedown
        event.preventDefault();
      }
    } else if ( event.target.nodeName !== 'IMG' ) {
      removeToolbar();
    }
  });

  editor.on( 'mouseup', function( event ) {
    var image,
      node = event.target,
      dom = editor.dom;

    // Don't trigger on right-click
    if ( event.button && event.button > 1 ) {
      return;
    }

    if ( node.nodeName === 'DIV' && dom.getParent( node, '#wp-image-toolbar' ) ) {
      image = dom.select( 'img[data-wp-chartselect]' )[0];

      if ( image ) {
        editor.selection.select( image );
        if ( dom.hasClass( node, 'remove' ) ) {
          removeImage( image );
        } else if ( dom.hasClass( node, 'edit' ) ) {
          editImage( image );
        }
      }
    } else if ( node.nodeName === 'IMG' && ! editor.dom.getAttrib( node, 'data-wp-chartselect' ) && ! isPlaceholder( node ) ) {
      addToolbar( node );
    } else if ( node.nodeName !== 'IMG' ) {
      removeToolbar();
    }
  });

  editor.on( 'cut', function() {
    removeToolbar();
  });


  // This might not be needed, not sure what it does.
  editor.on( 'PostProcess', function( event ) {
    if ( event.get ) {
      event.content = event.content.replace( / data-wp-chartselect="1"/g, '' );
    }
  });

});

Leave a Comment