AEM 6 SP1 - Rich Text Editor Classic UI Color Palette

Goal


Add a Color Picker to Rich Text Editor of Classic UI. A Touch UI RTE plugin is available here

Demo | Package Install  | [ No color field - Demo | Package Install ]

Demo shows dialog of foundation text component (/libs/foundation/components/text/dialog/items/tab1/items/text/rtePlugins) modified to add the color picker config. This is just for demonstration only (on Geometrixx pages), ideally the foundation components should never be altered...

Please leave a comment if you find bug / fix


Bug Fixes

Color palette shows up cropped on Firefox (bug screenshot, fix screenshot), reapplying color on single "p" block throws error - Tested on AEM 61 - Package Install


Picker with Default Colors


Configuration




Button



Picker



Custom Colors 


Configuration




Picker




Solution


1) Login to CRXDE Lite, create folder (nt:folder) /apps/rte-color-picker

2) Create clientlib (type cq:ClientLibraryFolder/apps/rte-color-picker/clientlib and set a property categories of String type to cq.widgets

3) Create file ( type nt:file ) /apps/rte-color-picker/clientlib/js.txt, add the following

                         color-picker.js

4) Create file (type nt:file) /apps/rte-color-picker/clientlib/color-picker.js, add the following code

CQ.Ext.ns("ExperienceAEM");

ExperienceAEM.ColorPicker = {
    ADD_COLOR_CMD : "addcolor"
};

ExperienceAEM.ColorPicker.Plugin = new Class({
    toString: "ColorPickerPlugin",
    extend: CUI.rte.plugins.Plugin,
    P: ExperienceAEM.ColorPicker,

    addPickerUI: null,

    getFeatures: function() {
        return [ this.P.ADD_COLOR_CMD ];
    },

    initializeUI: function(tbGenerator) {
        var plg = CUI.rte.plugins;

        if (this.isFeatureEnabled(this.P.ADD_COLOR_CMD)) {
            this.addPickerUI = tbGenerator.createElement(this.P.ADD_COLOR_CMD, this, true, "Add Color");
            tbGenerator.addElement("format", plg.Plugin.SORT_FORMAT,this.addPickerUI,1000);
        }
    },

    execute: function(cmd, value, env) {
        if (cmd == this.P.ADD_COLOR_CMD) {
            this.showDialog(env.editContext);
        }
    },

    showDialog: function(context) {
        var editorKernel = this.editorKernel, dm = editorKernel.getDialogManager();
        var config = this.config;

        var colorField = new CQ.form.ColorField({
            fieldLabel: "Hex Value",
            showHexValue: true
        });

        var colorPalette = {
            fieldLabel: "Select",
            labelStyle: "padding-top:15px",
            style: "padding-top:15px",
            xtype: "colorpalette",
            listeners: {
                select: function(t, sColor){
                    colorField.setValue(sColor);
                }
            }
        };

        if(config){
            if(config.defaultColor){
                colorPalette.value = config.defaultColor;
            }

            if(config.colors && config.colors.length > 0){
                colorPalette.colors = config.colors;
            }
        }

        var dialogConfig = {
            "jcr:primaryType": "cq:Dialog",
            title: "Color Picker",
            modal: true,
            width: 400,
            height: 250,
            items: [ {
                    xtype: "panel",
                    layout: "form",
                    padding: "20px 0 0 10px",
                    items: [ colorField, colorPalette ]
            }],
            ok: function() {
                this.close();

                var sColor = colorField.getValue();

                if(sColor){
                    editorKernel.relayCmd(ExperienceAEM.ColorPicker.ADD_COLOR_CMD, sColor );
                }
            }
        };

        var removeBtn = new CQ.Ext.Button( {
            text: "Remove Applied Color",
            width: 150,
            tooltip: 'Remove applied color and close dialog',
            handler: function(){
                this.close();
                editorKernel.relayCmd(ExperienceAEM.ColorPicker.ADD_COLOR_CMD);
            }
        });

        dialogConfig.buttons = [
            removeBtn,
            CQ.Dialog.OK,
            CQ.Dialog.CANCEL
        ];

        dm.show(CQ.WCM.getDialog(dialogConfig));
    },

    notifyPluginConfig: function(pluginConfig) {
        pluginConfig = pluginConfig || { };

        CUI.rte.Utils.applyDefaults(pluginConfig, {
            "tooltips": {
                "addcolor": {
                    "title": "Add Color",
                    "text": "Add Color"
                }
            }
        });

        this.config = pluginConfig;
    },

    updateState: function(selDef) {
        if(this.addPickerUI){
            this.addPickerUI.setSelected(false);
        }
    }
});

CUI.rte.plugins.PluginRegistry.register("colorpicker", ExperienceAEM.ColorPicker.Plugin);

