==============
Extending Kupu
==============

XXX this document needs a major overhaul
XXX we should cover the the typical process of implementing a new feature
XXX writing a new implementation of features -> INTEGRATING.txt

Abstract
--------

This document describes the typical process of extending Kupu with a
new feature, such as a toolbox with the underlying functionality.
Both, the ECMAScript API and the templating system are explained.

1. Kupu Tools
-------------

Apart from the context menu, all UI functionality in Kupu is added to
the core using a simple plugin mechanism. This can also be used to
write your own extensions, either by copying existing tools and
modifying them or by subclassing KupuTool (and sometimes KupuToolBox)
and writing your own functionality.

2. Tool interface
-----------------

The plugins have a simple interface to adhere to:

    interface KupuTool:

    attribute toolboxes = {};
      - id -> toolbox mapping

    method initialize(editor):
      - store reference to the editor, register event handlers, etc.

    method registerToolBox(id, toolbox):
      - register a UI element (ToolBox, more about those later)

    method updateState(selNode, event):
      - update the state of self and any toolboxes

    method createContextMenuElements(selNode, event) (optional):
      - return an array of ContextMenuElements

3. Writing a simple tool
------------------------

As an example we'll write our first simple tool, which displays the
path to the current node in the tree in the status bar. This is an
actual tool in the current Kupu code and you can see the result in the
status bar of any default Kupu installation. First we'll write a
class, and add a method called 'updateState' that will change the
status bar:

    // Example tool class
    function ExampleTool() {
        this.updateState = function(selNode, event) {
        };
    };

The updateState method is a method each tool can implement (actually
the code assumes it's available on each tool, but since the superclass
defines it as well it's not strictly necessary to add it to your
class) and that will be called when the user clicks inside the iframe,
presses enter or hits a cursor key (so basically, when the cursor can
end up in a different element).

As you can see we use in-line methods in Kupu, this is a choice of
style rather than for a functional reason, and if you wish you can
choose to use another coding style. Using
'<class>.prototype.<method> = function() {...};' will do equally well.

Now we'll add some functionality to our updateState method. The
updateState method's first argument will always be a reference to the
current selected element (or the element currently containing the
selection). To get a path to that element from the root, we can write
something like:
    
    // Example tool class
    function ExampleTool() {
        /* shows the path to the current element in the status bar */
        this.updateState = function(selNode, event) {
            /* calculate and display the path */
            var path = '';
            var currnode = selNode;
            while (currnode.nodeName != '#document') {
                path = '/' + currnode.nodeName.toLowerCase() + path;
                currnode = currnode.parentNode;
            };
            
            window.status = path;
        };
    };

The second argument is the event, if any, which started the
updateState chain (this doesn't have to be available, since
updateState can also be called by tools in certain situations).

Now that we have the updateState method complete, we have the
functionality we wanted available. To make sure the tool provides all
functionality required by the system, the tool should subclass
KupuTool. This is not strictly required, but is a nice way to provide
all the methods the editor requires to be available.

    // subclass KupuTool
    ExampleTool.prototype = new KupuTool;

To hook the tool up with the editor, it needs to be registered. For
this purpose the registerTool method is available on the KupuEditor
object, which should be called with an id and a tool as arguments
(note that the KupuEditor is called 'kupu' here, like in the default
initKupu function, where registration of our tool will most likely be
done):

    var exampletool = new ExampleTool();
    kupu.registerTool('exampletool', exampletool);

That's it, we just wrote a replacement for (or actually a copy of) the
ShowPathTool, the most basic Kupu tool.

4. A more complex example
-------------------------

Now we'll take a look at a more complex example: the ImageTool. This
will provide a way for users to add images to the iframe. First thing
we do is write a class like we did for the example tool:

    function ImageTool() {
        this.initialize = function(editor) {
            this.editor = editor;
            this.editor.logMessage('Image tool initialized');
        };
    };

As you can see we override the superclass' initialize method, we don't
strictly have to here, but it's a custom to send a message to the log
window telling the user the tool has initialized, and it shows us how
a reference to the editor object is stored on the tool and used to
call logMessage on (which in turn calls the logger registered in
initKupu).

Now let's add some functionality to the tool:

    function ImageTool() {

        // this will be required for later use, when the toolbox is added
        this.toolboxes = {};
        
        this.initialize  = function(editor) {
            this.editor = editor;
            this.editor.logMessage('Image tool initialized');
        };

        this.createImage = function(url) {
            /* insert an image */
            this.editor.execCommand('InsertImage', url);
            this.editor.logMessage('Image inserted');
        };
    };

This method calls 'execCommand' on the editor object, which in turn
calls execCommand on the iframe (actually there's another step, but
that's not important now). execCommand is a built-in method provided
by browsers, that provides a limited amount of commands to WYSIWYG
editor applications. The InsertImage command will, as you may have
guessed, insert an image at the cursor location.

All that is left to turn the tool into an Kupu Tool is set the prototype:

    ImageTool.prototype = new KupuTool;

register it to the editor (in initKupu or the HTML page):

    var imagetool = new ImageTool();
    kupu.registerTool('imagetool', imagetool);

and we're done with the basic tool.

5. ToolBoxes
------------

Now we have a class with a method that is capable of inserting an
image, but no way to call the method yet. The most basic way to call
the method would be from an HTML button, so we could simply create an
HTML button that would call the method. For larger tools, however,
it's nice to attach the event handlers from the code rather then from
the HTML itself, keeps things neat and clean, but on the other hand
it's nasty to have references to UI elements in the classes that
contain the functionality, for instance since someone using your tool
may want to use an element with another event or interface, or perhaps
even a completely different type of input (e.g. the context menu
mentioned below). Therefore we want another abstraction layer,
toolboxes.

A ToolBox is basically a UI part of a Tool, containing references to
the HTML elements and containing and registering event handlers. For
the image tool we'd probably want a class with a reference to 2 HTML
elements: an input field to enter a URL in and a button to click on to
create the image:

    function ImageToolBox(inputel, buttonel) {
        this.inputel = inputel;
        this.buttonel = buttonel;

        this.initialize(tool, editor) {
            // always store the references to the tool and editor
            this.tool = tool;
            this.editor = editor;

            // addEventHandler registers an event handler in both IE and
            // Mozilla, last argument is the context in which to call the 
            // method
            addEventHandler(this.button, 'click', this.createImage, this);
        };
        
        this.createImage = function() {
            var src = this.inputel.value;
            if (!src) {
                this.editor.logMessage('No image created since no URL was ' +
                                        'specified');
                return;
            };
            this.tool.createImage(src);
        };
    };

We don't create an updateState method here, although we could, since
we don't have a way to change the image's src attribute anyway. The
updateState method of toolboxes will usually update the state of the
UI elements of the tool (as happens with the TableTool, when inside a
tool you will see editing elements, when you're not inside one you'll
find elements to add a new table) or set the value of certain form
fields (as in the LinkTool, when inside a link it will show you the
href of the link), both not appropriate in this case (although a
usecase could easily be written I would say ;).

6. ContentFilters
-----------------

Before Kupu sends its contents to the server, it will be passed
through any registered content filters. Content filters are simple
classes that should have 2 methods: initialize, which will be called
on registration of the filter, with a single argument which is a
reference to the editor, and filter, which will be called with 2
arguments: a reference to the owner document (the document element of
the iframe) and the html node of the document. The idea is that the
filter should use DOM functionality to filter the incoming DOM and
return the filtered version.  To register a content filter to Kupu,
use 'kupu.registerFilter(filter)'.

7. Loggers
----------

Loggers are classes to log messages. A logger will usually do nothing,
or at most send everything to some text area or div, unless an error
log message comes in, in which case most loggers will probably raise
an exception.  Loggers should have a single method called 'log' that
gets one mandatory and one optional argument: it must get a message
(which is a string that will, or in some cases will not, get logged)
and it may get a severity argument, which can be 0 for debug messages,
1 for warnings and 2 for errors. The default should be 0.

A logger should be passed to the editor on instantiation time (it's
one of the constructor's arguments).

8. And the rest
---------------

Of course there's a lot more to Kupu that can be customized, added or
rewritten. For more details see the source: it should be quite clear
and simple and is written in a nice object oriented manner. Also make
sure to check out API.txt, which is a reference to Kupu' JavaScript
JSAPI.
