2010/01/01

Zoom Plugin for CKEditor

Recently all the browsers that are implementing the latest and greatest elements of CSS 3 have added support for CSS Transforms, and so I decided to have a little fun while creating a little example about how to create a custom dropdown element in the CKEditor toolbar.

The idea is to provide a new dropdown that allows the user to set the zoom level for the editor, this example is gonna be a little rough and it can be greatly improved, but this is mostly a proof of concept about how to do it.

The first step is to create the skeleton for the plugin:

CKEDITOR.plugins.add( 'zoom',
{
    requires : [ 'richcombo' ],

    init : function( editor )
    {

    } //Init
} );

That code should be placed in a "plugin.js" file located in the plugins/zoom folder of your CKEditor installation. If you want to create another plugin, you'll use another folder name and put that name as the first parameter in the call to CKEDITOR.plugins.add()

For this plugin we need to be sure that the code from the "richcombo" plugin is loaded, as that's the real code that will create the dropdown element, so we specify it in the requires field of the plugin.

Finally this plugin must be added to the CKEditor instance, so in your config.js (or whatever other place you want to specify it) add it to the plugins that must be loaded:

    config.extraPlugins = 'zoom';

You can place an alert() inside the init function to verify that the plugin is being loaded when you refresh the page with CKEditor. If it doesn't work, verify the error console and try to clear your cache.

Now we need to fill up the code in the init function to create the dropdown:

    init : function( editor )
    {
        var config = editor.config;

        editor.ui.addRichCombo( 'Zoom',
            {
                label : "100 %",
                title : 'Zoom',
                multiSelect : false,

                panel :
                {
                    css : [ CKEDITOR.getUrl( editor.skinPath + 'editor.css' ) ].concat( config.contentsCss )
                },

                init : function()
                {
                    var zoomOptions = [50, 75, 100, 125, 150, 200, 400],
                        zoom;
                   
                    this.startGroup( 'Zoom level' );
                    // Loop over the Array, adding all items to the
                    // combo.
                    for ( i = 0 ; i < zoomOptions.length ; i++ )
                    {
                        zoom = zoomOptions[ i ];
                        // value, html, text
                        this.add( zoom, zoom + " %", zoom + " %" );
                    }
                    // Default value on first click
                    this.setValue(100, "100 %");
                }
            });
        // End of richCombo element

    } //Init

In this function we are creating the dropdown as a "RichCombo" element with editor.ui.addRichCombo() The function takes as parameters the name of the RichCombo and the object that contains the definition. Inside this object there's an init() function that will be called to initialize the dropdown on first click, here we specify the options available in the RichCombo and add them with this.add()

So now we can add to the toolbar of our CKEditor this 'Zoom' RichCombo element:

...
    ['Maximize', 'ShowBlocks', 'Zoom'],
...

Refreshing the page will show now the dropdown and clicking on it will show the panel, although we still haven't specified what must be done  when an option is selected. Doing it in a simple way and not trying to optimize how it works we can add this function to the RichCombo definition:

...
                    // Default value on first click
                    this.setValue(100, "100 %");
                },

                onClick : function( value )
                {
                    var body = editor.document.getBody().$;
 
                    body.style.MozTransformOrigin = "top left";
                    body.style.MozTransform = "scale(" + (value/100)  + ")";

                    body.style.WebkitTransformOrigin = "top left";
                    body.style.WebkitTransform = "scale(" + (value/100)  + ")";

                    body.style.OTransformOrigin = "top left";
                    body.style.OTransform = "scale(" + (value/100)  + ")";

                    body.style.TransformOrigin = "top left";
                    body.style.Transform = "scale(" + (value/100)  + ")";
                    // IE
                    body.style.zoom = value/100;
                }
            });
        // End of richCombo element
...

This code blindly tries to set the property for each browser, without bothering about which one is being used of if it supports CSS Transforms at all. We know that IE doesn't support them, but it has supported since long ago the style.zoom so in this case we can use just that.

