All Downloads are FREE. Search and download functionalities are using the official Maven repository.

META-INF.resources.oam.custom.inputHtml.kupubasetools.js Maven / Gradle / Ivy

Go to download

JSF components and utilities that can be used with any JSF implementation. This library is based on the JSF1.1 version of Tomahawk, but with minor source code and build changes to take advantage of JSF2.1 features. A JSF2.1 implementation is required to use this version of the Tomahawk library.

The newest version!
/*****************************************************************************
 *
 * Copyright (c) 2003-2005 Kupu Contributors. All rights reserved.
 *
 * This software is distributed under the terms of the Kupu
 * License. See LICENSE.txt for license text. For a list of Kupu
 * Contributors see CREDITS.txt.
 *
 *****************************************************************************/
// $Id: kupubasetools.js 928511 2010-03-28 22:53:14Z lu4242 $

//----------------------------------------------------------------------------
//
// Toolboxes
//
//  These are addons for Kupu, simple plugins that implement a certain 
//  interface to provide functionality and control view aspects.
//
//----------------------------------------------------------------------------

//----------------------------------------------------------------------------
// Superclasses
//----------------------------------------------------------------------------

function KupuTool() {
    /* Superclass (or actually more of an interface) for tools 
    
        Tools must implement at least an initialize method and an 
        updateState method, and can implement other methods to add 
        certain extra functionality (e.g. createContextMenuElements).
    */

    this.toolboxes = {};

    // private methods
    addEventHandler = addEventHandler;
};

// methods
KupuTool.prototype.initialize = function(editor) {
    /* Initialize the tool.

        Obviously this can be overriden but it will do
        for the most simple cases
    */
    this.editor = editor;
};

KupuTool.prototype.registerToolBox = function(id, toolbox) {
    /* register a ui box 
    
        Note that this needs to be called *after* the tool has been 
        registered to the KupuEditor
    */
    this.toolboxes[id] = toolbox;
    toolbox.initialize(this, this.editor);
};

KupuTool.prototype.updateState = function(selNode, event) {
    /* Is called when user moves cursor to other element 

        Calls the updateState for all toolboxes and may want perform
        some actions itself
    */
    for (var id in this.toolboxes) {
        this.toolboxes[id].updateState(selNode, event);
    };
};

KupuTool.prototype.enable = function() {
    // Called when the tool is enabled after a form is dismissed.
};

KupuTool.prototype.disable = function() {
    // Called when the tool is disabled (e.g. for a modal form)
};

function KupuToolBox() {
    /* Superclass for a user-interface object that controls a tool */
};

KupuToolBox.prototype.initialize = function(tool, editor) {
    /* store a reference to the tool and the editor */
    this.tool = tool;
    this.editor = editor;
};

KupuToolBox.prototype.updateState = function(selNode, event) {
    /* update the toolbox according to the current iframe's situation */
};

function noContextMenu(object) {
    /* Decorator for a tool to suppress the context menu */
    object.createContextMenuElements = function(selNode, event) {
        return [];
    };
    return object;
}

// Helper function for enabling/disabling tools
function kupuButtonDisable(button) {
    button = button || this.button;
    if (button) {
        button.disabled = "disabled";
        button.className += ' disabled';
    }
};

function kupuButtonEnable(button) {
    button = button || this.button;
    if (button) {
        button.disabled = "";
        button.className = button.className.replace(/ *\bdisabled\b/g, '');
    }
};

//----------------------------------------------------------------------------
// Implementations
//----------------------------------------------------------------------------

function KupuButton(buttonid, commandfunc, tool) {
    /* Base prototype for kupu button tools */
    if (arguments.length) {
        this.buttonid = buttonid;
        this.button = getFromSelector(buttonid);
        this.commandfunc = commandfunc;
        this.tool = tool;
        this.disable = kupuButtonDisable;
        this.enable = kupuButtonEnable;
    };
};

KupuButton.prototype = new KupuTool;

