12

I am extending a cloud-hosted LMS with javascript. Therefore, we can add javascript to the page, but cannot modify the vendor javascript for different components.

The LMS uses tinyMCE frequently. The goal is to add a new button on to the toolbar of each tinyMCE editor.

The problem is that since the tinyMCE modules are initialized in the vendor's untouchable code, we cannot modify the init() call. Therefore, we cannot add any text on to the "toolbar" property of the init() object.

So I accomplished this in a moderately hacky way:

tinyMCE.on('AddEditor', function(e){
  e.editor.on('init', function(){
    tinyMCE.ui.Factory.create({
      type: 'button',
      icon: 'icon'
    }).on('click', function(){
      // button pressing logic
    })
    .renderTo($(e.editor.editorContainer).find('.mce-container-body .mce-toolbar:last .mce-btn-group > div')[0])
  });
});

So this works, but needless to say I am not totally comfortable having to look for such a specific location in the DOM like that to insert the button. Although this works, I do not believe it was the creator's intention for it to be used like this.

Is there a proper way to add the button to a toolbar, after initialization, if we cannot modify the initialization code?

aaronofleonard
  • 2,546
  • 17
  • 23
  • I am not sure sure about adding the button afterwards, but you could unbind the initial init and rebind everything yourself. So it will be as if you run the init. This will increase the loading time, but should give you what you want. – Chris Apr 19 '16 at 19:47

1 Answers1

22

I found a more elegant solution, but it still feels a bit like a hack. Here is what I got:

// get an instance of the editor
var editor=tinymce.activeEditor; //or tinymce.editors[0], or loop, whatever

//add a button to the editor buttons
editor.addButton('mysecondbutton', {
  text: 'My second button',
  icon: false,
  onclick: function () {
    editor.insertContent('&nbsp;<b>It\'s my second button!</b>&nbsp;');
  }
});

//the button now becomes
var button=editor.buttons['mysecondbutton'];

//find the buttongroup in the toolbar found in the panel of the theme
var bg=editor.theme.panel.find('toolbar buttongroup')[0];

//without this, the buttons look weird after that
bg._lastRepaintRect=bg._layoutRect;

//append the button to the group
bg.append(button);

I feel like there should be something better than this, but I didn't find it.

Other notes:

  • the ugly _lastRepaintRect is needed because of the repaint method, which makes the buttons look ugly regardless if you add new controls or not
  • looked in the code, there is no way of adding new controls to the toolbar without repainting and there is no way to get around it without the ugly hack
  • append(b) is equivalent to add(b).renderNew()
  • you can use the following code to add the button without the hack, but you are shortcircuiting a lot of other stuff:

Code:

bg.add(button);
var buttonElement=bg.items().filter(function(i) { return i.settings.text==button.text; })[0];
var bgElement=bg.getEl('body');
buttonElement.renderTo(bgElement);
Siderite Zackwehdex
  • 6,293
  • 3
  • 30
  • 46
  • Thanks for the thorough answer. Actually, the only part I needed was editor.theme.panel.find('toolbar buttongroup'). I had no idea that existed, tinyMCE's documentation is really lacking. But this one line solved my problem: inserting the button into the DOM without relying on knowing the specific DOM of tinyMCE. Thanks a lot :) – aaronofleonard Apr 20 '16 at 15:29
  • I've updated my answer with another solution at the end. The point is that your code adds some HTML in the DOM, but doesn't also add the data and the editor could at some time remove it or do something ugly because it doesn't know it's there. All research was done on the code. The documentation is tiny :) and the comments in the code (like 'TODO fix this!!') are illuminating. – Siderite Zackwehdex Apr 20 '16 at 15:34
  • `bg is not defined` too depend a version, not exist theme `editor.theme.panel.find` ;( – KingRider May 30 '17 at 14:11