Goal
Touch UI Color Picker Plugin for RTE (Rich Text Editor) Dialog - /libs/cq/gui/components/authoring/dialog/richtext
For a similar extension on 6.5.17 with custom colors from data source Check Github | 64 check this post | 63 check this post | 62 check this post | 61 check this post
In Core Components 2.8.0 uiSettings/cui/dialogFullScreen was removed and the plugin settings were moved to Template Policies, for making the RTE Color plugin work with Core Components 2.8.0 check this post
For demo purposes, dialog of core text component v2 was modified to add the color picker configuration - /apps/core/wcm/components/text/v2/text/cq:dialog/content/items/tabs/items/properties/items/columns/items/column/items/text/rtePlugins
Demo | Package Install | Github
Plugin Configuration
Add node experience-aem under rtePlugins and set features=*
Add experience-aem#colorPicker to uiSettings > cui > dialogFullScreen for showing the color palette toolbar icon in full screen
RTE Toolbar Icon
Color Picker in Full Screen
Color applied to text
Text with Color in CRX
Solution
1) Login to CRXDE Lite, add nt:folder /apps/eaem-touchui-dialog-rte-color-picker
2) To show the color picker in a dialog create /apps/eaem-touchui-dialog-rte-color-picker/color-picker-popover of type sling:Folder and /apps/eaem-touchui-dialog-rte-color-picker/color-picker-popover/cq:dialog of type nt:unstructured
<?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="nt:unstructured" jcr:title="Color Picker" sling:resourceType="cq/gui/components/authoring/dialog"> <content jcr:primaryType="nt:unstructured" sling:resourceType="granite/ui/components/foundation/container"> <layout jcr:primaryType="nt:unstructured" sling:resourceType="granite/ui/components/foundation/layouts/fixedcolumns" margin="{Boolean}false"/> <items jcr:primaryType="nt:unstructured"> <column jcr:primaryType="nt:unstructured" sling:resourceType="granite/ui/components/foundation/container"> <items jcr:primaryType="nt:unstructured"> <picker jcr:primaryType="nt:unstructured" sling:resourceType="granite/ui/components/coral/foundation/form/colorfield" name="./color"> <items jcr:primaryType="nt:unstructured"> <red jcr:primaryType="nt:unstructured" name="Red" value="#FF0000"/> <green jcr:primaryType="nt:unstructured" name="Green" value="#00FF00"/> <blue jcr:primaryType="nt:unstructured" name="Blue" value="#0000FF"/> <black jcr:primaryType="nt:unstructured" name="Black" value="#000000"/> </items> </picker> <add jcr:primaryType="nt:unstructured" sling:resourceType="granite/ui/components/foundation/button" class="coral-Button--primary" id="EAEM_CP_ADD_COLOR" text="Add Color"/> <remove jcr:primaryType="nt:unstructured" sling:resourceType="granite/ui/components/foundation/button" class="coral-Button--warning" id="EAEM_CP_REMOVE_COLOR" text="Remove Color"/> </items> </column> </items> </content> </jcr:root>
3) Create clientlib (cq:ClientLibraryFolder) /apps/eaem-touchui-dialog-rte-color-picker/clientlib set property categories to cq.authoring.dialog.all and dependencies to [underscore]
4) Create file (nt:file) /apps/eaem-touchui-dialog-rte-color-picker/clientlib/js.txt, add the following content
color-picker.js
5) Create file (nt:file) /apps/eaem-touchui-dialog-rte-color-picker/clientlib/color-picker.js, add the following code
(function($, CUI){ var GROUP = "experience-aem", COLOR_PICKER_FEATURE = "colorPicker", TCP_DIALOG = "eaemTouchUIColorPickerDialog", PICKER_NAME_IN_POPOVER = "color", REQUESTER = "requester", PICKER_URL = "/apps/eaem-touchui-dialog-rte-color-picker/color-picker-popover/cq:dialog.html", url = document.location.pathname; if( url.indexOf(PICKER_URL) !== 0 ){ addPluginToDefaultUISettings(); addDialogTemplate(); } var EAEMColorPickerDialog = new Class({ extend: CUI.rte.ui.cui.AbstractDialog, toString: "EAEMColorPickerDialog", initialize: function(config) { this.exec = config.execute; }, getDataType: function() { return TCP_DIALOG; } }); var TouchUIColorPickerPlugin = new Class({ toString: "TouchUIColorPickerPlugin", extend: CUI.rte.plugins.Plugin, pickerUI: null, getFeatures: function() { return [ COLOR_PICKER_FEATURE ]; }, initializeUI: function(tbGenerator) { var plg = CUI.rte.plugins; if (!this.isFeatureEnabled(COLOR_PICKER_FEATURE)) { return; } this.pickerUI = tbGenerator.createElement(COLOR_PICKER_FEATURE, this, false, { title: "Color Picker" }); tbGenerator.addElement(GROUP, plg.Plugin.SORT_FORMAT, this.pickerUI, 10); var groupFeature = GROUP + "#" + COLOR_PICKER_FEATURE; tbGenerator.registerIcon(groupFeature, "textColor"); }, execute: function (id, value, envOptions) { if(!isValidSelection()){ return; } var context = envOptions.editContext, 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"), plugin = this, dialog, color = $(tag).css("color"), dm = ek.getDialogManager(), $container = CUI.rte.UIUtils.getUIContainer($(context.root)), propConfig = { 'parameters': { 'command': this.pluginId + '#' + COLOR_PICKER_FEATURE } }; if(this.eaemColorPickerDialog){ dialog = this.eaemColorPickerDialog; }else{ dialog = new EAEMColorPickerDialog(); dialog.attach(propConfig, $container, this.editorKernel); dialog.$dialog.css("-webkit-transform", "scale(0.9)").css("-webkit-transform-origin", "0 0") .css("-moz-transform", "scale(0.9)").css("-moz-transform-origin", "0px 0px"); dialog.$dialog.find("iframe").attr("src", getPickerIFrameUrl(color)); this.eaemColorPickerDialog = dialog; } dm.show(dialog); registerReceiveDataListener(receiveMessage); function isValidSelection(){ var winSel = window.getSelection(); return winSel && winSel.rangeCount == 1 && winSel.getRangeAt(0).toString().length > 0; } function getPickerIFrameUrl(color){ var url = PICKER_URL + "?" + REQUESTER + "=" + GROUP; if(!_.isEmpty(color)){ url = url + "&" + PICKER_NAME_IN_POPOVER + "=" + color; } return url; } function removeReceiveDataListener(handler) { if (window.removeEventListener) { window.removeEventListener("message", handler); } else if (window.detachEvent) { window.detachEvent("onmessage", handler); } } function registerReceiveDataListener(handler) { if (window.addEventListener) { window.addEventListener("message", handler, false); } else if (window.attachEvent) { window.attachEvent("onmessage", handler); } } function receiveMessage(event) { if (_.isEmpty(event.data)) { return; } var message = JSON.parse(event.data), action; if (!message || message.sender !== GROUP) { return; } action = message.action; if (action === "submit") { if (!_.isEmpty(message.data)) { ek.relayCmd(id, message.data); } }else if(action === "remove"){ ek.relayCmd(id); plugin.eaemColorPickerDialog = null; }else if(action === "cancel"){ plugin.eaemColorPickerDialog = null; } dialog.hide(); removeReceiveDataListener(receiveMessage); } }, //to mark the icon selected/deselected updateState: function(selDef) { var hasUC = this.editorKernel.queryState(COLOR_PICKER_FEATURE, selDef); if (this.pickerUI != null) { this.pickerUI.setSelected(hasUC); } } }); CUI.rte.plugins.PluginRegistry.register(GROUP,TouchUIColorPickerPlugin); var TouchUIColorPickerCmd = new Class({ toString: "TouchUIColorPickerCmd", extend: CUI.rte.commands.Command, isCommand: function(cmdStr) { return (cmdStr.toLowerCase() == COLOR_PICKER_FEATURE); }, getProcessingOptions: function() { var cmd = CUI.rte.commands.Command; return cmd.PO_SELECTION | cmd.PO_BOOKMARK | cmd.PO_NODELIST; }, _getTagObject: function(color) { return { "tag": "span", "attributes": { "style" : "color: " + color } }; }, execute: function (execDef) { var color = execDef.value ? execDef.value[PICKER_NAME_IN_POPOVER] : undefined, selection = execDef.selection, nodeList = execDef.nodeList; if (!selection || !nodeList) { return; } var common = CUI.rte.Common, context = execDef.editContext, tagObj = this._getTagObject(color); //if no color value passed, assume delete and remove color if(_.isEmpty(color)){ 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, undefined, true); nodeList.commonAncestor = nodeList.nodes[0].dom.parentNode; } nodeList.surround(execDef.editContext, tagObj.tag, tagObj.attributes); } }); CUI.rte.commands.CommandRegistry.register(COLOR_PICKER_FEATURE, TouchUIColorPickerCmd); function addPluginToDefaultUISettings(){ var toolbar = CUI.rte.ui.cui.DEFAULT_UI_SETTINGS.inline.toolbar; toolbar.splice(3, 0, GROUP + "#" + COLOR_PICKER_FEATURE); toolbar = CUI.rte.ui.cui.DEFAULT_UI_SETTINGS.fullscreen.toolbar; toolbar.splice(3, 0, GROUP + "#" + COLOR_PICKER_FEATURE); } function addDialogTemplate(){ var url = PICKER_URL + "?" + REQUESTER + "=" + GROUP; var html = "<iframe width='600px' height='500px' 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-' + TCP_DIALOG] = CUI.rte.Templates['dlg-' + TCP_DIALOG] = Handlebars.compile(html); } }(jQuery, window.CUI,jQuery(document))); (function($, $document){ var SENDER = "experience-aem", REQUESTER = "requester", COLOR = "color", ADD_COLOR_BUT = "#EAEM_CP_ADD_COLOR", REMOVE_COLOR_BUT = "#EAEM_CP_REMOVE_COLOR"; if(queryParameters()[REQUESTER] !== SENDER ){ return; } $(function(){ _.defer(stylePopoverIframe); }); 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 stylePopoverIframe(){ var queryParams = queryParameters(), $dialog = $("coral-dialog"); if(_.isEmpty($dialog)){ return; } $dialog.css("overflow", "hidden").css("background-color", "#fff"); $dialog[0].open = true; var $addColor = $dialog.find(ADD_COLOR_BUT), $removeColor = $dialog.find(REMOVE_COLOR_BUT), color = queryParameters()[COLOR], $colorPicker = $document.find("coral-colorinput"); if(!_.isEmpty(color)){ color = decodeURIComponent(color); if(color.indexOf("rgb") == 0){ color = CUI.util.color.RGBAToHex(color); } $colorPicker[0].value = color; } adjustHeader($dialog); $colorPicker.css("margin-bottom", "285px"); $(ADD_COLOR_BUT).css("margin-left", "220px"); $addColor.click(sendDataMessage); $removeColor.click(sendRemoveMessage); } function adjustHeader($dialog){ var $header = $dialog.css("background-color", "#fff").find(".coral3-Dialog-header"); $header.find(".cq-dialog-submit").remove(); $header.find(".cq-dialog-cancel").click(function(event){ event.preventDefault(); $dialog.remove(); sendCancelMessage(); }); } function sendCancelMessage(){ var message = { sender: SENDER, action: "cancel" }; parent.postMessage(JSON.stringify(message), "*"); } function sendRemoveMessage(){ var message = { sender: SENDER, action: "remove" }; parent.postMessage(JSON.stringify(message), "*"); } function sendDataMessage(){ var message = { sender: SENDER, action: "submit", data: {} }, $dialog, color; $dialog = $(".cq-dialog"); color = $dialog.find("[name='./" + COLOR + "']").val(); if(color && color.indexOf("rgb") >= 0){ color = CUI.util.color.RGBAToHex(color); } message.data[COLOR] = color; parent.postMessage(JSON.stringify(message), "*"); } })(jQuery, jQuery(document));
We are having the an issue on 6.5 of Uncaught TypeError: Handlebars.compile is not a function. Is there an include or something we're missing?
ReplyDeleteWe had to add "handlebars" as a dependency in the clientlib, in addition to underscore.
DeleteHi Metamort, did you change anything else? I am using 6.5 and tried to add Hadlebars and underscore but still not working for me.
DeleteHi,
ReplyDeleteThanks for the document provided. it is working fine in aem 6.4 but the only problem is,
It is storing as RGB(100,1,1) values if we change color. is it possible to change this.
Thanks
Hi Sreekanth,
ReplyDeleteThank you for writing this article. Please can you advise me if the this works on a clean installation of AEM 6.5.2.0 with "We-Retail" sample content ?
I have tried following the steps below and I cannot see the Color Picker icon in the Rich Text Editor ?
(1) Navigate to http://localhost:4502/crx/de/index.jsp#/apps/core/wcm/components/text/v2/text/cq%3Adialog/content/items/tabs/items/properties/items/columns/items/column/items/text/rtePlugins
(2) Create node "experience-aem"
(3) Navigate to http://localhost:4502/crx/de/index.jsp#/apps/core/wcm/components/text/v2/text/cq%3Adialog/content/items/tabs/items/properties/items/columns/items/column/items/text/rtePlugins/experience-aem
(4) Add property "features" with value "*"
(5) Navigate to http://localhost:4502/crx/de/index.jsp#/apps/core/wcm/components/text/v2/text/cq%3Adialog/content/items/tabs/items/properties/items/columns/items/column/items/text/uiSettings/cui/dialogFullScreen
(6) Add value "experience-aem#colorPicker" to existing property "toolbar"
(7) Navigate to http://localhost:4502/crx/packmgr/index.jsp
(8) Upload and install package "eaem-touchui-dialog-rte-color-picker-65.zip"
(9) Navigate to http://localhost:4502/crx/de/index.jsp#/apps/eaem-touchui-dialog-rte-color-picker
(10) Verify this contains folders "clientlib" and "color-picker-popover"
(11) Navigate to http://localhost:4502/editor.html/content/we-retail/language-masters/en.html
(12) Add a Text component
(13) Select Text component , click "Edit" and "Full screen"
Please can you advise me if there are any additional steps needed to get the Rich Text Editor Color Picker working on a clean installation of AEM 6.5.2.0 with "We-Retail" sample content ?
Thank you for your time and advise,
Best regards,
James Collett
Hi Sreekanth
ReplyDeleteThanks for this plugin . We are using this in our project. The issue we are having is in the XF pages, the plugin is throwing Handlebars.compile is not a function error. I tried adding handlebars as additional dependencies value , but it didn't solve the issue. Any idea ?
TIA
Veena
Hello,
ReplyDeleteI insterded this plugin in my project, it works fine in dialog and fullscreen editors, but it doesn't work on inline editor.
I don't know if it is only my problem or the plugin itself. Is this plugin supposed to work even in the inline editor or only in fullscreen and dialog?
Thanks in advance.
Hi Srikanth,
ReplyDeleteI have added this plugin to inPlaceEditing node also. I'm able to see the color picker plugin only for first time, it disappears from inplace editing mode once i switch to dialog mode.
Please provide your thoughts.