AEM 6420 - Content Fragment Editor add Font size, Color, Background color RTE Plugin

Goal


Add an RTE plugin eaemTextFont to apply size, color, background color to the text in content fragment editor

Package validated on AEM 6.5.3.0

Demo | Package Install | Github


Minimized



Toolbar Plugin



Maximized



Saved in CRX



Solution


1) Login to CRXDE Lite, create folder (nt:folder) /apps/eaem-touchui-cfm-font-size-plugin

2) Create a Font selector cq:Page /apps/eaem-touchui-cfm-font-size-plugin/font-selector with the following code

<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:granite="http://www.adobe.com/jcr/granite/1.0" 
            xmlns:cq="http://www.day.com/jcr/cq/1.0" 
            xmlns:jcr="http://www.jcp.org/jcr/1.0" 
            xmlns:nt="http://www.jcp.org/jcr/nt/1.0"
    jcr:primaryType="cq:Page">
    <jcr:content
        jcr:mixinTypes="[sling:VanityPath]"
        jcr:primaryType="nt:unstructured"
        jcr:title="EAEM Font Selector"
        sling:resourceType="granite/ui/components/coral/foundation/page">
        <head jcr:primaryType="nt:unstructured">
            <favicon
                jcr:primaryType="nt:unstructured"
                sling:resourceType="granite/ui/components/coral/foundation/page/favicon"/>
            <viewport
                jcr:primaryType="nt:unstructured"
                sling:resourceType="granite/ui/components/coral/foundation/admin/page/viewport"/>
            <clientlibs
                jcr:primaryType="nt:unstructured"
                sling:resourceType="granite/ui/components/coral/foundation/includeclientlibs"
                categories="[coralui3,granite.ui.coral.foundation,granite.ui.shell,dam.gui.admin.coral, eaem-cfm.rte.plugin]"/>
        </head>
        <body
            jcr:primaryType="nt:unstructured"
            sling:resourceType="granite/ui/components/coral/foundation/page/body">
            <items jcr:primaryType="nt:unstructured">
                <form
                    jcr:primaryType="nt:unstructured"
                    sling:resourceType="granite/ui/components/coral/foundation/form"
                    class="foundation-form content-container"
                    maximized="{Boolean}true"
                    style="vertical">
                    <items jcr:primaryType="nt:unstructured">
                        <wizard
                            jcr:primaryType="nt:unstructured"
                            jcr:title="Select Text Font Color..."
                            sling:resourceType="granite/ui/components/coral/foundation/wizard">
                            <items jcr:primaryType="nt:unstructured">
                                <text
                                    jcr:primaryType="nt:unstructured"
                                    jcr:title="Select Text Font Color..."
                                    sling:resourceType="granite/ui/components/coral/foundation/container">
                                    <items jcr:primaryType="nt:unstructured">
                                        <fixedColumns
                                            jcr:primaryType="nt:unstructured"
                                            sling:resourceType="granite/ui/components/coral/foundation/fixedcolumns"
                                            margin="{Boolean}true">
                                            <items jcr:primaryType="nt:unstructured">
                                                <column
                                                    jcr:primaryType="nt:unstructured"
                                                    sling:resourceType="granite/ui/components/coral/foundation/container">
                                                    <items jcr:primaryType="nt:unstructured">
                                                        <fontSize
                                                            jcr:primaryType="nt:unstructured"
                                                            sling:resourceType="granite/ui/components/coral/foundation/form/select"
                                                            fieldDescription="Select text size"
                                                            fieldLabel="Size"
                                                            name="./size">
                                                            <items jcr:primaryType="nt:unstructured">
                                                                <def
                                                                    jcr:primaryType="nt:unstructured"
                                                                    text="Select Size"
                                                                    value=""/>
                                                                <small
                                                                    jcr:primaryType="nt:unstructured"
                                                                    text="Small (15px)"
                                                                    value="15px"/>
                                                                <medium
                                                                    jcr:primaryType="nt:unstructured"
                                                                    text="Medium (30px)"
                                                                    value="30px"/>
                                                                <large
                                                                    jcr:primaryType="nt:unstructured"
                                                                    text="Large (40px)"
                                                                    value="40px"/>
                                                            </items>
                                                        </fontSize>
                                                        <color
                                                            jcr:primaryType="nt:unstructured"
                                                            sling:resourceType="granite/ui/components/coral/foundation/form/colorfield"
                                                            fieldDescription="Select text color"
                                                            fieldLabel="Text Color"
                                                            name="./color"
                                                            showProperties="{Boolean}true"/>
                                                        <bgColor
                                                            jcr:primaryType="nt:unstructured"
                                                            sling:resourceType="granite/ui/components/coral/foundation/form/colorfield"
                                                            fieldDescription="Select background color"
                                                            fieldLabel="Background Color"
                                                            name="./bgColor"
                                                            showProperties="{Boolean}true"/>
                                                    </items>
                                                </column>
                                            </items>
                                        </fixedColumns>
                                    </items>
                                    <parentConfig jcr:primaryType="nt:unstructured">
                                        <prev
                                            granite:class="foundation-wizard-control"
                                            jcr:primaryType="nt:unstructured"
                                            sling:resourceType="granite/ui/components/coral/foundation/anchorbutton"
                                            text="Cancel">
                                            <granite:data
                                                jcr:primaryType="nt:unstructured"
                                                foundation-wizard-control-action="cancel"/>
                                        </prev>
                                        <next
                                            granite:class="foundation-wizard-control"
                                            jcr:primaryType="nt:unstructured"
                                            sling:resourceType="granite/ui/components/coral/foundation/button"
                                            disabled="{Boolean}true"
                                            text="Apply"
                                            type="submit"
                                            variant="primary">
                                            <granite:data
                                                jcr:primaryType="nt:unstructured"
                                                foundation-wizard-control-action="next"/>
                                        </next>
                                    </parentConfig>
                                </text>
                            </items>
                        </wizard>
                    </items>
                </form>
            </items>
        </body>
    </jcr:content>
