CKEditor add Format combo items inside context (nested) menu - ckeditor

I am using CKEditor 4.5.7 in one of my project. We have customized Format combo as shown in the below screen shot, the ask is to have all the items appearing inside this combo on right click.
And below is the code for Format combo:
config.format_tags = 'p;h3;h4;pre;ImageInline;ImageCentered;ImageCenteredWithDropShadow;FigureHeading;Equation;EquationDefinition;TableWithoutBorder';
config.format_ImageInline = { name: 'Image inline', element: 'img', attributes: { 'class': 'noborder' } };
config.format_ImageCentered = { name: 'Image centered', element: 'img', attributes: { 'class': 'noborderblock' } };
config.format_ImageCenteredWithDropShadow = { name: 'Image centered drop shadow', element: 'img', attributes: { 'class': 'border' } };
config.format_FigureHeading = { name: 'Figure/Table heading', element: 'p'/*['p', 'td']*/, attributes: { 'class': 'footing' } };
config.format_Equation = { name: 'Equation', element: 'table', attributes: { 'class': 'equation' } };
config.format_EquationDefinition = { name: 'Equation definition', element: 'table', attributes: { 'class': 'where' } };
config.format_TableWithoutBorder = { name: 'Table without border', element: 'table', attributes: { 'class': 'nobordertable' } };
I was able to get them displayed in context menu as shown in below screen shot:
But I am not sure what will be the command name for each one of them. i.e.
command: 'format_ImageCentered' /*I need help here*/
command: 'format_ImageCenteredWithDropShadow' /*I need help here*/
I have already downloaded full source code of CKEditor and gone thru ckeditor\plugins\format\plugin.js but wasn't able to figure out what to specify as command.
Below is my code for customizing Context menu:
var ck_article = CKEDITOR.replace("content", { customConfig: '<config js file path>', bodyClass: '<css class>' });
ck_article.on("instanceReady", function (evt) {
var editor = evt.editor;
/*Code for checking if editor has context menu or not removed for brevity*/
//...
//...
editor.addMenuGroup('ck_group');
editor.addMenuItem('bold', {
label: 'Bold',
command: 'bold',
group: 'ck_group'
});
editor.addMenuItem('iconselector', {
label: '...',
command: 'iconselector',
group: 'ck_group'
});
editor.addMenuItem('numberedlist', {
label: 'Numbered List',
command: 'numberedlist',
group: 'ck_group'
});
editor.addMenuItem('bulletedlist', {
label: 'Bulleted List',
command: 'bulletedlist',
group: 'ck_group'
});
editor.addMenuItem('link', {
label: 'Link',
command: 'link',
group: 'ck_group'
});
editor.addMenuItems({
formatting: {
label: 'Formatting',
group: 'ck_group',
getItems: function () {
var selection = editor.getSelection();
//This is to nest items inside context menu of CKEditor
return {
format_ImageCentered: CKEDITOR.TRISTATE_ON,
format_ImageCenteredWithDropShadow: CKEDITOR.TRISTATE_ON
}
}
},
format_ImageCentered: {
label: "Image centered",
group: 'ck_group',
command: 'format_ImageCentered' /*I need help here*/
},
format_ImageCenteredWithDropShadow: {
label: "Image centered drop shadow",
group: 'ck_group',
command: 'format_ImageCenteredWithDropShadow' /*I need help here*/
}
});
editor.contextMenu.addListener(function (element, selection, elementPath) {
var contentMenuItems = {
link: CKEDITOR.TRISTATE_ON,
bold: CKEDITOR.TRISTATE_ON,
numberedlist: CKEDITOR.TRISTATE_ON,
bulletedlist: CKEDITOR.TRISTATE_ON,
iconselector: CKEDITOR.TRISTATE_ON,
formatting: CKEDITOR.TRISTATE_ON
};
if (element.getAscendant('a', true)) {
//If we are already inside 'a' tag then remove link from Context menu else we will end up with two "Link" menus
delete contentMenuItems.link
}
if ($.trim(selection.getSelectedText()) === '') {
//If no text is selected then remove bold from context menu
delete contentMenuItems.bold;
//contentMenuItems.bold = CKEDITOR.TRISTATE_DISABLED; //This doesn't work as the menu item is disabled but hover effect is still there
//Similarly remove link if nothing is selected as it will insert hyperlink text as words inside CKEditor
delete contentMenuItems.link;
//contentMenuItems.link = CKEDITOR.TRISTATE_DISABLED; //This doesn't work as the menu item is disabled but hover effect is still there
}
return contentMenuItems;
});
});
References:
I have used below URL as references:
http://blog.ale-re.net/2010/06/ckeditor-context-menu.html
http://docs.ckeditor.com/#!/guide/plugin_sdk_sample_2
CKEditor - Possible to have context menu for basic styles?

