Goal
Touch UI Color Picker Plugin for RTE (Rich Text Editor) Dialog - /libs/cq/gui/components/authoring/dialog/richtext
For a similar extension on 63 check this post, 62 check this post, 61 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
Color Picker
Color applied to text
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"; 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); }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.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));
Color Picker plugin has a bug. When I open a dialog of another component and save my modify the REFRESH_PAGE is triggered. At this point, if I open my rich text the color Picker plugin disappear, but when I refresh the page it is visibile again. Any solutions?
ReplyDeleteusing the plugin in aem 6.5 getting Uncaught TypeError: Handlebars.compile is not a function. Can you provide any fix for this
ReplyDeletefor 65 - https://experience-aem.blogspot.com/2019/04/aem-65-touch-ui-rte-rich-text-editor-dialog-color-picker-plugin.html
DeleteHi all,
ReplyDeleteEven we are getting the same error after the upgrade to AEM 6.4.8.2. Please help
Uncaught ReferenceError: _ is not defined
at addDialogTemplate (rteCustomPlugins.3b77f1c5eca0b443a7f2cec89fe4ec4f.js:247)
at rteCustomPlugins.3b77f1c5eca0b443a7f2cec89fe4ec4f.js:11
at rteCustomPlugins.3b77f1c5eca0b443a7f2cec89fe4ec4f.js:257
So did you get the fix for this.
DeleteEven i faced the same issue as i upgraded from 6.4.2 to 6.4.8 service pack.I think it is not able to find the "underscore dependency" in the AEM (Service pack 6.4.8) that 'y it throwing error. I tried to add same dependency directly in the style-picker.js and its working as expected but the behavior is not consistent, So whenever i am closing the style dialog it's throwing error apart from this it is working as expected.
I have fixed this issue. If anyone facing this kind of issue just let me know. I will tell the fix for the same.
DeleteHi, I have the same issue, can you tell me how you have fixed?
DeleteHi Keshav. I'm facing the same issue (_ is undefined). Can you please help me the fix?
ReplyDelete