</jcr:root>

3) Create clientlib (type cq:ClientLibraryFolder) /apps/eaem-touchui-cfm-font-size-plugin/clientlib and set property categories of String[] type to [dam.cfm.authoring.contenteditor.v2, eaem-cfm.rte.plugin] and dependencies String[] to [lodash]

4) Create file ( type nt:file ) /apps/eaem-touchui-cfm-font-size-plugin/clientlib/css.txt, add the following

  cfm-rte-font-size-plugin.css

5) Create file (type nt:file) /apps/eaem-touchui-cfm-font-size-plugin/clientlib/cfm-rte-font-size-plugin.css, add the following code

.eaem-cfm-font-size {
    width: 50%;
    margin-left: -50%;
    height: 53%;
    margin-top: -50%;
    box-sizing: content-box;
    z-index: 10100;
}

.eaem-cfm-font-size > iframe {
    width: 100%;
    height: 100%;
    border: 1px solid #888;
}

6) Create file ( type nt:file ) /apps/eaem-touchui-cfm-font-size-plugin/clientlib/js.txt, add the following

  cfm-rte-font-size-plugin.js

7) Create file (type nt:file) /apps/eaem-touchui-cfm-font-size-plugin/clientlib/cfm-rte-font-size-plugin.js, add the following code

