AEM 6540 - Core Components 280 - Touch UI RTE (Rich Text Editor) Dialog Emojis Plugin


Touch UI Emoji Picker Plugin for RTE (Rich Text Editor) Dialog - /libs/cq/gui/components/authoring/dialog/richtext

In Core Components 2.8.0 uiSettings/cui/dialogFullScreen was removed and the plugin settings were moved to Template (Component) Policies, this post is for core components >= 2.8.0 

Uses Open Emoji API https://emoji-api.com/

For demo purposes, design dialog of core text component v2 was modified to add the emoji picker configuration - /apps/core/wcm/components/text/v2/text/cq:design_dialog/content/items/tabs/items/plugins/items/experience-aem-emojis



Emoji Plugin



Plugin Configuration in Design





Enable Plugin in Component Policy



Let's Do It

1) Login to CRXDE Lite, add nt:folder /apps/eaem-emojis-rte-plugin

2) To show the Emoji Picker in a dialog create cq:Page /apps/eaem-emojis-rte-plugin/emoji-selector and add  eaem.rte.emojis.plugin categories in /apps/eaem-emojis-rte-plugin/emoji-selector/jcr:content/head/clientlibs

<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/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 Emoji 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.rte.emojis.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">
                        <emoji
                            jcr:primaryType="nt:unstructured"
                            jcr:title="Click on the Emoji select.."
                            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">
                                                <emojiPicker
                                                    jcr:primaryType="nt:unstructured"
                                                    sling:resourceType="/apps/eaem-emojis-rte-plugin/emoji-widget"
                                                    fieldDescription="Select Emoji..."
                                                    fieldLabel="Select Emoji..."
                                                    name="./eaemEmoji"/>
                                            </items>
                                        </column>
                                    </items>
                                </fixedColumns>
                            </items>
                        </emoji>
                    </items>
                </form>
            </items>
        </body>
    </jcr:content>
</jcr:root>

3) Create widget /apps/eaem-emojis-rte-plugin/emoji-widget/emoji-widget.jsp to add a div container, search code for the emojis...

<%@ include file="/libs/granite/ui/global.jsp" %>

<ui:includeClientLib categories="lodash" />

<%
    String defaultKeyword = "face";
%>

<div class="coral-Form-fieldwrapper">
    <label class="coral-Form-fieldlabel">Search</label>
    <input is="coral-textfield" class="coral-Form-field" placeholder="<%= defaultKeyword %>"
           id="eaem-emoji-input"
           value="<%= defaultKeyword %>">
</div>

<div class="coral-Form-fieldwrapper" id="eaem-emojis">
</div>

<script>
    (function($){
        var URL = "https://emoji-api.com/emojis?access_key=bcbd6980f7392ceda8914932404927e9b198486e&search=";

        function showEmojis(keyword) {
            $.ajax(URL + keyword).done(handler);

            function handler(data) {
                var $emojis = $("#eaem-emojis"), html = "";

                _.each(data, function (emoji) {
                    html = html + getEmojiHtml(emoji["character"]);
                });

                $emojis.html(html);
            }

            function getEmojiHtml(character) {
                return "<span style='cursor:pointer'>" + character + "</span>";
            }
        }

        function addListener(){
            $("form").on("submit", function(){
                showEmojis($("#eaem-emoji-input").val());
                return false;
            });
        }

        showEmojis("<%=defaultKeyword%>");

        addListener();
    }(jQuery));
</script>

4) Create clientlib (cq:ClientLibraryFolder) /apps/eaem-emojis-rte-plugin/clientlib set property categories to [cq.authoring.dialog.all, eaem.rte.emojis.plugin] and dependencies to [lodash]

5) Create file (nt:file) /apps/eaem-emojis-rte-plugin/clientlib/js.txt, add the following content

                                    eaem-emojis.js

6) Create file (nt:file) /apps/eaem-emojis-rte-plugin/clientlib/eaem-emojis.js, add the following code