KupuButton.prototype.initialize = function(editor) {
    this.editor = editor;
    if (!this.button) return;
    addEventHandler(this.button, 'click', this.execCommand, this);
};

KupuButton.prototype.execCommand = function() {
    /* exec this button's command */
    this.commandfunc(this, this.editor, this.tool);
};

KupuButton.prototype.updateState = function(selNode, event) {
    /* override this in subclasses to determine whether a button should
        look 'pressed in' or not
    */
};

function KupuStateButton(buttonid, commandfunc, checkfunc, offclass, onclass) {
    /* A button that can have two states (e.g. pressed and
       not-pressed) based on CSS classes */
    this.buttonid = buttonid;
    this.button = getFromSelector(buttonid);
    this.commandfunc = commandfunc;
    this.checkfunc = checkfunc;
    this.offclass = offclass;
    this.onclass = onclass;
    this.pressed = false;

    this.execCommand = function() {
        /* exec this button's command */
        this.button.className = (this.pressed ? this.offclass : this.onclass);
        this.pressed = !this.pressed;
        this.editor.focusDocument();
        this.commandfunc(this, this.editor);
    };

    this.updateState = function(selNode, event) {
        /* check if we need to be clicked or unclicked, and update accordingly 
        
            if the state of the button should be changed, we set the class
        */
        if (!this.button) return;
        var currclass = this.button.className;
        var newclass = null;
        if (this.checkfunc(selNode, this, this.editor, event)) {
            newclass = this.onclass;
            this.pressed = true;
        } else {
            newclass = this.offclass;
            this.pressed = false;
        };
        if (currclass != newclass) {
            this.button.className = newclass;
        };
    };
};

KupuStateButton.prototype = new KupuButton;

/* Same as the state button, but the focusDocument call is delayed.
 * Mozilla&Firefox have a bug on windows which can cause a crash if you
 * change CSS positioning styles on an element which has focus.
 */
function KupuLateFocusStateButton(buttonid, commandfunc, checkfunc,
        offclass, onclass) {
    KupuStateButton.apply(this, [buttonid, commandfunc, checkfunc,
                                 offclass, onclass]);
}

KupuLateFocusStateButton.prototype = new KupuStateButton;

KupuLateFocusStateButton.prototype.execCommand = function() {
    /* exec this button's command */
    this.button.className = (this.pressed ? this.offclass : this.onclass);
    this.pressed = !this.pressed;
    this.commandfunc(this, this.editor);
    this.editor.focusDocument();
};

function KupuRemoveElementButton(buttonid, element_name, cssclass) {
    /* A button specialized in removing elements in the current node
       context. Typical usages include removing links, images, etc. */
    this.button = getFromSelector(buttonid);
    this.element_name = element_name;
    this.onclass = 'invisible';
    this.offclass = cssclass;
    this.pressed = false;
};

KupuRemoveElementButton.prototype = new KupuStateButton;

KupuRemoveElementButton.prototype.commandfunc = function(button, editor) {
    editor.focusDocument();
    editor.removeNearestParentOfType(editor.getSelectedNode(), this.element_name);
    editor.updateState();
};

KupuRemoveElementButton.prototype.checkfunc = function(currnode, button,
        editor, event) {
    var element = editor.getNearestParentOfType(currnode, this.element_name);
    return (element ? false : true);
};

function KupuUI(textstyleselectid) {
    /* View 
    
        This is the main view, which controls most of the toolbar buttons.
        Even though this is probably never going to be removed from the view,
        it was easier to implement this as a plain tool (plugin) as well.
    */
    
    // attributes
    this.tsselect = getFromSelector(textstyleselectid);
    this.paraoptions = [];
    this.tableoptions = [];
    this.styleoptions = [];
    this.tableoffset = 0;
    this.styleoffset = 0;
    this.tablegrp = null;
    this.optionstate = -1;
    this.otherstyle = null;
    this.tablestyles = {};
    this.charstyles = {};
    this.styles = {}; // use an object here so we can use the 'in' operator later on
    this.blocktagre = /^(p|div|h.|ul|ol|dl|menu|dir|pre|blockquote|address|center)$/i;
    this.spanre = /^span\b/i;
    this.tblre = /^thead|tbody|table|t[rdh]\b/i;
};


