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.
Related
When using the config code shown below the title is with error shown at the image. This also occurs at other fields such as tooltip and labels.
But when I tried it at https://codepen.io/ I've a got a correct result (title as expected).
Does anyone know how to troubleshoot this?
const config = {
type: 'bar',
data: dataSource,
options: {
plugins: {
title: {
display: true,
text: 'Código',
}
}
}
};
Try to replace ó by its HexCode as follows.
title: {
display: true,
text: 'C' + '\xF3' + 'digo'
}
or...
title: {
display: true,
text: 'Código'.replace('ó', '\xF3')
}
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
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;
}
}
}
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?
I have a Sencha Touch app that has a nested list.
The nestedList automatically creates its own toolBar.
I would like to dock a panel below the toolbar, but above the list-items. And I only need this on the top-level of the list. I am hoping to have it disappear after the first leaf is selected.
Does this sound like something doable? As you can see in my code, I only have the ability to dock an item panel on top of the current toolbar.
Thanks so much in advance. I really appreciate any advice you guys might have.
Al.
Below is what I have so far...
// Menu Pages
var menu = new Ext.NestedList({
fullscreen: true,
title: 'Menu',
displayField: 'text',
store: menu_store,
// ** This is the dockedItem I would like to insert between the toolbar and list-items
dockedItems: [ {
xtype : 'panel',
dock : 'top',
html : '<span>This is the logo panel</span>',
cls : 'front-logo-panel',
flex : 1
}],
// Add Panel for Leaf nodes
getDetailCard: function(item, parent) {
var itemData = item.attributes.record.data,
parentData = parent.attributes.record.data,
detailCard = new Ext.Panel({
scroll: 'vertical',
cls: 'menu-item-panel',
styleHtmlContent : true,
tpl: menuTemplate,
// add button to Leaf Node
listeners: {
activate: function() {
Ext.getCmp('itemToolbar').setTitle('New Title Bar');
}
}
});
detailCard.update(itemData);
this.backButton.setText(parentData.text);
return detailCard;
},
// add template for all nodes
getItemTextTpl: function() {
var tplConstructor =
'<tpl if="newItem">' +
'<span class="list-new-item">New Item!</span>' +
'</tpl>'+
'{text}' +
'<tpl>'+
'<div class="metadata">' +
'{description:ellipsis(40)}' +
'</div>' +
'</tpl>';
return tplConstructor;
}
});
Old question, I know, but to solve this, you can remove the toolbar from the list (without destroying it) and add it to a panel above the list. All nestedList functionality stays the same on the toolbar. Here's the solution I have:
First, I created a view that extends from NestedList and contains a toolbar:
Ext.define('MyApp.view.DynamicList', {
extend: 'Ext.dataview.NestedList',
alias: 'widget.dynamiclist',
config: {
toolbar: {
xtype: 'toolbar',
docked: 'top',
itemId: 'header-bar',
layout: {
pack: 'end',
type: 'hbox'
},
items: [
{
xtype: 'spacer'
},
{
xtype: 'button',
itemId: 'show-nav-sheet-button',
ui: 'plain',
width: 45,
iconCls: 'more'
}
]
}
}
});
Next, I created a main panel with a vbox layout that contains a child panel and this toolbar:
Ext.define('MyApp.view.MainContainer', {
extend: 'Ext.Container',
requires: [
'MyApp.view.DynamicList'
],
config: {
id: 'main-container',
layout: {
type: 'vbox'
},
items: [
{
xtype: 'panel',
flex: 1,
itemId: 'info-container'
},
{
xtype: 'dynamiclist',
flex: 1
}
]
}
});
Then, in a controller, I listen for the initialize event of the nested list. When it's fired, I remove the toolbar from the nested list and add it to the panel.
onNestedListInitialize: function() {
// need to wait until everythin is initialized;
var me = this;
var renderFn = function renderPanels() {
var main = me.getMainContainer();
// wait until main is intialized;
if(!main) {
Ext.defer(renderFn, 50, this);
return;
}
var list = main.down('#my-list');
var infocont = main.down('#info-container');
// wait until the container's components are initialized
if(!list || !infocont) {
Ext.defer(renderFn, 50, this);
return;
}
// remove the toolbar from the list, and add it to the container.
var toolbar = list.down('#header-bar');
list.remove(toolbar, false);
infocont.add(toolbar);
}
// call the function for the first time.
renderFn();
}
Note, I had to add a few checks to make sure the components were correctly initialized before using them, but the heart of it comes to the list.remove(toolbar, false) followed by the infocont.add(toolbar) commands. The false flag in the .remove() method means that the item won't be destroyed, so it's available to be re-added to the panel.