Wednesday, January 5, 2011

Adding a Custom Toolbar Item to Dojo Rich Text Edit

As mentioned in a previous blog post, dojo's rich text editor includes a plugin mechanism to add custom toolbar items. In this post I will explore how I accomplished this, details of how to integrate into WPF can be found here.

My goal is to be able to click on a toolbar button and have a modal dialog box with a lightbox effect appear. The contents of the dialog is a custom search result where individual result items can be selected, causing the dialog to close and inserting a link to that item in the rich text edit.

Create a New Plugin

Since writing code from scratch is tedious (I am impatient) I always prefer to start with something that works and then modify it to meet my needs. In this case I've taken the plugin named factory\dojo\dijit\_editor\plugins\ToggleDir.js since it's nice and short, and re-purposed it to create a modal popup window when clicked. I'm going to copy it into a new file named SearchPopup.js.

dojo.provide("dijit._editor.plugins.SearchPopup");
dojo.require("dijit._editor._Plugin");
dojo.declare("dijit._editor.plugins.SearchPopup",
    dijit._editor._Plugin,
    {
        // summary:
        //        This plugin provides a popup window to search and select content
        //
        // description:
        //        The commands provided by this plugin are:

    _initButton: function(){
        // summary:
        // This is the button that will appear on the toolbar
        this.button = new dijit.form.Button({
            label: "Search and add content",
            showLabel: false,
            iconClass: this.iconClassPrefix + " " + this.iconClassPrefix + "CreateLink",
            tabIndex: "-1",
            onClick: dojo.hitch(this, function(){popupSearch()})
        });
        }
        
    }
);

// Register this plugin.
dojo.subscribe(dijit._scopeName + ".Editor.getPlugin",null,function(o){
    if(o.plugin){ return; }
    switch(o.args.name){
    case "searchPopup": 
        o.plugin = new dijit._editor.plugins.SearchPopup();
    }
});


This is probably the most basic plugin that can be created. All it does is invoke a javascript function named popupSearch() when the button is clicked:
onClick: dojo.hitch(this, function(){popupSearch()})

The code to pick an icon is a little tricky, it refers to factory\dojo\dijit\themes\tundra\images\editor.gif which is an image containing a long strip of icons like this:

and finally the iconClass can be tracked down in factory\dojo\dijit\themes\tundra\Editor.css:
Code:
iconClass: this.iconClassPrefix + " " + this.iconClassPrefix + "CreateLink",

Matching CSS:
.tundra .dijitEditorIconCreateLink { background-position: -90px; }

If I wanted to use my own icon I'd have to edit the icon strip and add a new image, but since I can't draw to save my life I decided to just reuse the one for creating a link. The rest of the code is just boilerplate, and we can add our newly minted plugin to the toolbar with some javascript:
dojo.addOnLoad(function(){
 dojo.require("dijit._editor.plugins.SearchPopup");
 var widget = dijit.byId(gcps.assessmentWidget);
 widget.addPlugin('searchPopup');
});

EDIT 2/14/2012: I found a pretty good developerworks article on this topic. Check it out.
Please note that generic comments containing links with the intent of self promotion will be flagged as spam and deleted.

9 comments:

  1. This tutorial is spot on, except when I try to add this plugin the button is "disabled" by the toolbar. Have you noticed this behavior?

    ReplyDelete
  2. I haven't had any issues with the button being disabled. I took a quick look at isFocusable() in dijit._form._FormWidget.js and there seems to be some other code that could cause that symptom. Try stepping through it with chrome's debugger.....

    ReplyDelete
  3. Yeah, it's very strange. Dojo runs each plugin through a piece of code which tries to enable/disable the plugin.

    Here is the piece that's telling the plugin to be disabled, it comes from RichText.js:

    try{
    return elem.queryCommandEnabled(command);
    }catch(e){
    //Squelch, occurs if editor is hidden on FF 3 (and maybe others.)
    return false;
    }

    ReplyDelete
  4. Welp, I've figured it out. I had to add my "commandName" to the commands.js file in dijit/_editor/nls.

    Thanks again for your help and for the tutorial.

    ReplyDelete
  5. Ah - I'm going to take a guess that you had an indirect reference like

    label: strings["newPage"]

    where I have

    label: "Search and add content"

    I'm glad I was able to help.

    ReplyDelete
  6. Any chance you can post a working example of this because I can't seem to get it working :(

    ReplyDelete
  7. I have tried this code to develop Dojo Rich Text Edit toolbar but I can't get it. Can you explain by video?

    ReplyDelete
  8. I'm not sure exactly why but I think you need to set the variable

    useDefaultCommand: false,

    Which tells the editor that this is not a default command as described in the IBM article. Otherwise the button does not appear (working in dojo 1.6)

    My working code:

    dojo.declare("my.widgets.LoadQuery", dijit._editor._Plugin, {

    // When you click the command button, you show a toolbar,
    // instead of executing some editor commands.
    useDefaultCommand: false,

    _initButton: function () {
    // summary:
    // This is the button that will appear on the toolbar
    this.button = new dijit.form.Button({
    label: "This",
    showLabel: false,
    iconClass: this.iconClassPrefix + " " + this.iconClassPrefix + "CreateLink",
    tabIndex: "-1",
    onClick: dojo.hitch(this, function () {
    alert('click');
    })
    });
    }
    });

    // Register this plug-in so it can construct itself when the editor publishes a topic.
    dojo.subscribe(dijit._scopeName + ".Editor.getPlugin", null, function (o) {
    if (!o.plugin && o.args.name.toLowerCase() === "loadquery") {
    o.plugin = new my.widgets.LoadQuery();
    }
    });

    ReplyDelete
  9. I have been looking to extend the toolbar to add in the create link plugin for a short time now.

    ReplyDelete

Are you about to post a generic comment that has nothing to do with this post? Something like "Hey thanks for this very valuable information, BTW here's my website". If so, it will be marked as spam and deleted within 24 hours.

Note: Only a member of this blog may post a comment.