Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TinyMCE adding toggle style

I'm working on a TinyMCE plugin and one thing I want it to do is register commands/buttons that toggle custom formatting.

For example if you click the bold button in TinyMCE it will show the bold button highlighted while in bold text. Digging into the source code I see this happens via: tinymce.EditorCommands.addCommands thought I can't seem to figure out how to duplicate it. The documentation of TinyMCE is just horrible as well =(

So given customFormat I want to be able to have a button setup by my plugin that when the customFormat is applied it shows as such like the Bold, Italics, and other such buttons do on the toolbar. And clicking on my customFormat toggles that format on/off. I can easily accomplish the toogle via "addCommand" and "addButton" but then it does not have state tracking like Bold and others do.

Showing my current non-working attempt (this code is inside init of my plugin create method):

tinymce.EditorCommands.call('addCommands', {
   'MyFormat' :  function(name) {
      ed.formatter.toggle("customFormat");
    }
},'exec');

tinymce.EditorCommands.call('addCommands', {
   'MyFormat' : function(name) {
       return ed.formatter.match('customFormat');
    } 
},'state');

ed.addButton('customformat', {cmd : 'MyFormat'});

And here is the link to the "documentation" of addCommands: http://www.tinymce.com/wiki.php/API3:method.tinymce.EditorCommands.addCommands

After a lot more looking around I found this which seems to be perfect: http://www.tinymce.com/wiki.php/API3:method.tinymce.Editor.addQueryStateHandler

But when I implement the code it doesn't change the state of the button:

  ed.addCommand('MyFormat', function(ui, v) {
    ed.formatter.toggle("thoughtFormat");
  });

  ed.addQueryStateHandler('MyFormat', function() { 
      return ed.formatter.match('thoughtFormat');
  });

  ed.addButton('myformat', {cmd : 'MyFormat'});
like image 992
Kansha Avatar asked Feb 11 '13 13:02

Kansha


2 Answers

In case someone doesn't want to do it the 'plug-in' way, here's the guide for TinyMCE 4.x.

First of all, you need to define a custom format:

formats: {
   custom_format: {inline: 'span', styles: {color: "red"}, attributes: {class: 'some_css_class'}}
}

Then you'll have to add a button to your toolbar:

toolbar: "mybutton",

Next, you need to setup your button, so that it toggles the format:

setup: function(editor) {
        editor.addButton('mybutton', {
            text: 'My button',
            icon: false,
            onclick: function() {
                tinymce.activeEditor.formatter.toggle('custom_format')
            }
        });
}

Furthermore, if you want the editor to set the state of the button to indicate the format of current node, automatically, add this to setup function:

onPostRender: function() {
    var ctrl = this;                
    editor.on('NodeChange', function(e) {
        ctrl.active(e.element.className == "some_css_class")
    });
}

Your tinymce.init function should look like this:

tinymce.init({
    selector: "textarea",
    formats: {
       // Other formats...
       custom_format: {inline: 'span', styles: {color: "red"}, attributes: {class: 'some_css_class'}}
    }
    // Other default toolbars
    toolbar_n: "mybutton",

    // Finally, setup your button
    setup: function(editor) {
        editor.addButton('mybutton', {
            text: 'My Button',
            icon: false,
            onclick: function() {
                tinymce.activeEditor.formatter.toggle('custom_format')
            },
            onPostRender: function() {
                var ctrl = this;                
                editor.on('NodeChange', function(e) {
                    ctrl.active(e.element.className == "some_css_class")
                });
            }
        });
    }

Note that class attribute I added to my custom format. This approach made it possible for me define my custom styles in a separate stylesheet file and keep my markup as dry as possible (No inline styling!). Point content_css option to your stylesheet and you'll be good to go. However, due to fact that I'm using Rails as back-end and BatmanJS as front-end (and I'm fairly new to the latter), I couldn't figure out how assets routing works, and ended up adding my custom styles to default content stylesheet file of tinyMCE skin itself (located at skins/SKIN_NAME/content.min.css).

like image 133
Farzad Avatar answered Sep 23 '22 14:09

Farzad


Thanks to Thariama for insights that allowed me to dig deeper finally figuring out how to do this. I'm not sure its the "right way" but as I said TinyMCE has the worst documentation imaginable.

The key for me was to make an hook the onNodeChange event, using the setActive trick. Full example plugin with a custom button that activates when that format is present wherever the cursor is:

(function() {
   tinymce.create('tinymce.plugins.CoolPlugin', {  
   init : function(ed, url) {   

      ed.addCommand('MyFormat', function(ui, v) {
        ed.formatter.toggle("myFormat");
      });

      ed.addButton("coolformat", {
        title : 'MyFormat Tooltip', 
        cmd : 'MyFormat',
        image: url + '/coolformat.png',
      });

      ed.onNodeChange.add(function(ed, cm, n) {
        active = ed.formatter.match('myFormat');
        control = ed.controlManager.get('coolformat').setActive(active);
      });

      ed.onInit.add(function(ed, e) {
        ed.formatter.register('myFormat', 
           {inline: 'span', classes : ['cool'] } );
      });
  }
  });

  // Register plugin
  tinymce.PluginManager.add('cool', tinymce.plugins.CoolPlugin);
})();
like image 45
Kansha Avatar answered Sep 23 '22 14:09

Kansha