I ended up creating plugin for each and every option listed in Format combo. If there is any other better way than this then please let me know. I am passing code just in case if someone else stumbles with the similar issue.
Below screen shot shows how the plugin folder looks inside CKEditor folder:
I am pasting just one plugin code as all of plugins have the exact same code the only difference is value for pluginName, which is exact same as folder name:
//All the files inside folder stating with context_<name> have exact same code except pluginName variable.
//I need to this to support format inside right click
(function () {
"use strict";
var pluginName = 'contextmenu_tablewithoutborder'; //This name will be used to add to 'config.extraPlugins' string
var commandName = pluginName;
// Register the plugin within the editor.
CKEDITOR.plugins.add(pluginName, {
// Register the icons. They must match command names.
icons: pluginName,
// The plugin initialization logic goes inside this method.
init: function (editor) {
// Define an editor command.
editor.addCommand(commandName, { //Command name must match with name provided in editor.ui.addButton
// Define the function that will be fired when the command is executed.
exec: function (editor) {
if (typeof editor.applyFormatStyles === 'function')
editor.applyFormatStyles(pluginName.split('_')[1]);
else
throw new Error('applyFormatStyles is not defined as function (' + pluginName + ')');
}
});
}
});
})();
I then extended CKEditor and added below methods:
CKEDITOR.editor.prototype.getFormatStyles = function () {
var styles = {}
var editor = this;
var config = editor.config,
lang = editor.lang.format;
// Gets the list of tags from the settings.
var tags = config.format_tags.split(';');
for (var i = 0; i < tags.length; i++) {
var tag = tags[i];
var style = new CKEDITOR.style(config['format_' + tag]);
if (!editor.filter.customConfig || editor.filter.check(style)) {
styles[tag] = style;
styles[tag]._.enterMode = editor.config.enterMode;
}
}
return styles;
}
CKEDITOR.editor.prototype.applyFormatStyles = function (styleName) {
var editor = this;
var styles = editor.getFormatStyles();
editor.focus();
editor.fire('saveSnapshot');
var style = styles[styleName],
elementPath = editor.elementPath();
editor[style.checkActive(elementPath, editor) ? 'removeStyle' : 'applyStyle'](style);
// Save the undo snapshot after all changes are affected. (#4899)
setTimeout(function () {
editor.fire('saveSnapshot');
}, 0);
}
I then modified my config file and added all these plugins as extra plugins:
CKEDITOR.editorConfig = function (config) {
var extraPlugins = [];
//Remove other code for brevity
//...
//...
config.format_tags = 'p;h3;h4;pre;imageinline;imagecentered;imagecenteredwithdropshadow;figureheading;equation;equationdefinition;tablewithoutborder';
config.format_imageinline = { name: 'Image inline', element: 'img', attributes: { 'class': 'noborder' } };
config.format_imagecentered = { name: 'Image centered', element: 'img', attributes: { 'class': 'noborderblock' } };
config.format_imagecenteredwithdropshadow = { name: 'Image centered drop shadow', element: 'img', attributes: { 'class': 'border' } };
config.format_figureheading = { name: 'Figure/Table heading', element: 'p'/*['p', 'td']*/, attributes: { 'class': 'footing' } };
config.format_equation = { name: 'Equation', element: 'table', attributes: { 'class': 'equation' } };
config.format_equationdefinition = { name: 'Equation definition', element: 'table', attributes: { 'class': 'where' } };
config.format_tablewithoutborder = { name: 'Table without border', element: 'table', attributes: { 'class': 'nobordertable' } };
var contextmenu_plugins = config.format_tags.split(";");
for (var i = 0; i < contextmenu_plugins.length; i++) {
var pluginName = contextmenu_plugins[i];
extraPlugins.push("contextmenu_{0}".format(pluginName))
}
config.extraPlugins = extraPlugins.join(',');
}
And then finally when I create editor I extent the context menu. I could add this logic in plugin.js file but since I wanted plugin.js code to be exact same except a line or two's difference I didn't bother adding it there.
var ck_article = CKEDITOR.replace("content", { customConfig: '<config js file path>', bodyClass: '<css class>' });
ck_article.on("instanceReady", function (evt) {
var editor = evt.editor;
editor.addMenuGroup('ck_group');
editor.addMenuItem('bold', {
label: 'Bold',
command: 'bold',
group: 'ck_group'
});
editor.addMenuItem('iconselector', {
label: '...',
command: 'iconselector',
group: 'ck_group'
});
editor.addMenuItem('numberedlist', {
label: 'Numbered List',
command: 'numberedlist',
group: 'ck_group'
});
editor.addMenuItem('bulletedlist', {
label: 'Bulleted List',
command: 'bulletedlist',
group: 'ck_group'
});
editor.addMenuItem('link', {
label: 'Link',
command: 'link',
group: 'ck_group'
});
editor.addMenuItems({
formatting: {
label: 'Formatting',
group: 'ck_group',
getItems: function () {
var selection = editor.getSelection();
//This is to nest items inside context menu of CKEditor
var tags = editor.config.format_tags.split(";");
var menu_items = {
};
for (var i = 0; i < tags.length; i++) {
menu_items[tags[i]] = CKEDITOR.TRISTATE_ON;
}
//menu_items - will have object something like below
//{p: 1, h3: 1, h4: 1......}
return menu_items;
}
},
p: {
label: "Normal",
group: 'ck_group',
command: 'contextmenu_p'
},
h3: {
label: "Heading",
group: 'ck_group',
command: 'contextmenu_h3'
},
h4: {
label: "Sub Heading",
group: 'ck_group',
command: 'contextmenu_h4'
},
pre: {
label: "Formatted",
group: 'ck_group',
command: 'contextmenu_pre'
},
imageinline: {
label: "Image inline",
group: 'ck_group',
command: 'contextmenu_imageinline'
},
imagecentered: {
label: "Image centered",
group: 'ck_group',
command: 'contextmenu_imagecentered'
},
imagecenteredwithdropshadow: {
label: "Image centered drop shadow",
group: 'ck_group',
command: 'contextmenu_imagecenteredwithdropshadow'
},
figureheading: {
label: "Figure/Table heading",
group: 'ck_group',
command: 'contextmenu_figureheading'
},
equation: {
label: "Equation",
group: 'ck_group',
command: 'contextmenu_equation'
},
equationdefinition: {
label: "Equation definition",
group: 'ck_group',
command: 'contextmenu_equationdefinition'
},
tablewithoutborder: {
label: "Table without border",
group: 'ck_group',
command: 'contextmenu_tablewithoutborder'
}
});
editor.contextMenu.addListener(function (element, selection, elementPath) {
var contentMenuItems = {
link: CKEDITOR.TRISTATE_ON,
bold: CKEDITOR.TRISTATE_ON,
numberedlist: CKEDITOR.TRISTATE_ON,
bulletedlist: CKEDITOR.TRISTATE_ON,
iconselector: CKEDITOR.TRISTATE_ON,
formatting: CKEDITOR.TRISTATE_ON
};
if (element.getAscendant('a', true)) {
//If we are already inside 'a' tag then remove link from Context menu else we will end up with two "Link" menus
delete contentMenuItems.link
}
if ($.trim(selection.getSelectedText()) === '') {
//If no text is selected then remove bold from context menu
delete contentMenuItems.bold;
//Similarly remove link if nothing is selected as it will insert hyperlink text as words inside CKEditor
delete contentMenuItems.link;
}
return contentMenuItems;
});
});
I know there is too much of code but I wasn't able to find any other simple way to do this.
When all done this is how it looks:
Quick recap:
Created plugin for each item appearing in Format combo
Only change in all the plugin.js file is pluginName (it is name of the folder)
Extend CKEditor and add applyFormatStyles method which will be invoked by plugin.js file
In your config file add all these as extra plugins
And on creation of CKEditor instance extend context menu