KupuUI.prototype = new KupuTool;

KupuUI.prototype.initialize = function(editor) {
    /* initialize the ui like tools */
    this.editor = editor;
    this.cleanStyles();
    this.enableOptions(false);
    if (this.tsselect) {
        this._selectevent = addEventHandler(this.tsselect, 'change', this.setTextStyleHandler, this);
    }
};

KupuUI.prototype.getStyles = function() {
    if (!this.paraoptions) {
        this.cleanStyles();
    }
    return [ this.paraoptions, this.tableoptions ];
};

KupuUI.prototype.setTextStyleHandler = function(event) {
    this.setTextStyle(this.tsselect.options[this.tsselect.selectedIndex].value);
};

// event handlers
KupuUI.prototype.basicButtonHandler = function(action) {
    /* event handler for basic actions (toolbar buttons) */
    this.editor.execCommand(action);
    this.editor.updateState();
};

KupuUI.prototype.saveButtonHandler = function() {
    /* handler for the save button */
    this.editor.saveDocument();
};

KupuUI.prototype.saveAndExitButtonHandler = function(redirect_url) {
    /* save the document and, if successful, redirect */
    this.editor.saveDocument(redirect_url);
};

KupuUI.prototype.cutButtonHandler = function() {
    try {
        this.editor.execCommand('Cut');
    } catch (e) {
        if (this.editor.getBrowserName() == 'Mozilla') {
            alert(_('Cutting from JavaScript is disabled on your Mozilla due to security settings. For more information, read http://www.mozilla.org/editor/midasdemo/securityprefs.html'));
        } else {
            throw e;
        };
    };
    this.editor.updateState();
};

KupuUI.prototype.copyButtonHandler = function() {
    try {
        this.editor.execCommand('Copy');
    } catch (e) {
        if (this.editor.getBrowserName() == 'Mozilla') {
            alert(_('Copying from JavaScript is disabled on your Mozilla due to security settings. For more information, read http://www.mozilla.org/editor/midasdemo/securityprefs.html'));
        } else {
            throw e;
        };
    };
    this.editor.updateState();
};

KupuUI.prototype.pasteButtonHandler = function() {
    try {
        this.editor.execCommand('Paste');
    } catch (e) {
        if (this.editor.getBrowserName() == 'Mozilla') {
            alert(_('Pasting from JavaScript is disabled on your Mozilla due to security settings. For more information, read http://www.mozilla.org/editor/midasdemo/securityprefs.html'));
        } else {
            throw e;
        };
    };
    this.editor.updateState();
};

KupuUI.prototype.cleanStyles = function() {
    if (!this.tsselect) return;
    var options = this.tsselect.options;
    var parastyles = this.styles;
    var tablestyles = this.tablestyles;
    var charstyles = this.charstyles;
    
    var normal = ['Normal', 'p|'];
    var td = ['Plain Cell', 'td|'];
    var nostyle = ['(remove style)', ''];

    var opts = [];
    while (options.length) {
        var opt = options[0];
        options[0] = null;
        var v = opt.value;
        if (v.indexOf('|') > -1) {
            var split = v.split('|');
            v = split[0].toLowerCase() + "|" + split[1];
        } else {
            v = v.toLowerCase()+"|";
        };
        var optarray = [opt.text, v];
        if (v=='td|') {
            td = optarray;
        } else if (v=='p|') {
            normal = optarray;
        } else if (v=='') {
            nostyle = optarray;
        } else {
            opts.push([opt.text,v]);
        }
    }
    this.tableoptions.push(td);
    tablestyles[td[1]] = 0;
    this.paraoptions.push(normal);
    parastyles[normal[1]] = 0;

    for (var i = 0; i < opts.length; i++) {
        optarray = opts[i];
        v = optarray[1];

        if (this.spanre.test(v)) {
            charstyles[v] = this.styleoptions.length;
            this.styleoptions.push(optarray);
        } else if (this.tblre.test(v)) {
            tablestyles[v] = this.tableoptions.length;
            this.tableoptions.push(optarray);
        } else {
            parastyles[v] = this.paraoptions.length;
            this.paraoptions.push(optarray);
        };
    };
    this.paraoptions.push(nostyle);
    this.styleoffset = this.paraoptions.length;
    this.tableoffset = this.styleoffset + this.styleoptions.length;
};

