Is it possible to customize TinyMCE HTML Editor?

Erik Stolberg
Tera Guru

The TinyMCE editor is the default for HTML fields. As TinyMCE is an Open Source project (TinyMCE - Develop) it looks like changes to the code are supported. I'm curious if anyone has made changes (to create custom buttons, for example, not just adding or removing existing buttons), and what the process would be to get the new code into ServiceNow, as TinyMCE is currently added as a plugin. I'm assuming Support would need to be involved.

7 REPLIES 7

Erik Stolberg
Tera Guru

I did find this article, which seems to still be an open discussion without a definitive answer other than a new workaround:I have seen that TinyMCE is


tltoulson
Kilo Sage

Hi Erik,



It doesn't seem as though this would be easy to accomplish with OOB HTML fields.   Using custom widgets (Formatters and UI Macros) I am sure much more would be possible.   ServiceNow's implementation of the TinyMCE editor is initialized in a self contained function in the render events.   This makes it very difficult to hook into the init of the editor and of course TinyMCE is designed to make most meaningful changes at init, not dynamically.   So unless ServiceNow gives us the ability to hook into some things or adds properties, we are left with either hacking into the editor or initializing our own separate editor (where we would presumably tie it to a normal text field).



There are some settings that can be changed and you can also wire into some of the events.   So there are some changes you can make.   But to add plugins and such, you would probably have to find a way to destroy the editor (easy: tinymce.EditorManager.editors[0].destroy()) and then reinitialize it (more complicated tinyMCE configuration + TinyMCETextAreaElement).



Hope this information is helpful.



Kind regards,



Travis


xiaix
Tera Guru

I wanted to remove the Source Code button from an HTML field on a Catalog Item:


find_real_file.png



So here's what I did to make it work: (Catalog Client Script (onLoad)):


var __sTO;


var __ticks = 0;


function onLoad()


{


      __sTO = setTimeout(function(){ removeSourceCodeButton(); }, 100);


}


function removeSourceCodeButton()


{


      __ticks++;


      var cleared = false;


      var divs = document.getElementsByTagName('div');


      for (var i = 0; i < divs.length; i++)


      {


              if (divs[i].id && divs[i].id.indexOf('mceu_') == 0)


              {


                      if (divs[i].outerHTML.indexOf('Source code') >= 0 && divs[i].outerHTML.indexOf('mce-i-code') >= 0)


                      {


                              if (divs[i].outerHTML.indexOf('Source code') >= 0 && divs[i].outerHTML.indexOf('mce-i-code') >= 0 && divs[i].innerHTML.indexOf('<button') == 0)


                              {


                                      divs[i].parentNode.removeChild(divs[i]);


                                      clearTimeout(__sTO);


                                      cleared = true;


                                      break;


                              }


                      }


              }


      }    


      if (!cleared)


      {


              clearTimeout(__sTO);


              __sTO = setTimeout(function(){ removeSourceCodeButton(); }, 100);


      }    


      // Safety


      if (__ticks >= 50) // 5 seconds


      {


              console.debug("Safety Cleared __sTO");


              clearTimeout(__sTO);


      }


}


Oh, and here's a way you can do multiple buttons:


var __sTO;


var __ticks = 0;


var __verify = [];




function onLoad()


{


      __sTO = setTimeout(function(){ removeUnwantedButtons(); }, 100);


}




function removeUnwantedButtons()


{


      // Safety


      if (++__ticks >= 50) // 5 seconds


      {


              console.debug("Safety Cleared __sTO");


              clearTimeout(__sTO);


              return;


      }


      var breakOut = false;    


      var btnsA = [];


      btnsA.push({Name:'Source code', Class:'mce-i-code'},


                            {Name:'Insert/edit video', Class:'mce-i-media'},


                            {Name:'Insert/edit image', Class:'mce-i-image'},


                            {Name:'Insert/edit link', Class:'mce-i-link'},


                            {Name:'Remove link', Class:'mce-i-unlink'});


      var divs = document.getElementsByTagName('div');


      for (var i = 0; i < divs.length && !breakOut; i++)


      {


              if (divs[i].id && divs[i].id.indexOf('mceu_') == 0)


              {


                      for (var a = 0; a < btnsA.length && !breakOut; a++)


                      {


                              if (divs[i].childNodes.length &&


                                      divs[i].outerHTML.indexOf(btnsA[a].Name) >= 0 &&


                                      divs[i].outerHTML.indexOf(btnsA[a].Class) >= 0 &&


                                      divs[i].innerHTML.indexOf('<button') == 0 &&


                                      divs[i].childNodes[0].innerHTML.indexOf(btnsA[a].Class) >= 1 &&


                                      divs[i].childNodes[0].innerHTML.indexOf('<i') == 0)


                              {


                                      __verify.push(btnsA[a].Name + '(' + divs[i].id.toString() + ')');


                                      divs[i].parentNode.removeChild(divs[i]);


                                      breakOut = true;


                              }


                      }


              }


      }    


      if (__verify.length == btnsA.length)


      {


              clearTimeout(__sTO);


              for (var v = 0; v < __verify.length; v++)


              {


                      console.debug('Button ' + __verify[v] + ' successfully removed.');


              }


              __verify = [];


              return;


      }


      else


      {


              clearTimeout(__sTO);


              __sTO = setTimeout(function(){ removeUnwantedButtons(); }, 100);


      }


}