Related

ExtJS loadMask on the form is not working?

I have a form and I am trying to apply mask when submit button is clicked but somehow mask is not displaying on form.
var objMask = new Ext.LoadMask({
target: target,
msg: 'test',
//hideMode: 'display',
listeners: {
beforedestroy: function (lmask) {
//this.hide();
},
hide: function (lmask) {
//this.hide();
}
}
});
It is working in panel but in form, we are not getting anything.
Thanks in advance.
You need to call show method on Ext.LoadMask instance.
Example on https://fiddle.sencha.com/#view/editor&fiddle/3cuq
let target = Ext.create("Ext.form.Panel", {
renderTo: Ext.getBody(),
width: 400,
height: 400
});
var objMask = new Ext.LoadMask({
target: target,
msg: 'test',
//hideMode: 'display',
listeners: {
beforedestroy: function (lmask) {
//this.hide();
},
hide: function (lmask) {
//this.hide();
}
}
});
objMask.show();

jVectorMap active marker on page load

i have custom colors for regions and custom images for marker icons. Hovering, clicking on markers changes everytjing like i want but... How to make one marker, marked after loading a map, i cant find solution. I've been trying to click the marker icon with jquery, finding by element attribute, the click worked (according to the console log), but nothing changed on the map.
https://jsfiddle.net/6ss2eahr/7/
$(document).ready(function () {
var markers = [
{ latLng: [54.5039433, 18.3233958], name: 'Gdynia', region: 'PL-PM' },
{ latLng: [51.7472675, 18.0070145], name: 'Kalisz', region: 'PL-WP' },
{ latLng: [50.2138079, 18.8671087], name: 'Katowice', region: 'PL-SL' },
{ latLng: [50.8541274, 20.5456014], name: 'Kielce', region: 'PL-SK' }
];
var last_poi;
$('#map-pl').vectorMap({
map: 'pl_merc',
backgroundColor: '#fff',
zoomButtons: false,
zoomOnScroll: false,
regionsSelectable: false,
regionsSelectableOne: true,
markersSelectable: true,
markersSelectableOne: true,
markers: markers,
markerStyle: {
initial: {
image: 'https://www.royalparks.org.uk/_media/images/map_icons/find-my-location-icon.png'
},
hover: {
image: 'http://tiltedkilt.com/wp-content/themes/base/library/images/pin-small-icon.png',
cursor: 'pointer'
},
selected: {
image: 'http://tiltedkilt.com/wp-content/themes/base/library/images/pin-small-icon.png'
},
selectedHover: {
image: 'http://tiltedkilt.com/wp-content/themes/base/library/images/pin-small-icon.png'
}
},
regionStyle: {
hover: { fill: '#fdefc9' },
initial: { stroke: "white", "stroke-width": 1, fill: "#fcf8ed" },
selected: { fill: "#ffcc39" }
},
onMarkerClick: function (event, id) {
var mapObject = $('#map-pl').vectorMap('get', 'mapObject');
mapObject.clearSelectedRegions();
mapObject.setSelectedRegions(markers[id].region);
last_poi = id;
},
onMarkerOver: function (event, id) {
var mapObject = $('#map-pl').vectorMap('get', 'mapObject');
mapObject.clearSelectedRegions();
if (last_poi) {
mapObject.setSelectedRegions(markers[last_poi].region);
}
mapObject.setSelectedRegions(markers[id].region);
},
onMarkerOut: function (event, id) {
var mapObject = $('#map-pl').vectorMap('get', 'mapObject');
mapObject.clearSelectedRegions();
if (last_poi) {
mapObject.setSelectedRegions(markers[last_poi].region);
}
},
onRegionTipShow: function (e, label, code) {
e.preventDefault();
}
});
});
All the functions getSelectedMarkers, setSelectedRegions, and so on can handle array of values, so the solution is pretty easy to dynamically select one or more Marker and the corresponding Region:
var mapObject = $('#map-pl').vectorMap('get', 'mapObject');
// select Gdynia by index
var selectedMarkers = [0],
selectedRegions = [];
selectedMarkers.forEach(function(item) {
selectedRegions.push(mapObject.markers[item].config.region);
});
mapObject.setSelectedMarkers(selectedMarkers);
mapObject.setSelectedRegions(selectedRegions);
Your can remove the whole logic to keep track of the marker index using last_poi, which is causing the deselection in onMarkerOut and replace with the above function to get the selected Region directly from the mapObject.