// Remove otherstyle and switch to appropriate style set.
KupuUI.prototype.enableOptions = function(inTable) {
    if (!this.tsselect) return;
    var select = this.tsselect;
    var options = select.options;
    if (this.otherstyle) {
        options[0] = null;
        this.otherstyle = null;
    }
    if (this.optionstate == inTable) return; /* No change */

    // while (select.firstChild) select.removeChild(select.firstChild);

    function option(info) {
        return newElement('option', {'value': info[1]}, [info[0]]);
    }
    if (this.optionstate==-1) {
        for (var i = 0; i < this.paraoptions.length; i++) {
            select.appendChild(option(this.paraoptions[i]));
        }
        if (this.styleoptions.length) {
            var grp = document.createElement('optgroup');
            grp.label = 'Character styles';
            for (var i = 0; i < this.styleoptions.length; i++) {
                grp.appendChild(option(this.styleoptions[i]));
            }
            select.appendChild(grp);
        }
    }
    if (inTable) {
        var grp = (this.tablegrp = document.createElement('optgroup'));
        grp.label = 'Table elements';
        for (var i = 0; i < this.tableoptions.length; i++) {
            grp.appendChild(option(this.tableoptions[i]));
        }
        select.appendChild(grp);
    } else {
        while (select.options[this.tableoffset]) {
            select.options[this.tableoffset] = null;
        };
        if (this.tablegrp) {
            select.removeChild(this.tablegrp);
            this.tablegrp = null;
        };
    };
    this.optionstate = inTable;
};

KupuUI.prototype.setIndex = function(currnode, tag, index, styles) {
    var className = currnode.className;
    this.styletag = tag;
    this.classname = className;
    var style = tag+'|'+className;

    if (style in styles) {
        return styles[style];
    } else if (!className && tag in styles) {
        return styles[tag];
    }
    return index;
};

KupuUI.prototype.nodeStyle = function(node) {
    var currnode = node;
    var index = -1;
    this.styletag = undefined;
    this.classname = '';

    // Set the table state correctly
    this.intable = false;

    while(currnode) {
        var tag = currnode.nodeName;
        if (/^body$/i.test(tag)) break;
        if (this.tblre.test(tag)) {
            this.intable = true;
            break;
        };
        currnode = currnode.parentNode;
    };
    currnode = node;
    while (currnode) {
        var tag = currnode.nodeName.toLowerCase();

        if (/^body$/.test(tag)) {
            if (!this.styletag) {
                // Forced style messes up in Firefox: return -1 to
                // indicate no style 
                return -1;
            }
            break;
        }
        if (this.spanre.test(tag)) {
            index = this.setIndex(currnode, tag, index, this.charstyles);
            if (index >= 0) return index+this.styleoffset; // span takes priority
        } else if (this.blocktagre.test(tag)) {
            index = this.setIndex(currnode, tag, index, this.styles);
        } else if (this.tblre.test(tag)) {
            if (index > 0) return index; // block or span takes priority.
            index = this.setIndex(currnode, tag, index, this.tablestyles);
            if (index >= 0 || tag=='table') {
                return index+this.tableoffset; // Stop processing if in a table
            }
        }
        currnode = currnode.parentNode;
    }
    return index;
};

KupuUI.prototype.updateState = function(selNode) {
    /* set the text-style pulldown */

    // first get the nearest style
    // search the list of nodes like in the original one, break if we encounter a match,
    // this method does some more than the original one since it can handle commands in
    // the form of '