I build a small CKEditor plugin, where a button insert a span tag. The span must be the same height as the p, so i need the css line-height value or the height from the p tag. Current plugin code:
CKEDITOR.plugins.add('border',{
icons: 'border',
init: function(editor){
editor.addCommand('addBox',{
exec: function(editor) {
var box = editor.document.createElement('span');
box.setAttribute('style', 'border:1px solid #000;display:inline-block;width:40px;height:40px;vertical-align: text-top;');
box.setAttribute('class', 'borderbox');
editor.insertElement(box);
}
});
editor.ui.addButton('empty box',{
label: 'insert box',
command: 'addBox',
toolbar: 'links'
});
}
});
how can i access the parent p?
Solution found: editor.elementPath().elements[0] return the first parent element.
Related
I have an editable custom widget that can be placed in CKEditor 4 by
clicking a button in the toolbar - works fine = does not allow nesting
drag & drop from outside of the editor - allows nesting
I do not want to let user to have nested content of the widget. On the other hand I do want users to able to edit the content of the widget.
NOTE
click the button to insert widget's content. Click inserted text and the button becomes not available for clicking. Click some other text and you would be able to insert again. This is desired behavior.
the widget button will not be present in the final version of the CKEditor application / widget. Only drag&drop way of inserting text will be available
Insert two widgets in the editor and try to drag&drop one inside the other one. It will not work. This is desired behavior.
Now try to insert a widget and then insert another one by drag&drop of the text "master or editable" into existing widget. It will be possible.
Could someone help me to set CKEditor a way so nesting is NOT possible?
Working jsfiddle.
init: function( editor ) {
editor.widgets.add( 'simplebox', {
button: 'Create a simple box',
template:
'<div class="simplebox">' +
'<h2 class="simplebox-title">Title</h2>' +
'<div class="simplebox-content"><p>Content<br>.<br>.<br>.</p></div>' +
'</div>',
editables: {
title: {
selector: '.simplebox-title',
allowedContent: 'br strong'
},
content: {
selector: '.simplebox-content',
allowedContent: 'p br ul ol li strong em'
}
},
allowedContent:
'div(!simplebox); div(!simplebox-content); h2(!simplebox-title)',
requiredContent: 'div(simplebox)',
upcast: function( element ) {
return element.name == 'div' && element.hasClass( 'simplebox' );
}
} );
}
} );
I have create a simple CKEditor widget that highlights the elements that have the class "pink".
I have also added a "Pinkify" button to the toolbar, which replaces the HTML of the selected element with some other elements that have the class "pink".
What I observe when I click the button is that widgets are not created for the freshly inserted elements. However, when I toggle between Source mode and WYSISYG mode, the widgets get created.
See the jsfiddle and its code:
CKEDITOR.replace('ck', {
allowedContent: true,
extraPlugins: 'pink'
});
CKEDITOR.plugins.add('pink', {
requires: 'widget',
init: function(editor) {
editor.widgets.add('pinkwidget', {
upcast: function(element) {
return element.hasClass('pink');
}
});
editor.addCommand('pinkify', {
editorFocus: 1,
exec: function(editor) {
var selection = editor.getSelection(),
selectedElement = selection.getStartElement();
if (selectedElement) {
selectedElement.setHtml("Let's have some <span class=\"pink\">pink</span> widget here!");
editor.widgets.checkWidgets(); // needed?
}
}
});
editor.ui.addButton('pinkify', {
label: 'Pinkify',
command: 'pinkify'
});
},
onLoad: function() {
CKEDITOR.addCss('.cke_widget_pinkwidget { background: pink; }');
}
});
I am aware of this question on Stackoverflow, but I can't get it to work with setHtml called on an element. Can you suggest how to modify the code so that widgets get created as soon as the HTML is updated?
According to the CKEditor team, it is normal that CKEDITOR.dom.element.setHtml does not instanciate widgets (see Widgets not initialised after calling setHtml on an element).
So the workaround they gave me was to rewrite the code that insert HTML in place of the selected element to:
if (selectedElement) {
selectedElement.setHtml("");
editor.insertHtml("Let's have some <span class=\"pink\">pink</span> widget here!");
}
For those like me who didn't know, editor.insertHTML inserts HTML code into the currently selected position in the editor in WYSIWYG mode.
Updated jsFiddle here.
I'm currently creating a 'smartobject' widget. In the widgets dialog, the user can choose a 'smartobject', which simply put, generates some html, which should be added to the editor. Here comes the tricky part: the html sometimes div elements and sometimes simply span elements. In the case of the div variant, the widget should be wrapped in a div 'template'. In the case of a span variant, the widget should be wrapped in a span and the html should be added 'inline'.
In the widgets API I see the following way to define a template:
editor.widgets.add('smartobject', {
dialog: 'smartobject',
pathName: lang.pathName,
template: '<div class="cke_smartobject"></div>', // <------
upcast: function(element) {
return element.hasClass('smartObject');
},
init: function() {
this.setData('editorHtml', this.element.getOuterHtml());
},
data: function() {
var editorHtml = this.data.editorHtml;
var newElement = new CKEDITOR.dom.element.createFromHtml(editorHtml);
newElement.copyAttributes(this.element);
this.element.setText(newElement.getText());
}
});
But in my case, the template is more dynamic: sometimes a div and sometimes the span will do the correct thing..
How can I fix this without needing to create two widgets which will do the exact same thing, with only the wrapping element as difference?
I've already tried to replace the entire element in the 'data' method, like:
newElement.replace(this.element);
this.element = newElement;
But this seemed not supported: resulted in undefined errors after calling editor.getData().
I'm using ckeditor v4.5.9
Thanks for your help!
It seems I got it working (with a workaround).
The code:
CKEDITOR.dialog.add('smartobject', this.path + 'dialogs/smartobject.js');
editor.widgets.add('smartobject', {
pathName: lang.pathName,
// This template is needed, to activate the widget logic, but does nothing.
// The entire widgets html is defined and created in the dialog.
template: '<div class="cke_smartobject"></div>',
init: function() {
var widget = this;
widget.on('doubleclick', function(evt) {
editor.execCommand('smartobject');
}, null, null, 5);
},
upcast: function(element) {
return element.hasClass('smartObject');
}
});
// Add a custom command, instead of using the default widget command,
// otherwise multiple smartobject variants (div / span / img) are not supported.
editor.addCommand('smartobject', new CKEDITOR.dialogCommand('smartobject'));
editor.ui.addButton && editor.ui.addButton('CreateSmartobject', {
label: lang.toolbar,
command: 'smartobject',
toolbar: 'insert,5',
icon: 'smartobject'
});
And in the dialog, to insert code looks like:
return {
title: lang.title,
minWidth: 300,
minHeight: 80,
onOk: function() {
var element = CKEDITOR.dom.element.createFromHtml(smartobjectEditorHtml);
editor.insertElement(element);
// Trigge the setData method, so the widget html is transformed,
// to an actual widget!
editor.setData(editor.getData());
},
...etc.
UPDATE
I made the 'onOk' method a little bit better: the smartobject element is now selected after the insertion.
onOk: function() {
var element = CKEDITOR.dom.element.createFromHtml(smartobjectEditorHtml);
var elementId = "ckeditor-element-" + element.getUniqueId();
element.setAttribute("id", elementId);
editor.insertElement(element);
// Trigger the setData method, so the widget html is transformed,
// to an actual widget!
editor.setData(editor.getData());
// Get the element 'fresh' by it's ID, because the setData method,
// makes the element change into a widget, and thats the element which should be selected,
// after adding.
var refreshedElement = CKEDITOR.document.getById(elementId);
var widgetWrapperElement = CKEDITOR.document.getById(elementId).getParent();
// Better safe then sorry: if the fresh element doesn't have a parent, simply select the element itself.
var elementToSelect = widgetWrapperElement != null ? widgetWrapperElement : refreshedElement;
// Normally the 'insertElement' makes sure the inserted element is selected,
// but because we call the setData method (to ensure the element is transformed to a widget)
// the selection is cleared and the cursor points to the start of the editor.
editor.getSelection().selectElement(elementToSelect);
},
So in short, I partially used the widget API for the parts I wanted:
- Make the html of the widget not editable
- Make it moveable
But I created a custom dialog command, which simply bypasses the default widget insertion, so I can entirely decide my own html structure for the widget.
All seems to work like this.
Any suggestions, to make it better are appreciated:)!
As suggested in this ckeditor forum thread, the best approach would be to set the template to include all possible content elements. Then, in the data function, remove the unnecessary parts according to your specific logic.
When editing content that includes a table in ckeditor, it shows a border around table cells even though there is no border in the markup. This seems to be a convenience feature, so I'd like to be able to toggle it, perhaps via a checkbox in the toolbar. Is this possible? Perhaps there is a plugin of some sort that I have not configured? Thanks for your help.
Screen shot of table borders
This this best example to hide table border. Key player is : startupShowBorders: false,
$(function () {
var editor = CKEDITOR.replace("textarea", {
width: 750, height: 500, fullPage: true,
extraPlugins: 'stylesheetparser',
allowedContent: true,
qtBorder: '0',
startupShowBorders: false,
////pasteFilter: 'semantic-content',
//// Custom stylesheet for editor content.
//// contentsCss: ['content/css/html-email.css'],
//// Do not load the default Styles configuration.
stylesSet: []
});
});
I'm trying to implement my own Image plugin replacement for CKEditor. I bootstraped an implementation from the tutorial at http://docs.ckeditor.com/#!/guide/plugin_sdk_sample_1
Right now, the only attribute thing editable is the src
In the code below, $.imagebrowser.openPopup(callback) opens a popup once the user has made his selection, calls callback, with the new src attribute of the image.
This works fine, both for insertion and edition, but there is a glich in the undo / redo integration. A modification of the src attribute made by doubleclicking is not undoable until an othe modification occurs (like typing text). Then the modification of the src attribute seems to be properly integrated in the undo/redo stack, and I can undo and redo it.
Any idea of what I'm doing wrong ?
CKEDITOR.plugins.add( 'customimage', {
// Register the icons. They must match command names.
icons: 'customimage',
// The plugin initialization logic goes inside this method.
init: function( editor) {
editor.on( 'doubleclick', function( event ) {
if(event.data.element.getName() == "img") {
$.imagebrowser.openPopup(function(src) {
event.data.element.setAttribute("src", src);
});
}
});
editor.addCommand( 'insertCustomimage', {
allowedContent: ['img[!src,alt]{width,height,float,margin}'],
// Define the function that will be fired when the command is executed.
exec: function() {
$.imagebrowser.openPopup(function(src) {
editor.insertHtml('<img src="' + src + '" style="width: 400px; height: auto; float: left; margin: 10px 10px;">');
});
}
});
// Create the toolbar button that executes the above command.
editor.ui.addButton( 'Customimage', {
label: 'Image',
command: 'insertCustomimage',
toolbar: 'insert'
});
}
});
I'm not sure this is what your looking for but you can make snapshots:
editor.fire( 'saveSnapshot' );
This will add a state to the Undo/redo stack.
This command should be added before this line:
event.data.element.setAttribute("src", src);
The editor.insertHtml() function has this included in the function. But if you're editing tags you need to do this manually