Goal
Add a new plugin to RichText Editor. Here we add the Pull Quote plugin for inserting text that can be formatted/styled during component rendering. Source code, Package Install and Demo Video are available for download. Please leave a comment if you find bugs.
Solution
1) Login to CRXDE Lite, create folder (nt:folder) /apps/rte-pull-quote
2) Create clientlib (type cq:ClientLibraryFolder) /apps/rte-pull-quote/clientlib and set a property categories of String type to cq.widgets
3) Create file ( type nt:file ) /apps/rte-pull-quote/clientlib/js.txt, add the following
js/rtepullquote.js.js
4) Create folder (nt:folder) /apps/rte-pull-quote/clientlib/js
5) Create file (type nt:file) /apps/rte-pull-quote/clientlib/js/rtepullquote.js, add the following code
CQ.Ext.ns("MyClientLib");
MyClientLib.PullQuote = {
ADD_QUOTE_CMD : "addquote",
REMOVE_QUOTE_CMD : "removequote",
DEFAULT_PATTERN: "[pullquote:(align=<align>,text=<text>)]"
};
MyClientLib.PullQuote.Plugin = new Class({
toString: "PullQuotePlugin",
extend: CUI.rte.plugins.Plugin,
P: MyClientLib.PullQuote,
addQuoteUI: null,
removeQuoteUI: null,
getFeatures: function() {
return [ this.P.ADD_QUOTE_CMD, this.P.REMOVE_QUOTE_CMD ];
},
initializeUI: function(tbGenerator) {
var plg = CUI.rte.plugins;
if (this.isFeatureEnabled(this.P.ADD_QUOTE_CMD)) {
this.addQuoteUI = tbGenerator.createElement(this.P.ADD_QUOTE_CMD, this, true, "Add/Modify Pull Quote");
tbGenerator.addElement("format", plg.Plugin.SORT_FORMAT,this.addQuoteUI,1000);
}
if (this.isFeatureEnabled(this.P.REMOVE_QUOTE_CMD)) {
this.removeQuoteUI = tbGenerator.createElement(this.P.REMOVE_QUOTE_CMD, this, true,"Remove Pull Quote");
tbGenerator.addElement("format", plg.Plugin.SORT_FORMAT,this.removeQuoteUI,1001);
}
},
execute: function(cmd, value, env) {
if (cmd == this.P.ADD_QUOTE_CMD) {
this.showDialog(env.editContext);
}else {
this.editorKernel.relayCmd(MyClientLib.PullQuote.REMOVE_QUOTE_CMD, value);
}
},
showDialog: function(context) {
var editorKernel = this.editorKernel, dm = editorKernel.getDialogManager(), pattern = this.config.pattern;
if(!pattern){
pattern = this.P.DEFAULT_PATTERN;
}
var selection = CUI.rte.Selection.createProcessingSelection(context);
var rteText = selection.startNode.data, initValue = { align : "LEFT", text : "" };
if(rteText){
//this parsing logic depends on pattern, so when you add new pattern for pullquote make
//sure you modify the following code to suit your pattern
try{
var start = rteText.lastIndexOf("[pullquote:", selection.startOffset);
if( start !== -1 ){
var dIndex = rteText.indexOf(",");
initValue.align = rteText.substring(rteText.indexOf("=",start) + 1, dIndex);
initValue.text = rteText.substring(rteText.indexOf("=", dIndex) + 1, rteText.indexOf(")]"));
}
}catch(err){
CQ.Ext.Msg.alert("Error","Error parsing text with pattern : " + pattern);
}
}
var dialogConfig = {
"jcr:primaryType": "cq:Dialog",
title: "Pull Quote",
modal: true,
width: 600,
height: 400,
items: [ {
xtype: "panel",
layout: "fit",
padding: "20px 0 0 10px",
items: [ {
xtype: "panel",
layout: "form",
border: false,
items: [ {
xtype: 'radiogroup',
columns: 4,
fieldLabel: "Align",
items: [{
boxLabel: ' Left',
name: 'align',
value: 'LEFT',
checked: (initValue.align === "LEFT")
}, {
name: 'align',
boxLabel: ' Right',
value: 'RIGHT',
checked: (initValue.align === "RIGHT")
}, {
name: 'align',
boxLabel: ' Center',
value: 'CENTER',
checked: (initValue.align === "CENTER")
}, {
name: 'align',
boxLabel: ' Justify',
value: 'JUSTIFY',
checked: (initValue.align === "JUSTIFY")
}]
},{
xtype: "textarea",
height: 250,
name: "text",
fieldLabel: "Text",
fieldDescription: "Enter quote text",
anchor: "90%",
value: initValue.text
} ]
} ]
} ],
ok: function() {
var tBox = this.findByType("textarea")[0];
var rBox = this.findByType("radiogroup")[0];
if(!tBox.getValue()){
CQ.Ext.Msg.alert("Error","Enter text for quote");
return;
}
var value = {
text: tBox.getValue(),
align: rBox.getValue().value,
pattern: pattern
};
this.close();
editorKernel.relayCmd(MyClientLib.PullQuote.ADD_QUOTE_CMD, value);
},
listeners: {
show: function() {
editorKernel.fireUIEvent("dialogshow");
},
hide: function() {
editorKernel.fireUIEvent("dialoghide");
}
}
};
dm.show(CQ.WCM.getDialog(dialogConfig));
},
notifyPluginConfig: function(pluginConfig) {
pluginConfig = pluginConfig || { };
CUI.rte.Utils.applyDefaults(pluginConfig, {
"tooltips": {
"addquote": {
"title": "Add/Modify Pull Quote",
"text": "Add/Modify Pull Quote"
},
"removequote": {
"title": "Remove Pull Quote",
"text": "Remove Pull Quote"
}
}
});
this.config = pluginConfig;
},
updateState: function(selDef) {
var rteText = selDef.selection.startNode.data;
//this parsing logic depends on pattern, so when you add new pattern for pullquote make
//sure you modify the following code to suit your pattern
if(rteText && (rteText.lastIndexOf("[pullquote:", selDef.startOffset) !== -1)){
this.removeQuoteUI.setDisabled(false);
}else{
this.removeQuoteUI.setDisabled(true);
}
this.addQuoteUI.setSelected(false);
this.removeQuoteUI.setSelected(false);
}
});
CUI.rte.plugins.PluginRegistry.register("pullquote", MyClientLib.PullQuote.Plugin);
MyClientLib.PullQuote.Cmd = new Class({
toString: "PullQuote",
extend: CUI.rte.commands.Command,
P: MyClientLib.PullQuote,
isCommand: function(cmdStr) {
return (cmdStr == this.P.ADD_QUOTE_CMD) || (cmdStr == this.P.REMOVE_QUOTE_CMD);
},
getProcessingOptions: function() {
var cmd = CUI.rte.commands.Command;
return cmd.PO_SELECTION | cmd.PO_NODELIST;
},
addPullQuote: function(execDef){
var value = execDef.value, selection = execDef.selection;
var node = CUI.rte.DomProcessor.createNode(execDef.editContext, "span");
var rteText = selection.startNode.data;
var start = rteText ? rteText.lastIndexOf("[pullquote:", selection.startOffset) : -1;
if( start !== -1 ){
CUI.rte.Common.insertNode(node, selection.startNode, start);
selection.startNode.parentNode.removeChild(selection.startNode);
}else{
CUI.rte.Common.insertNode(node, selection.startNode, selection.startOffset);
}
if(value.pattern){
node.innerHTML = value.pattern.replace("<align>", value.align).replace("<text>", value.text);
}else{
node.innerHTML = "[pullquote:(align=\"" + value.align + "\",text=\"" + value.text + "\")]";
}
},
removePullQuote: function(execDef){
var selection = execDef.selection;
var rteText = selection.startNode.data;
var start = rteText.lastIndexOf("[pullquote:", selection.startOffset);
if( start !== -1 ){
selection.startNode.parentNode.removeChild(selection.startNode);
}
},
execute: function(execDef) {
if(execDef.command == this.P.ADD_QUOTE_CMD){
this.addPullQuote(execDef);
}else{
this.removePullQuote(execDef);
}
}
});
CUI.rte.commands.CommandRegistry.register("pullquote", MyClientLib.PullQuote.Cmd);
6) #185, #243 we are registering the plugin and necessary action command. #25, #30 creates the toolbar buttons for add, remove quotes
7) Let us add the not-so-great icons and necessary css. Create folder (nt:folder) /apps/rte-pull-quote/clientlib/themes
8) Create clientlib (type cq:ClientLibraryFolder) /apps/rte-pull-quote/clientlib/themes/default and set a property categories of String type to cq.widgets
9) Create file (nt:file) /apps/rte-pull-quote/clientlib/themes/default/css.txt, add the following
css/rtepullquote.css
10) Create folder (nt:folder) /apps/rte-pull-quote/clientlib/themes/default/css and make sure you add the icons addquote.png and removequote.png
11) Create file (nt:file) /apps/rte-pull-quote/clientlib/themes/default/css/rtepullquote.css, add the following code. RTE looks for css classes x-edit-addquote and x-edit-removequote (for the plugin toolbar buttons added namely addquote and removequote )
#CQ .x-html-editor-tb .x-edit-addquote {
background: url(addquote.png) center no-repeat;
}
#CQ .x-html-editor-tb .x-edit-removequote {
background: url(removequote.png) center no-repeat;
}
12) Add any text component with RichText editor and in the rtePlugins path of dialog (eg. /apps/rte-pull-quote/text/dialog/items/tab1/items/text/rtePlugins) add the pullquote node to enable pull quote plugins
13) The component structure in CRXDE Lite