Style nesting can be avoided in CKEditor?

I'd like to restrict the nesting of specific classes which were made available in the styles dropdown.
A span.my-style-1 should not be able to nest a span.my-style-2.
I was hoping to use the disallowedContent rule to at least recurse the selected elements and return true or false based on the classes applied to it's selection.
In fact it's stuck and only prints a fake element based on the first style (my-style-1).
CKEDITOR.stylesSet.add( 'my_styles', [
{ name: 'My Style 1', element: 'span', attributes: { 'class': 'my-style-1' } },
{ name: 'My Stile 2', element: 'span', attributes: { 'class': 'my-stile-2' } },
]
);
CKEDITOR.config.allowedContent = {
$1: {
elements: CKEDITOR.dtd,
attributes: true,
styles: true,
classes: true
}
};
CKEDITOR.config.disallowedContent = {
span: {
match: function( element ) {
console.log(element.classes, element.children.length, element.parent);
return false;
}
}
}

adding options to the ckeditor inline tool bar not working

I'm having some trouble adding toolbar options to the CKEditor4 inline toolbar option. I've been reading the docs and still can't figure out where my problem is.
I'm creating a div on the fly, then adding the CKEditor to the div. Everything works fine, but I want to remove some of the tool bar options and add some other ones. When I add parameters to the inline() call nothing changes?
Here is how I create the instance on the fly:
.on('dblclick', function(e){
e.preventDefault();
e.stopPropagation();
ed.ck_restore();
ed.ck_active_block = $(this).attr("id");
ed.ck_block_data = $(this).html();
var block_width = $(this).css("width");
var block_height = $(this).css("height")+20;
var block_padding_top = $(this).css("padding-top");
var block_padding_right = $(this).css("padding-right");
var block_padding_bottom = $(this).css("padding-bottom");
var block_padding_left = $(this).css("padding-left");
var padding = 'padding-top: '+block_padding_top+';padding-right: '+block_padding_right+';padding-bottom: '+block_padding_bottom+';padding-left: '+block_padding_left+';';
var editor = '<div id="edit" contenteditable="true" style="margin-top: -'+block_padding_top+'; margin-left: -'+block_padding_left+';'+padding+' width: '+block_width+'; height: '+block_height+';background-color: #fff;position: absolute;">'+ed.ck_block_data+'</div>';
$("#"+ed.ck_active_block).prepend(editor);
if(CKEDITOR.instances.edit)
{
CKEDITOR.instances.edit.destroy(); //remove any previously created instances
}
CKEDITOR.inline("edit",
[CKEDITOR.config.fontSize_style = {
element: 'span',
styles: { 'font-size': '#(size)' },
overrides: [ {
element: 'font', attributes: { 'size': null }
}]
}]
);
$("#edit").click(function(e){e.stopPropagation();}).focus();
$("w_save").text("1");
});
http://docs.ckeditor.com/#!/api/CKEDITOR-method-inline
The docs imply I can pass a configuration parameter to change the options, but I'm missing something and after 3 hours of trying I need a bit of help.
Any help is appreciated.
Thanks.
I don't know what that meant to be:
CKEDITOR.inline("edit",
[CKEDITOR.config.fontSize_style = {
element: 'span',
styles: { 'font-size': '#(size)' },
overrides: [ {
element: 'font', attributes: { 'size': null }
}]
}]
);
It is invalid JavaScript code. Have you checked your JS console?
Anyway, CKEDITOR.inline accepts two arguments - name and config object.
CKEDITOR.inline( 'edit', {
fontSize_style: {
element: 'span',
styles: { 'font-size': '#(size)' },
overrides: [
{ element: 'font', attributes: { 'size': null } }
]
},
language: 'pl',
removeButtons: 'Bold,Italic' // or set toolbar or toolbarGroups.
} );
Thanks for the reply Reinmar. I was able to find another solution. Here is the code for others:
CKEDITOR.inline("edit",{customConfig : "mycustomConfig.js"});
This referenced a custom config file, I just pulled it from an example config on the CKEditor site and changed some of the options. Works now.

Replace the image plugin in CKeditor

I want to override the image plugin in CKeditor. When I right click on an image I want to open my own dialog. Can anyone point me in the right direction. I've done a basic plugin which I copied from the CKeditor site - How do I swap this to replace the image editor.
CKEDITOR.plugins.add('myplugin',
{
init: function (editor) {
editor.addCommand('mydialog', new CKEDITOR.dialogCommand('mydialog'));
if (editor.contextMenu) {
editor.addMenuGroup('mygroup', 10);
editor.addMenuItem('My Dialog',
{
label: 'Open dialog',
command: 'mydialog',
group: 'mygroup'
});
editor.contextMenu.addListener(function (element) {
return { 'My Dialog': CKEDITOR.TRISTATE_OFF };
});
}
CKEDITOR.dialog.add('mydialog', function (api) {
// CKEDITOR.dialog.definition
var dialogDefinition =
{
title: 'Sample dialog',
minWidth: 390,
minHeight: 130,
contents: [
{
id: 'tab1',
label: 'Label',
title: 'Title',
expand: true,
padding: 0,
elements:
[
{
type: 'html',
html: '<p>This is some sample HTML content.</p>'
},
{
type: 'textarea',
id: 'textareaId',
rows: 4,
cols: 40
}
]
}
],
buttons: [CKEDITOR.dialog.okButton, CKEDITOR.dialog.cancelButton],
onOk: function () {
// "this" is now a CKEDITOR.dialog object.
// Accessing dialog elements:
var textareaObj = this.getContentElement('tab1', 'textareaId');
alert("You have entered: " + textareaObj.getValue());
}
};
return dialogDefinition;
});
}
});
Hi the reason I wanted to do this was that we have our image editor control which for "usability" reasons we need to carry on using. It gets used in different bits of the site and two dialogs would confuse people. In summary what I did was
Remove the image plugin CKEDITOR.config.removePlugins = 'image, forms, div,flash,iframe,table';
Add extra plugins extraPlugins: 'tinsertimage,teditimage,teditlink,tinsertlink,teditimagelink' on creating the CKEditor
In the plugin run some JS which intercept the right click on the image
CKEDITOR.plugins.add('teditimage',
{
init: function (editor) {
editor.addCommand('tEditImage',
{
exec: function (editor) {
//This opens the custom editor
ZWSInlineEditor.openImageProperties(editor, false);
}
});
if (editor.addMenuItem) {
// A group menu is required
// order, as second parameter, is not required
editor.addMenuGroup('gImage');
// Create a manu item
editor.addMenuItem('gEditImageItem', {
label: 'Edit Image Properties',
command: 'tEditImage',
group: 'gImage'
});
}
if (editor.contextMenu) {
editor.contextMenu.addListener(function (element, selection) {
// Get elements parent, strong parent first
var parents = element.getParents("img");
// Check if it's strong
if (parents[0].getName() != "img")
return null; // No item
return { gEditImageItem: CKEDITOR.TRISTATE_ON };
});
}
}
});
I don't understand what's the point in what you're doing (or please explain us). Maybe you should rather customize dialogs than do things from scratch?

Resources