Quantcast
Channel: Experiencing Adobe Experience Manager (AEM, CQ)
Viewing all articles
Browse latest Browse all 525

AEM 61 - TouchUI Rich Text Editor Color Picker Plugin

$
0
0

Goal


Color Picker Plugin extension for RTE (Rich Text Editor) in 61 Touch UI. Click here for Color Picker widget documentation

This is a better implementation of check this post

For Classic UI check this post

Demo | Package Install


Picker with Free Style Palette




Picker with Palette Shades - Classic



Palette Edit Mode




RTE text with Color applied



Remove Color



Solution


1) Login to CRXDE Lite http://localhost:4502/crx/de, add nt:folder /apps/touchui-rte-color-picker-plugin

2) To show the color picker in a dialog create /apps/touchui-rte-color-picker-plugin/color-picker-popover of type sling:Folder and /apps/touchui-rte-color-picker-plugin/color-picker-popover/cq:dialog of type nt:unstructured

3) XML representation of /apps/touchui-rte-color-picker-plugin/color-picker-popover/cq:dialog and structure in CRXDE Lite


<?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/foundation/form/colorpicker"
classicPaletteType="{Boolean}true"
editType="{Boolean}true"
fieldLabel="Select Color"
name="./color">
<colors 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"/>
<brown
jcr:primaryType="nt:unstructured"
name="Brown"
value="#996633"/>
<orange
jcr:primaryType="nt:unstructured"
name="Orange"
value="#FF7F00"/>
<purple
jcr:primaryType="nt:unstructured"
name="Purple"
value="#7F007F"/>
<yellow
jcr:primaryType="nt:unstructured"
name="Yellow"
value="#FFFF00"/>
</colors>
</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>

4) Colors shown in picker dialog are added in /apps/touchui-rte-color-picker-plugin/color-picker-popover/cq:dialog/content/items/column/items/picker/colors

5) Create clientlib (cq:ClientLibraryFolder) /apps/touchui-rte-color-picker-plugin/clientlib set property categories to rte.coralui2 and dependencies to [underscore]

6) Create file (nt:file) /apps/touchui-rte-color-picker-plugin/clientlib/js.txt, add the following

                   color-picker-plugin.js

7) Create file (nt:file) /apps/touchui-rte-color-picker-plugin/clientlib/color-picker-plugin.js, add the following code