(function ($, $document) {
    var EAEM_PLUGIN_ID = "eaemfont",
        EAEM_TEXT_FONT_FEATURE = "eaemTextFont",
        EAEM_TEXT_FONT_ICON = EAEM_PLUGIN_ID + "#" + EAEM_TEXT_FONT_FEATURE,
        CANCEL_CSS = "[data-foundation-wizard-control-action='cancel']",
        FONT_SELECTOR_URL = "/apps/eaem-touchui-cfm-font-size-plugin/font-selector.html",
        SENDER = "experience-aem", REQUESTER = "requester", $eaemFontPicker,
        url = document.location.pathname;

    if( url.indexOf("/editor.html") == 0 ){
        extendStyledTextEditor();
        registerPlugin();
    }else if(url.indexOf(FONT_SELECTOR_URL) == 0){
        handlePicker();
    }

    function handlePicker(){
        $document.on("foundation-contentloaded", fillDefaultValues);

        $document.on("click", CANCEL_CSS, sendCancelMessage);

        $document.submit(sentTextAttributes);
    }

    function setWidgetValue(form, selector, value){
        Coral.commons.ready(form.querySelector(selector), function (field) {
            field.value = _.isEmpty(value) ? "" : decodeURIComponent(value);
        });
    }

    function rgbToHex(color){
        if(_.isEmpty(color)){
            return color;
        }

        if(color.indexOf("rgb") == 0){
            color = CUI.util.color.RGBAToHex(color);
        }

        return color;
    }

    function fillDefaultValues(){
        var queryParams = queryParameters(),
            form = $("form")[0];

        setWidgetValue(form, "[name='./color']", queryParams.color);

        setWidgetValue(form, "[name='./size']", queryParams.size);

        setWidgetValue(form, "[name='./bgColor']", queryParams.bgColor);
    }

    function sentTextAttributes(){
        var message = {
            sender: SENDER,
            action: "submit",
            data: {}
        }, $form = $("form"), $field;

        _.each($form.find("[name^='./']"), function(field){
            $field = $(field);
            message.data[$field.attr("name").substr(2)] = $field.val();
        });

        parent.postMessage(JSON.stringify(message), "*");
    }

    function queryParameters() {
        var result = {}, param,
            params = document.location.search.split(/\?|\&/);

        params.forEach( function(it) {
            if (_.isEmpty(it)) {
                return;
            }

            param = it.split("=");
            result[param[0]] = param[1];
        });

        return result;
    }

    function sendCancelMessage(){
        var message = {
            sender: SENDER,
            action: "cancel"
        };

        getParent().postMessage(JSON.stringify(message), "*");
    }

    function getParent() {
        if (window.opener) {
            return window.opener;
        }

        return parent;
    }

    function closePicker(event){
        event = event.originalEvent || {};

        if (_.isEmpty(event.data)) {
            return;
        }

        var message, action;

        try{
            message = JSON.parse(event.data);
        }catch(err){
            return;
        }

        if (!message || message.sender !== SENDER) {
            return;
        }

        action = message.action;

        if(action === "submit"){
            $eaemFontPicker.eaemFontPlugin.editorKernel.execCmd(EAEM_TEXT_FONT_FEATURE, message.data);
        }

        var modal = $eaemFontPicker.data('modal');
        modal.hide();
        modal.$element.remove();
    }

    function extendStyledTextEditor(){
        var origFn = Dam.CFM.StyledTextEditor.prototype._start;

        Dam.CFM.StyledTextEditor.prototype._start = function(){
            addTextFontPluginSettings(this);
            origFn.call(this);
        }
    }

    function addTextFontPluginSettings(editor){
        var config = editor.$editable.data("config");

        config.rtePlugins[EAEM_PLUGIN_ID] = {
            features: "*"
        };

        config.uiSettings.cui.multieditorFullscreen.toolbar.push(EAEM_TEXT_FONT_ICON);
    }

    function registerPlugin(){
        var EAEM_CFM_TEXT_FONT_PLUGIN = new Class({
            toString: "eaemCFMTextFontPlugin",

            extend: CUI.rte.plugins.Plugin,

            textFontUI:  null,

            getFeatures: function () {
                return [ EAEM_TEXT_FONT_FEATURE ];
            },

            notifyPluginConfig: function (pluginConfig) {
                var defaults = {
                    tooltips: {}
                };

                defaults.tooltips[EAEM_TEXT_FONT_FEATURE] = {
                    title: "Set text font size, color, background color..."
                };

                CUI.rte.Utils.applyDefaults(pluginConfig, defaults);

                this.config = pluginConfig;
            },

            initializeUI: function (tbGenerator) {
                if (!this.isFeatureEnabled(EAEM_TEXT_FONT_FEATURE)) {
                    return;
                }

                this.textFontUI = new tbGenerator.createElement(EAEM_TEXT_FONT_FEATURE, this, false,
                                        this.config.tooltips[EAEM_TEXT_FONT_FEATURE]);

                tbGenerator.addElement(EAEM_TEXT_FONT_FEATURE, 999, this.textFontUI, 999);

                if (tbGenerator.registerIcon) {
                    tbGenerator.registerIcon(EAEM_TEXT_FONT_ICON, "textColor");
                }

                $(window).off('message', closePicker).on('message', closePicker);
            },

            isValidSelection: function(){
                var winSel = window.getSelection();
                return winSel && winSel.rangeCount == 1 && winSel.getRangeAt(0).toString().length > 0;
            },

            execute: function (pluginCommand, value, envOptions) {
                var context = envOptions.editContext;

                if (pluginCommand != EAEM_TEXT_FONT_FEATURE) {
                    return;
                }

                if(!this.isValidSelection()){
                    return;
                }

                var selection = CUI.rte.Selection.createProcessingSelection(context),
                    ek = this.editorKernel, startNode = selection.startNode;

                if ( (selection.startOffset === startNode.length) && (startNode != selection.endNode)) {
                    startNode = startNode.nextSibling;
                }

                var $tag = $(CUI.rte.Common.getTagInPath(context, startNode, "span")),
                    color, size = $tag.css("font-size");

                color = this.getColorAttributes($tag);

                this.showFontModal(this.getPickerIFrameUrl(size, color.color, color.bgColor));
            },

            getColorAttributes: function($tag){
                var key, color = { color: "", bgColor : ""};

                if(!$tag.attr("style")){
                    return color;
                }

                //donot use .css("color"), it returns default font color, if color is not set
                var parts = $tag.attr("style").split(";");

                _.each(parts, function(value){
                    value = value.split(":");

                    key = value[0] ? value[0].trim() : "";
                    value = value[1] ? value[1].trim() : "";

                    if(key == "color"){
                        color.color = rgbToHex(value);
                    }else if(key == "background-color"){
                        color.bgColor = rgbToHex(value);
                    }
                });

                return color;
            },

            showFontModal: function(url){
                var self = this, $iframe = $('<iframe>'),
                    $modal = $('<div>').addClass('eaem-cfm-font-size coral-Modal');

                $iframe.attr('src', url).appendTo($modal);

                $modal.appendTo('body').modal({
                    type: 'default',
                    buttons: [],
                    visible: true
                });

                $eaemFontPicker = $modal;

                $eaemFontPicker.eaemFontPlugin = self;

                $modal.nextAll(".coral-Modal-backdrop").addClass("cfm-coral2-backdrop");
            },

            getPickerIFrameUrl: function(size, color, bgColor){
                var url = Granite.HTTP.externalize(FONT_SELECTOR_URL) + "?" + REQUESTER + "=" + SENDER;

                if(!_.isEmpty(color)){
                    url = url + "&color=" + encodeURIComponent(color);
                }

                if(!_.isEmpty(bgColor)){
                    url = url + "&bgColor=" + encodeURIComponent(bgColor);
                }

                if(!_.isEmpty(size)){
                    url = url + "&size=" + size;
                }

                return url;
            }
        });

        var EAEM_CFM_TEXT_FONT_CMD = new Class({
            toString: "eaemCFMTextFontCmd",

            extend: CUI.rte.commands.Command,

            isCommand: function (cmdStr) {
                return (cmdStr.toLowerCase() == EAEM_TEXT_FONT_FEATURE);
            },

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

            getTagObject: function(textData) {
                var style = "";

                if(!_.isEmpty(textData.color)){
                    style = "color: " + textData.color + ";";
                }

                if(!_.isEmpty(textData.size)){
                    style = style + "font-size: " + textData.size + ";";
                }

                if(!_.isEmpty(textData.bgColor)){
                    style = style + "background-color: " + textData.bgColor;
                }

                return {
                    "tag": "span",
                    "attributes": {
                        "style" : style
                    }
                };
            },

            execute: function (execDef) {
                var textData = execDef.value, selection = execDef.selection,
                    nodeList = execDef.nodeList;

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

                var common = CUI.rte.Common,
                    context = execDef.editContext,
                    tagObj = this.getTagObject(textData);

                if(_.isEmpty(textData.size) && _.isEmpty(textData.color) && _.isEmpty(textData.bgColor)){
                    nodeList.removeNodesByTag(execDef.editContext, tagObj.tag, undefined, true);
                    return;
                }

                var tags = common.getTagInPath(context, selection.startNode, tagObj.tag);

                //remove existing color before adding new color
                if (tags != null) {
                    nodeList.removeNodesByTag(execDef.editContext, tagObj.tag, tags.attributes ? tags.attributes : undefined, true);
                }

                nodeList.surround(execDef.editContext, tagObj.tag, tagObj.attributes);
            },

            queryState: function(selectionDef, cmd) {
                return false;
            }
        });

        CUI.rte.plugins.PluginRegistry.register(EAEM_PLUGIN_ID, EAEM_CFM_TEXT_FONT_PLUGIN);

        CUI.rte.commands.CommandRegistry.register(EAEM_TEXT_FONT_FEATURE, EAEM_CFM_TEXT_FONT_CMD);
    }
}(jQuery, jQuery(document)));