A correct implementation of the plugin should verify that the browser does support CSS Transforms (or is IE and can use .zoom) before adding the RichCombo, and in the onClick event handler should use just the correct property instead of trying all of them.

This plugin also lacks internationalization, because that wasn't the point of this test, but it should be easy to add and to fix other little things.

But there are two problems that can be seen with just this code:

The first one is that the width of the combo in the toolbar is a little too big to specify some little text like "100%". This is being tracked in ticket 4559 but as you can see by reading all the comments there's no easy solution that can be applied to automatically fix it. So the solution is to add some rules in the stylesheet, although that makes deploying the plugin a little harder. Maybe the RichCombo should be allowed to specify a width in its definition?

The second problem appears only in Webkit browsers (Safari and Chrome), when you open up the panel it will take full height, and it's a little ugly. Other combos have their size specified in the skins css but that doesn't seems like a good solution if you want to publish a plugin that will be used for other people, as well as making upgrading a little harder.

Browser support: The zoom feature should work in IE6+, Firefox 3.5+, current Safari and Chrome (I don't have old versions to test) and Opera 10.5+ (still in alpha)

Happy 2010

Edit may-2013:

A new version of the plugin compatible with CKEditor 4 is available at the plugins repository. Zoom plugin.

13 comments:

Lam Nguyen said...

It's cool. And I have a question: How to add external plugin? I try to something but seems still not work.

Alfonso said...

With CKEDITOR.plugisn.addExternal(name, path)

Lam Nguyen said...

Thanks! And one more a question? I have a plugin helloworld below:


CKEDITOR.plugins.add('helloworld',
{
requires : ['iframedialog'],
init : function(editor) {
var pluginName = 'helloworld';
var mypath = "http://127.0.0.1:8080/static/eXoPlugins/helloworld/";
editor.ui.addButton(
'helloworld.btn',
{
label : "My Plug-in",
command : 'helloworld.cmd',
icon : mypath + 'images/helloworld.gif'
}
);
var cmd = editor.addCommand('helloworld.cmd', {exec:showDialogPlugin});
CKEDITOR.dialog.addIframe(
'helloworld.dlg',
'Hello Title',
mypath + 'helloworld.html',
400,
300,
function(){
}
);
}
}
);

function showDialogPlugin(e){
e.openDialog('helloworld.dlg');
}


How can i disable okButton and cancelButton of iframedialog?

Alfonso said...

I still haven't really used the iframe dialogs, but I think that someone posted some comments at the forums.

Anyway, I would strongly suggest you to not hardcode full paths or you'll become crazy later trying to find out why it fails.

Lam Nguyen said...

Thanks a lots of your help! I only hardcode to test....

sweta said...

This is a great tutorial. I have one more question. I added a custom combo box and I want to change the width of the combo box and panel. Please advice

sweta said...

how to overcome 2 problems you listed in your tutorial with Ckeditor3.X.Please advice

Alfonso said...

If that's for a plugin that you're creating for your own then add whatever css you need and that's all.

Anonymous said...
This comment has been removed by the author.
Whispers and Worship said...

Hi. Seems to work fine in Chrome where the text window expands only vertically and allows the text to flow neatly. But in IE, the text expands both vertically and horizontally, causing part of the text to be out of view. Also, when zooming less than 100%, the right margin moves further left. Any ideas how to fix this (other than asking the user to switch browsers)? Thanks.

Alfonso said...

This plugin only adds some css to the content and a custom IE attribute.
You can play with a plain HTML page to find out if there's some solution for IE and then modify the plugin to fit your needs.

Ankit said...

Hi,

In IE10 my texts are not getting wrap inside the window when i try to zoom using the default zoom plugin. Can you please provide some solution to get my texts get wrap on zooming in IE10

Alfonso said...

Check my reply to Randy Hulshizer (just above your comment)