(function ($, $document, Handlebars) {
"use strict";

var _ = window._,
Class = window.Class,
CUI = window.CUI,
REQUESTER = "requester",
GROUP = "experience-aem",
COLOR_PICKER_FEATURE = "colorPicker",
COLOR_PICKER_DIALOG = "colorPickerDialog",
DIALOG_URL = "/apps/touchui-rte-color-picker-plugin/color-picker-popover/cq:dialog",
PICKER_NAME_IN_POPOVER = "color",
EAEMCuiToolbarBuilder,
EAEMColorPickerPluginDialog,
EAEMDialogManager,
EAEMToolkitImpl,
EAEMColorPickerPlugin,
EAEMColorPickerCmd;

function getUISetting() {
return GROUP + "#" + COLOR_PICKER_FEATURE;
}

//extend the toolbar builder to register plugin icon in fullscreen mode
EAEMCuiToolbarBuilder = new Class({
toString: "EAEMCuiToolbarBuilder",

extend: CUI.rte.ui.cui.CuiToolbarBuilder,

_getUISettings: function (options) {
var uiSettings = this.superClass._getUISettings(options),
toolbar = uiSettings.fullscreen.toolbar,
feature = getUISetting();

if (toolbar.indexOf(feature) === -1) {
toolbar.splice(3, 0, feature);
}

if (!this._getClassesForCommand(feature)) {
//.coral-ColorPicker-button
this.registerAdditionalClasses(feature, "coral-Icon coral-Icon--textColor");
}

return uiSettings;
}
});

//popover dialog hosting iframe
EAEMColorPickerPluginDialog = new Class({
extend: CUI.rte.ui.cui.AbstractBaseDialog,

toString: "EAEMColorPickerPluginDialog",

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

//extend the CUI dialog manager to register popover dialog
EAEMDialogManager = new Class({
toString: "EAEMDialogManager",

extend: CUI.rte.ui.cui.CuiDialogManager,

create: function (dialogId, config) {
if (dialogId !== COLOR_PICKER_DIALOG) {
return this.superClass.create.call(this, dialogId, config);
}

var context = this.editorKernel.getEditContext(),
$container = CUI.rte.UIUtils.getUIContainer($(context.root)),
dialog = new EAEMColorPickerPluginDialog();

dialog.attach(config, $container, this.editorKernel, true);

return dialog;
}
});

//extend the toolkit implementation for custom toolbar builder and dialog manager
EAEMToolkitImpl = new Class({
toString: "EAEMToolkitImpl",

extend: CUI.rte.ui.cui.ToolkitImpl,

createToolbarBuilder: function () {
return new EAEMCuiToolbarBuilder();
},

createDialogManager: function (editorKernel) {
return new EAEMDialogManager(editorKernel);
}
});

CUI.rte.ui.ToolkitRegistry.register("cui", EAEMToolkitImpl);

EAEMColorPickerPlugin = new Class({
toString: "ColorPickerDialogPlugin",

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, true, "Select Color");

tbGenerator.addElement(GROUP, plg.Plugin.SORT_FORMAT, this.pickerUI, 120);
},

execute: function (id, value, envOptions) {
var ek = this.editorKernel,
dm = ek.getDialogManager(),
$popover, dialog, context = envOptions.editContext;

if(!isValidSelection()){
return;
}

var dialogConfig = {
parameters: {
"command": getUISetting()
}
};

dialog = this.dialog = dm.create(COLOR_PICKER_DIALOG, dialogConfig);

dialog.restoreSelectionOnHide = false;

dm.prepareShow(this.dialog);

dm.show(this.dialog);

$popover = this.dialog.$dialog.find(".coral-Popover-content");

var selection = CUI.rte.Selection.createProcessingSelection(context),
tag = CUI.rte.Common.getTagInPath(context, selection.startNode, "span" );

loadPopoverUI($popover, $(tag).css("color"));

function isValidSelection(){
var winSel = window.getSelection();
return winSel && winSel.type && winSel.type.toUpperCase() == "RANGE";
}

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);
}

dialog.hide();

removeReceiveDataListener(receiveMessage);
}

function loadPopoverUI($popover, color) {
var url = DIALOG_URL + ".html?" + REQUESTER + "=" + GROUP;

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

$popover.parent().css("width", ".1px").height(".1px").css("border", "none");
$popover.css("width", ".1px").height(".1px");
$popover.find("iframe").attr("src", url);

//receive the dialog values from child window
registerReceiveDataListener(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, EAEMColorPickerPlugin);

EAEMColorPickerCmd = new Class({
toString: "ColorPickerDialogCmd",

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, EAEMColorPickerCmd);

//returns the picker dialog html
//Handlebars doesn't do anything useful here, but the framework expects a template
function dlgTemplate() {
CUI.rte.Templates["dlg-" + COLOR_PICKER_DIALOG] =
Handlebars.compile('<div data-rte-dialog="' + COLOR_PICKER_DIALOG +
'" class="coral--dark coral-Popover coral-RichText-dialog">' +
'<iframe width="525px" height="465px"></iframe>' +
'</div>');
}

dlgTemplate();
})(jQuery, jQuery(document), Handlebars);

(function($, $document){
var SENDER = "experience-aem",
REQUESTER = "requester",
COLOR = "color",
ADD_COLOR_BUT = "#EAEM_CP_ADD_COLOR",
REMOVE_COLOR_BUT = "#EAEM_CP_REMOVE_COLOR",
PICKER_COLORS = location.pathname.replace(".html", "") + "/content/items/column/items/picker/colors.infinity.json",
HELP_BUTTON_SEL = ".cq-dialog-help",
CANCEL_BUTTON_SEL = ".cq-dialog-cancel",
SUBMIT_BUTTON_SEL = ".cq-dialog-submit",
pickerInstance;

if(queryParameters()[REQUESTER] !== SENDER ){
return;
}

$document.on("foundation-contentloaded", 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();

if(!_.isEmpty(queryParameters()[COLOR])){
pickerInstance._setColor(decodeURIComponent(queryParams[COLOR]));
}

var $dialog = $(".cq-dialog"),
$cancel = $dialog.find(CANCEL_BUTTON_SEL),
$submit = $dialog.find(SUBMIT_BUTTON_SEL),
$addColor = $dialog.find(ADD_COLOR_BUT),
$removeColor = $dialog.find(REMOVE_COLOR_BUT);

$dialog.css("border", "solid 2px");
$dialog.find(HELP_BUTTON_SEL).hide();
$document.find(".coral-ColorPicker").closest(".coral-Form-fieldwrapper")
.css("margin-bottom", "20px");

$document.off("click", CANCEL_BUTTON_SEL);
$document.off("click", SUBMIT_BUTTON_SEL);
$document.off("submit");

$cancel.click(sendCloseMessage);
$submit.click(sendDataMessage);
$addColor.click(sendDataMessage);
$removeColor.click(sendRemoveMessage);
}

function sendCloseMessage(){
var message = {
sender: SENDER,
action: "close"
};

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), "*");
}

CUI.Colorpicker = new Class({
toString: "Colorpicker",
extend: CUI.Colorpicker,

_readDataFromMarkup: function () {
this.superClass._readDataFromMarkup.call(this);

var el = this.$element;

//extend otb CUI.Colorpicker to workaround the pickerModes bug
//in granite/ui/components/foundation/form/colorpicker/render.jsp
//colorpickerJson.put("modes", pickerModes); should have been
//colorpickerJson.put("pickerModes", pickerModes);
if (el.data('config').modes) {
this.options.config.displayModes = el.data('config').modes;
}

pickerInstance = this;

function setColors(data){
if(_.isEmpty(data)){
return;
}

var colors = {};

_.each(data, function(color, key){
if(key.indexOf("jcr:") >= 0){
return;
}

colors[color.name] = color.value;
});

pickerInstance.options.config.colors = colors;
}

$.ajax({ url: PICKER_COLORS, async: false, dataType: 'json' } ).done(setColors);
}
});

CUI.Widget.registry.register("colorpicker", CUI.Colorpicker);
})(jQuery, jQuery(document));

8) To set palette type to Free style or Classic, set the following properties on /apps/touchui-rte-color-picker-plugin/color-picker-popover/cq:dialog/content/items/column/items/picker

                  freestylePaletteType="{Boolean}true"
                  classicPaletteType="{Boolean}true"




Viewing all articles
Browse latest Browse all 525

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>