11 comments:

  1. How to add this plug to a component? same as experience-aem?

    ReplyDelete
    Replies
    1. you mean this? http://experience-aem.blogspot.com/2018/05/aem-64-touch-ui-rte-rich-text-editor-color-picker-plugin.html

      Delete
    2. @sreekanth choudry can you share same plugin with same options for RTE?

      Delete
  2. Hi,

    I am trying to add h4 to the paraformat drop down for the same content fragment rte, right now it shows p,h1,h2,h3 only. Any suggestions in how to do this?

    ReplyDelete
  3. Hi,
    how can I add this plugin in text component.

    ReplyDelete
  4. Hi,

    in AEM 6.5 it is not working, I have this console error:

    Uncaught ReferenceError: Dam is not defined

    Any idea regarding this error?

    Regards.

    ReplyDelete
    Replies
    1. Were you able to resolve this issue ? Facing similar DAM issue now in aem 6.5

      Delete
  5. Hi,

    i have to create a content fragment model with a multifield. Multifield will consist of three fields 1.Text 2.pathbrowser 3.Summary.
    the 3rd field summary under multifield should be a rte.

    Any idea on how to proceed with this scenario?

    Regards,
    Komal

    ReplyDelete
  6. Hi,
    Can you please suggest how to enable font size selection in RTE field ?

    ReplyDelete
  7. Getting following error while adding : Uncaught ReferenceError: Dam is not defined
    How to resolve this issue

    ReplyDelete