ExperienceAEM.ColorPicker.Cmd = new Class({
    toString: "ColorPicker",
    extend: CUI.rte.commands.Command,

    P: ExperienceAEM.ColorPicker,

    isCommand: function(cmdStr) {
        return (cmdStr == this.P.ADD_COLOR_CMD);
    },

    getProcessingOptions: function() {
        var cmd = CUI.rte.commands.Command;
        return cmd.PO_SELECTION | cmd.PO_NODELIST;
    },

    addColor: function(execDef){
        var nodeList = execDef.nodeList,
            selection = execDef.selection;

        if(!nodeList || !selection){
            return;
        }

        try{
            nodeList.removeNodesByTag(execDef.editContext, "span", undefined, true);

            if(!execDef.value){
                return;
            }

            nodeList.surround(execDef.editContext, "span", { style: "color:#" + execDef.value } );
        }catch(err){
            console.log("Error applying or removing color - " + err);
        }
    },

    execute: function(execDef) {
        if(execDef.command == this.P.ADD_COLOR_CMD){
            this.addColor(execDef);
        }
    }
});

CUI.rte.commands.CommandRegistry.register("colorpicker", ExperienceAEM.ColorPicker.Cmd);

5) Add necessary CSS. Create folder (nt:folder) /apps/rte-color-picker/clientlib/themes

6) Create clientlib (type cq:ClientLibraryFolder/apps/rte-color-picker/clientlib/themes/default and set a property categories of String type to cq.widgets

7) Create file (nt:file) /apps/rte-color-picker/clientlib/themes/default/css.txt, add the following

                    css/color-picker.css

8) Create folder (nt:folder) /apps/rte-color-picker/clientlib/themes/default/css and add the icon addcolor.png

9) Create file (nt:file) /apps/rte-color-picker/clientlib/themes/default/css/color-picker.css, add the following code. RTE looks for css classes x-edit-addcolor (for the plugin toolbar button addcolor)

#CQ .x-html-editor-tb .x-edit-addcolor {
    background: url(addcolor.png) center no-repeat;
}

10) Add any text component with RichText editor and in the rtePlugins path of dialog add colorpicker node to enable color picker plugin



13 comments:

  1. I want to create custom video plugin. Here you are using xtype as colorpalette. What xtype needs to be used if we want develop video plugin? Could you please guide me to create video plugin in RTE?

    ReplyDelete
    Replies
    1. hello ezhil, not aware of video plugins xtypes. 'guess you can develop one that simply outputs html5 video tag, so in a dialog you select video and play it using the tag...

      Delete
  2. Hello.. I'm wondering if you could look at this related SO question.... http://stackoverflow.com/questions/28866912/color-picker-for-aem-component Thanks again for your awesome blog- tremendous resource!

    ReplyDelete
  3. Hi ,
    Do we have a rich text plugin or xtype for font size and family.Please share references.

    ReplyDelete
  4. Hello,

    To make it work I need to add the following statement:

    nodeList.commonAncestor = nodeList.nodes[0].dom.parentElement;

    after the line 154 above.
    This is because commonAncestor reference in nodeList is not updates correctly in function removeNodesByTag
    causing exception in line 160.

    Do you have the same issue?

    Thanks in advance.

    Regards,
    Ana.

    ReplyDelete
    Replies
    1. Ana, i think you are hitting the issue "reapplying color on single p block" - check package install the bugfixes section above

      Delete
    2. Thank you for the answer.
      I didn't see the pacakge bugfixes.

      Delete
  5. Hello,

    Thank you for sharing your code!

    I had a task to wrap selected text along with its parent tag (no matter how many tags there) with some div. It was easier for me to use jQuery so if anyone interested, sharing my code here:

    addColor: function(execDef) {
    var nodeList = execDef.nodeList,
    selection = execDef.selection,
    nodes = nodeList.nodes, // selected tags
    wrapperEl = "here goes your HTML with class what you want",
    selectedTags = [],
    $ = jQuery;

    if (!nodeList || !selection) {
    return;
    }

    try {

    for (var i = 0; i < nodes.length; i++) {
    if (nodes.length > 1) {
    selectedTags.push(nodes[i].dom); // prepare array to wrap merged items into one div with background
    } else {
    selectedTags.push(nodes[i].dom.parentElement); // correct array if only 1 tag selected
    }
    };

    if(!execDef.value){
    $(selectedTags).unwrap(); // removing wrapper with background (deletes it from dom)
    return;
    }

    $(selectedTags).wrapAll(wrapperEl).parent().css({"background-color": "#" + execDef.value });

    } catch(err) {
    console.log("Error applying or removing color - " + err);
    }
    },

    Hope it helps somebody :)

    Regards,
    Natalie

    ReplyDelete
  6. Hello,

    I had try to color picker to AEM 6.1.
    But I found some issue with IE.

    Can not changed the color. And source is different like below.

    //IE
    <P>Test<span style="color: rgb(255, 0, 0);"></span></P>

    //Chrome
    <P><span style="color: #FF0000;">Test</span></P>

    Do you have the same issue?
    And Do you have the solution?

    Thanks

    ReplyDelete
  7. This comment has been removed by the author.

    ReplyDelete
  8. Hi Sreekanth,

    Thanks for the plugin its working great...:)

    ReplyDelete
  9. The hex value is not retained in the color picker though the changed color is displayed correctly with the component (e.g. if you change the text color from #ffffff to #fed141, hit ok and then open the color picker again, the hex value displays #ffffff even though the color displayed is #fed141)
    How i can fix it?

    ReplyDelete
  10. This comment has been removed by the author.

    ReplyDelete