(function($, CUI, $document){
    var GROUP = "experience-aem-emojis",
        INSERT_EMOJI_FEATURE = "insertEmoji",
        EAEM_INSERT_EMOJI_DIALOG = "eaemTouchUIInsertEmojiDialog",
        SENDER = "experience-aem", REQUESTER = "requester",
        FONT_SELECTOR_URL = "/apps/eaem-emojis-rte-plugin/emoji-selector.html",
        url = document.location.pathname;

    if( url.indexOf(FONT_SELECTOR_URL) == 0 ){
        handlePicker();
        return;
    }

    function handlePicker(){
        $document.on("click", "#eaem-emojis span", addEmojiSelectListener);
    }

    function addEmojiSelectListener(){
        var message = {
            sender: SENDER,
            action: "submit",
            data: {
                emoji: $(this).html()
            }
        };

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

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

        return parent;
    }

    addPlugin();

    addPluginToDefaultUISettings();

    addDialogTemplate();

    function addDialogTemplate(){
        var url = Granite.HTTP.externalize(FONT_SELECTOR_URL) + "?" + REQUESTER + "=" + SENDER;

        var html = "<iframe width='700px' height='300px' frameBorder='0' src='" + url + "'></iframe>";

        if(_.isUndefined(CUI.rte.Templates)){
            CUI.rte.Templates = {};
        }

        if(_.isUndefined(CUI.rte.templates)){
            CUI.rte.templates = {};
        }

        CUI.rte.templates['dlg-' + EAEM_INSERT_EMOJI_DIALOG] = CUI.rte.Templates['dlg-' + EAEM_INSERT_EMOJI_DIALOG] = Handlebars.compile(html);
    }

    function addPluginToDefaultUISettings(){
        var groupFeature = GROUP + "#" + INSERT_EMOJI_FEATURE,
            toolbar = CUI.rte.ui.cui.DEFAULT_UI_SETTINGS.dialogFullScreen.toolbar;

        if(toolbar.includes(groupFeature)){
            return;
        }

        toolbar.splice(3, 0, groupFeature);
    }

    var EAEMInsertEmojiDialog = new Class({
        extend: CUI.rte.ui.cui.AbstractDialog,

        toString: "EAEMInsertEmojiDialog",

        initialize: function(config) {
            this.exec = config.execute;
        },

        getDataType: function() {
            return EAEM_INSERT_EMOJI_DIALOG;
        }
    });

    function addPlugin(){
        var EAEMTouchUIInsertEmojiPlugin = new Class({
            toString: "EAEMTouchUIInsertEmojiPlugin",

            extend: CUI.rte.plugins.Plugin,

            pickerUI: null,

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

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

                addPluginToDefaultUISettings();

                if (!this.isFeatureEnabled(INSERT_EMOJI_FEATURE)) {
                    return;
                }

                this.pickerUI = tbGenerator.createElement(INSERT_EMOJI_FEATURE, this, false, { title: "Select Emoji..." });
                tbGenerator.addElement(GROUP, plg.Plugin.SORT_FORMAT, this.pickerUI, 10);

                var groupFeature = GROUP + "#" + INSERT_EMOJI_FEATURE;
                tbGenerator.registerIcon(groupFeature, "heart");
            },

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

                if (pluginCommand != INSERT_EMOJI_FEATURE) {
                    return;
                }

                var dialog,dm = ek.getDialogManager(),
                    $container = CUI.rte.UIUtils.getUIContainer($(context.root)),
                    propConfig = {
                        'parameters': {
                            'command': this.pluginId + '#' + INSERT_EMOJI_FEATURE
                        }
                    };

                if(this.eaemInsertEmojiDialog){
                    dialog = this.eaemInsertEmojiDialog;

                    dialog.$dialog.find("iframe").attr("src", this.getPickerIFrameUrl());
                }else{
                    dialog = new EAEMInsertEmojiDialog();

                    dialog.attach(propConfig, $container, this.editorKernel);

                    dialog.$dialog.find("iframe").attr("src", this.getPickerIFrameUrl());

                    this.eaemInsertEmojiDialog = dialog;

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

                dm.show(dialog);

                function receiveMessage(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"){
                        ek.relayCmd(pluginCommand, message.data);
                    }

                    dialog.hide();
                }
            },

            getPickerIFrameUrl: function(){
                return Granite.HTTP.externalize(FONT_SELECTOR_URL) + "?" + REQUESTER + "=" + SENDER;
            },

            updateState: function(selDef) {
                var hasUC = this.editorKernel.queryState(INSERT_EMOJI_FEATURE, selDef);

                if (this.pickerUI != null) {
                    this.pickerUI.setSelected(hasUC);
                }
            }
        });

        var EAEMTouchUIEmojiCmd = new Class({
            toString: "EAEMTouchUIEmojiCmd",

            extend: CUI.rte.commands.Command,

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

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

            execute: function (execDef) {
                var emoji = execDef.value.emoji, context = execDef.editContext,
                    emojiSpan = context.doc.createElement("span");

                emojiSpan.innerHTML = emoji;

                var range = CUI.rte.Common.ua.isIE ? CUI.rte.Selection.saveNativeSelection(context)
                                        : CUI.rte.Selection.getLeadRange(context);

                range.insertNode(emojiSpan);
            },

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

        CUI.rte.commands.CommandRegistry.register(INSERT_EMOJI_FEATURE, EAEMTouchUIEmojiCmd);

        CUI.rte.plugins.PluginRegistry.register(GROUP,EAEMTouchUIInsertEmojiPlugin);
    }
}(jQuery, window.CUI,jQuery(document)));


1 comment: