AEM · AEM 6

AEM | RTE – Custom Styles Plugin – Classic UI


Goal: Create RTE (Rich Text Editor) fontstyle & fontcolor plugin for Classic UI. A Similar Touch UI RTE Extension is available here

Note: Please see RTE – Custom Styles Plugin for understanding of Custom Styles plugin requirement.

Download: Source Code | Package Install

Preview

RTE Classic UI - Styles plugin
RTE Classic UI – Styles plugin

Solution

  1. Login to CRXDE Lite (http://localhost:4502/crx/de) and create folder /apps/rte/plugins/classic-ui-styles
  2. Create node /apps/rte/plugins/classic-ui-styles/clientlib of type cq:ClientLibraryFolder and add a String property categories with value cq.widgets
  3. Create file (nt:file) /apps/rte/plugins/classic-ui-styles/clientlib/js.txt and add following files
    #base=./js
    commands/SCStyle.js
    toolbar/SCStylesSelectorImpl.js
    toolbar/SCToolbarBuilder.js
    fontstyles.js
    fontcolor.js
  4. Create file (nt:file) /apps/rte/plugins/classic-ui-styles/clientlib/commands/SCStyle.js and add the following code to create styles command.
    CQ.Ext.ns("SC.rte.commands");
    /*************************************************************************
    * RTE Plugins
    * ___________________
    * Styles Command manager for following plugins:
    * 1. fontstyles
    * 2. fontcolor
    *
    * @author Mohit K. Bansal
    **************************************************************************/
    SC.rte.commands.SCStyle = new Class({
    
        toString: "SCStyle",
    
        extend: CUI.rte.commands.Command,
    
        addStyle: function(execDef) {
            var sel = CUI.rte.Selection;
            var com = CUI.rte.Common;
            var styleName = execDef.value.val;
            var styleList = execDef.value.styles;
            var selection = execDef.selection;
            var context = execDef.editContext;
            // handle DOM elements
            var selectedDom = sel.getSelectedDom(context, selection);
            var styleableObjects = CUI.rte.plugins.StylesPlugin.STYLEABLE_OBJECTS;
            if (selectedDom && com.isTag(selectedDom, styleableObjects)) {
                com.removeAllClasses(selectedDom);
                com.addClass(selectedDom, styleName);
                return;
            }
            // handle text fragments
            var nodeList = execDef.nodeList;
    
            if (nodeList) {
                if(selection.startNode.parentNode.tagName == "SPAN") {
                    var newStyles = [];
                    var existingStyles = selection.startNode.parentNode.className.split(" ");
                    for(var i=0; i<existingStyles.length;i++) {
                        var status = true;
                        for(var j=0; j list of span tags?
            return false;
        }
    
    });
    
    // register command
    CUI.rte.commands.CommandRegistry.register("_scstyle", SC.rte.commands.SCStyle);

    Download: Source Code | Package Install

  5. Create file (nt:file) /apps/rte/plugins/classic-ui-styles/clientlib/commands/SCStylesSelectorImpl.js and add the following code to create Styles Selector.
    CQ.Ext.ns("SC.rte.plugins");
    /*************************************************************************
    * RTE Plugins
    * ___________________
    * Create ACT Style toolbar renderer for following plugins:
    * 1. fontstyles
    * 2. fontcolor
    *
    * @author Mohit K. Bansal
    **************************************************************************/
    SC.rte.plugins.SCStylesSelectorImpl = new Class({
        toString: "SCStyleSelectorImpl",
    
        extend: CUI.rte.ui.TbStyleSelector,
    
        // Interface implementation ------------------------------------------------------------
        addToToolbar: function(toolbar) {
        var com = CUI.rte.Common;
        this.toolbar = toolbar;
        if (com.ua.isIE) {
            // the regular way doesn't work for IE anymore with Ext 3.1.1, hence working
            // around
            var helperDom = document.createElement("span");
            helperDom.innerHTML = "<select class=\"x-font-select\">"
                + this.createStyleOptions() + "</span>";
            this.styleSelector = CQ.Ext.get(helperDom.childNodes[0]);
        } else {
            this.styleSelector = CQ.Ext.get(CQ.Ext.DomHelper.createDom({
                tag: "select",
                cls: "x-font-select",
                html: this.createStyleOptions()
            }));
        }
        this.initializeSelector();
        var title = this.id.replace("font", "");
        toolbar.add(
            CQ.I18n.getMessage(title.charAt(0).toUpperCase() + title.slice(1)),
            " ",
            this.styleSelector.dom);
     },
    
     createToolbarDef: function() {
        return [ {
            "xtype": "panel",
            "itemId": this.id,
            "html": "<select class=\"x-font-select\">"
                + this.createStyleOptions() + "</span>",
            "listeners": {
                "afterrender": function() {
                    var item = this.toolbar.items.get(this.id);
                    if (item && item.body) {
                        this.styleSelector = CQ.Ext.get(item.body.dom.childNodes[0]);
                        this.initializeSelector();
                    }
                },
                "scope": this
            }
        }];
     },
    
     initializeSelector: function() {
        this.styleSelector.on('change', function() {
        var style = this.styleSelector.dom.value;
        if (style.length > 0) {
        }
        this.plugin.execute(this.id);
     }, this);
     this.styleSelector.on('focus', function() {
        this.plugin.editorKernel.isTemporaryBlur = true;
     }, this);
     // fix for a Firefox problem that adjusts the combobox' height to the height
     // of the largest entry
     this.styleSelector.setHeight(19);
     },
    
     getSelectorDom: function() {
         return this.styleSelector.dom;
     },
    
     getSelectedStyle: function() {
        var style = this.styleSelector.dom.value;
        if (style.length > 0) {
            return style;
        }
        return null;
     },
    
     selectStyles: function(styles, selDef) {
        var indexToSelect;
        var styleableObject = selDef.selectedDom;
        var selectorDom = this.getSelectorDom();
        if (styles.length == 0) {
            indexToSelect = 0;
        } else if (styles.length > 1) {
            indexToSelect = -1;
        } else {
            if (selDef.isContinuousStyle || styleableObject) {
                var styleToSelect = styles[0];
                var options = selectorDom.options;
                for (var optIndex = 0; optIndex < options.length; optIndex++) {
                    var optionToCheck = options[optIndex];
                    if (optionToCheck.value == styleToSelect) {
                        indexToSelect = optIndex;
                        break;
                    }
                }
            } else {
                indexToSelect = -1;
            }
         }
         selectorDom.selectedIndex = indexToSelect;
         if (styleableObject != null) {
             selectorDom.disabled = false;
         } else if (selDef.isSelection) {
             selectorDom.disabled = false;
         } else {
             selectorDom.disabled = true;
         }
       }
    });

    Download: Source Code | Package Install

  6. Create file (nt:file) /apps/rte/plugins/classic-ui-styles/clientlib/commands/SCToolbarBuilder.js and add the following code to invoke custom toolbar.
    /*************************************************************************
    * RTE Plugins
    * ___________________
    * Extend toolbar builder to register different commands
    *
    * @author Mohit K. Bansal
    **************************************************************************/
    (function() {
        CQ.Ext.override(CUI.rte.ui.ext.ExtToolbarBuilder, {
            createSCStyleSelector: function(id, plugin, tooltip, styles) {
                return new SC.rte.plugins.SCStylesSelectorImpl(id, plugin, false, tooltip,
                        false, undefined, styles);
            }
        });
    }());

    Download: Source Code | Package Install

  7. Create file (nt:file) /apps/rte/plugins/classic-ui-styles/clientlib/fontstyles.js and add the following code to create Font styles plugin.
    CQ.Ext.ns("SC.rte.plugins");
    /*************************************************************************
    * RTE Plugins
    * ___________________
    * Font Style Plugin
    *
    * @author Mohit K. Bansal
    **************************************************************************/
    SC.rte.plugins.Fontstyles = new Class({
        toString: "Fontstyles",
        extend: CUI.rte.plugins.Plugin,
        /**
         * @private
         */
        cachedStyles: null,
    
        /**
         * @private
         */
        stylesUI: null,
    
        getFeatures: function() {
            return [ "fontstyles" ];
        },
    
    	getStyles: function() {
            var com = CUI.rte.Common;
            if (!this.cachedStyles) {
                this.cachedStyles = this.config.styles;
                if (this.cachedStyles) {
                    // take styles from config
                    com.removeJcrData(this.cachedStyles);
                    this.cachedStyles = com.toArray(this.cachedStyles, "cssName", "text");
                } else {
                    this.cachedStyles = [ ];
                }
            }
            return this.cachedStyles;
        },
    
        setStyles: function(styles) {
            this.cachedStyles = styles;
        },
    
        hasStylesConfigured: function() {
            return !!this.config.styles;
        },
    
        initializeUI: function(tbGenerator) {
            var plg = CUI.rte.plugins;
            var ui = CUI.rte.ui;
            if (this.isFeatureEnabled("fontstyles")) {
                this.stylesUI = tbGenerator.createSCStyleSelector("fontstyles", this, null,
                        this.getStyles());
                tbGenerator.addElement("fontstyles", plg.Plugin.SORT_PARAFORMAT, this.stylesUI,
                        500);
            }
        },
    
    	notifyPluginConfig: function(pluginConfig) {
            pluginConfig = pluginConfig || { };
            CUI.rte.Utils.applyDefaults(pluginConfig, { });
            this.config = pluginConfig;
        },
    
        execute: function(cmdId, styleDef) {
    		if (!this.stylesUI) {
                return;
            } 
            var cmd = null;
            var value = null;
            switch (cmdId.toLowerCase()) {
                case "fontstyles":
                    cmd = "applyscstyle";
                    value = (styleDef != null ? styleDef : this.stylesUI.getSelectedStyle());
                    break;
            }
            if (cmd) {
                var confValue = {
                    val: value,
                    styles: this.cachedStyles
                };
                this.editorKernel.relayCmd(cmd, confValue);
            }
        },
    
        updateState: function(selDef) {
            if (!this.stylesUI) {
                return;
            }
    		var com = CUI.rte.Common;
            var styles = selDef.styles;
            var actualStyles = [ ];
            var s;
            var styleableObject = selDef.selectedDom;
    		if (styleableObject) {
                if (!CUI.rte.Common.isTag(selDef.selectedDom,
                        CUI.rte.plugins.StylesPlugin.STYLEABLE_OBJECTS)) {
                    styleableObject = null;
                }
            }
    		var stylesDef = this.getStyles();
            var styleCnt = stylesDef.length;
    		if (styleableObject) {
                for (s = 0; s < styleCnt; s++) {
                    var styleName = stylesDef[s].cssName;
                    if (com.hasCSS(styleableObject, styleName)) {
                        actualStyles.push({
                            "className": styleName
                        });
                    }
                }
            } else {
                var checkCnt = styles.length;
                for (var c = 0; c < checkCnt; c++) {
                    var styleToProcess = styles[c];
                    var currentStyles = styleToProcess.className.split(" ");
                    for(var j=0; j<currentStyles.length; j++) {
                        for (s = 0; s < styleCnt; s++) {
                           	if (stylesDef[s].cssName == currentStyles[j]) {
                                actualStyles.push(currentStyles[j]);
                                break;
                            }
                        }
    				}
                }
            }
            this.stylesUI.selectStyles(actualStyles, selDef);
        }
    
    });
    
    // register plugin
    CUI.rte.plugins.PluginRegistry.register("fontstyles",
            SC.rte.plugins.Fontstyles);

    Download: Source Code | Package Install

  8. Create file (nt:file) /apps/rte/plugins/classic-ui-styles/clientlib/fontcolor.js and add the following code to create Font color plugin.
    CQ.Ext.ns("SC.rte.plugins");
    /*************************************************************************
    *
    * RTE Plugins
    * ___________________
    *
    * Font Color Plugin
    *
    * @author Mohit K. Bansal
    *
    *
    **************************************************************************/
    SC.rte.plugins.Fontcolor = new Class({
        toString: "Fontcolor",
        extend: CUI.rte.plugins.Plugin,
        /**
         * @private
         */
        cachedStyles: null,
    
        /**
         * @private
         */
        stylesUI: null,
    
        getFeatures: function() {
            return [ "fontcolor" ];
        },
    
    	getStyles: function() {
            var com = CUI.rte.Common;
            if (!this.cachedStyles) {
                this.cachedStyles = this.config.styles;
                if (this.cachedStyles) {
                    // take styles from config
                    com.removeJcrData(this.cachedStyles);
                    this.cachedStyles = com.toArray(this.cachedStyles, "cssName", "text");
                } else {
                    this.cachedStyles = [ ];
                }
            }
            return this.cachedStyles;
        },
    
        setStyles: function(styles) {
            this.cachedStyles = styles;
        },
    
        hasStylesConfigured: function() {
            return !!this.config.styles;
        },
    
        initializeUI: function(tbGenerator) {
            var plg = CUI.rte.plugins;
            var ui = CUI.rte.ui;
            if (this.isFeatureEnabled("fontcolor")) {
                this.stylesUI = tbGenerator.createSCStyleSelector("fontcolor", this, null,
                        this.getStyles());
                tbGenerator.addElement("fontcolor", plg.Plugin.SORT_PARAFORMAT, this.stylesUI,
                        800);
            }
        },
    
    	notifyPluginConfig: function(pluginConfig) {
            pluginConfig = pluginConfig || { };
            CUI.rte.Utils.applyDefaults(pluginConfig, { });
            this.config = pluginConfig;
        },
    
        execute: function(cmdId, styleDef) {
    		if (!this.stylesUI) {
                return;
            } 
            var cmd = null;
            var value = null;
            switch (cmdId.toLowerCase()) {
                case "fontcolor":
                    cmd = "applyscstyle";
                    value = (styleDef != null ? styleDef : this.stylesUI.getSelectedStyle());
                    break;
            }
            if (cmd) {
                var confValue = {
                    val: value,
                    styles: this.cachedStyles
                };
                this.editorKernel.relayCmd(cmd, confValue);
            }
        },
    
        updateState: function(selDef) {
            if (!this.stylesUI) {
                return;
            }
    		var com = CUI.rte.Common;
            var styles = selDef.styles;
            var actualStyles = [ ];
            var s;
            var styleableObject = selDef.selectedDom;
    		if (styleableObject) {
                if (!CUI.rte.Common.isTag(selDef.selectedDom,
                        CUI.rte.plugins.StylesPlugin.STYLEABLE_OBJECTS)) {
                    styleableObject = null;
                }
            }
    		var stylesDef = this.getStyles();
            var styleCnt = stylesDef.length;
    		if (styleableObject) {
                for (s = 0; s < styleCnt; s++) {
                    var styleName = stylesDef[s].cssName;
                    if (com.hasCSS(styleableObject, styleName)) {
                        actualStyles.push({
                            "className": styleName
                        });
                    }
                }
            } else {
                var checkCnt = styles.length;
                for (var c = 0; c < checkCnt; c++) {
                    var styleToProcess = styles[c];
                    var currentStyles = styleToProcess.className.split(" ");
                    for(var j=0; j<currentStyles.length; j++) {
                        for (s = 0; s < styleCnt; s++) {
                            if (stylesDef[s].cssName == currentStyles[j]) {
                                actualStyles.push(currentStyles[j]);
                                break;
                            }
                        }
    				}
                }
            }
            this.stylesUI.selectStyles(actualStyles, selDef);
        }
    
    });
    
    
    // register plugin
    CUI.rte.plugins.PluginRegistry.register("fontcolor",
            SC.rte.plugins.Fontcolor);

Download: Source Code | Package Install

8 thoughts on “AEM | RTE – Custom Styles Plugin – Classic UI

  1. Mohit, that was a spectacular work from your side. You saved a lot of time. Kindly keep posting such posts. I haven’t checked the packages posted here, but in the “SCStyle.js” you have missed some lines of code i guess. Kindly update it so that it might be helpful for all. I’ve corrected it myself by using your touch UI blog.

Leave a comment