Prepend CKEditor element with another element - ckeditor

Whenever a table-element is inserted (through the table-icon), I would like to prepend it with another element. Eg.
<div>Hello World!</div> <!-- this was automatically added -->
<table>
<tr>
<td>A</td>
<td>A</td>
<td>A</td>
</tr>
</table>
Trying to achieve this with a custom plugin, but I just can't get it to work:
(function ($) {
CKEDITOR.plugins.add('hello_world', {
init: function (editor) {
editor.on('insertElement', function(ev) {
if (ev.data.getName() === 'table') {
var helloWorld = new CKEDITOR.dom.element('div');
helloWorld.appendText('Hello World!');
ev.data.insertBeforeMe(helloWorld);
}
});
}
});
})(jQuery);
Console returns an "Uncaught TypeError: Cannot read property 'insertBefore' of null" error. The API documentation (http://docs.ckeditor.com/#!/api/CKEDITOR.dom.element) however states that the insertBefore and insertBeforeMe functions are available.

You are getting this error because at this stage, the element is not yet added to the CKEditor (therefore its parent is null).
If you don't mind adding the comment right after the element, you may use the following code instead:
CKEDITOR.plugins.add('hello_world', {
init: function (editor) {
editor.on('insertElement', function(ev) {
if (ev.data.getName() === 'table') {
ev.data.append(new CKEDITOR.dom.comment(' this was automatically added '), true );
}
});
}
});
Then you will get the following output:
<table><!-- this was automatically added -->
See JSFiddle here.
Note: if you must add the comment before the table and you're feeling brave, you may delay the addition of the comment by using a timer:
CKEDITOR.plugins.add('hello_world', {
init: function (editor) {
editor.on('insertElement', function(ev) {
if (ev.data.getName() === 'table') {
var table = ev.data;
setTimeout(function () { table.insertBeforeMe(new CKEDITOR.dom.comment(' this was automatically added '))}, 0);
}
});
}
});
You will then get the desired output:
<!-- this was automatically added -->
<table>
See JSFiddle here

May be this solution of you problem:
CKEDITOR.editorConfig = function( config ) {
config.extraPlugins = 'responsivetables';
};
CKEDITOR.plugins.add('responsivetables', {
init: function (editor) {
editor.on('insertElement', function (event) {
if (event.data.getName() === 'table') {
var div = new CKEDITOR.dom.element('div').addClass('table-scroll'); // Create a new div element to use as a wrapper.
div.append(event.data); // Append the original element to the new wrapper.
event.data = div; // Replace the original element with the wrapper.
}
}, null, null, 1);
}
});

Related

mithril.js issues with CKEditor

I've written this code using mithril.js + CKEditor
<body>
<script>
var frm = {};
frm.vm = (function () {
var vm = {};
vm.afterLoad = function () {
CKEDITOR.replace( 'editor1' );
};
vm.btnClick = function () {
console.log(document.getElementById('editor1').value);
};
return vm;
})();
frm.controller = function () {
};
frm.view = function (controller) {
return m("div", {config: frm.vm.afterLoad}, [
m("textarea", {id: "editor1"}),
m("button", {onclick: frm.vm.btnClick}, "Click here to see the text you've typed")
])
;
};
m.mount(document.body, frm);
</script>
</body>
but when I click the button, I see this error:
Uncaught The editor instance "editor1" is already attached to the provided element.
and console.log() prints a blank line.
What am I doing wrong?
I found the problem. To get the value of the CKEditor, one needs to use
CKEditor.instance.nameoftextarea.getData()

How add class to parent in CKEditor

I use simpleuploads plugin.
When images are uploaded i need add to parent of them a class.
This is HTML code wich is pasted after image upload process
<div><img src="link/to/image"><img src="link/to/image2"></div>
Now i need to add class to the 'div' element but i don't know how.
To remove height and width attributes of < img > elements i use this code:
<script>
CKEDITOR.on('instanceReady', function(e) {
e.editor.on( 'simpleuploads.finishedUpload' , function(ev)
{
var element = ev.data.element;
if (element.getName() == 'img')
{
var img = element.$;
img.removeAttribute('width');
img.removeAttribute('height');
ev.stop(); // Stop the event in case the listener is inserted twice
}
});
});
</script>
so will be great if someone can help me to add class using it.
This is the right code:
<script>
CKEDITOR.on('instanceReady', function(e) {
e.editor.on( 'simpleuploads.finishedUpload' , function(ev)
{
var element = ev.data.element;
if (element.getName() == 'img')
{
var img = element.$;
img.removeAttribute('width');
img.removeAttribute('height');
img.parentNode.setAttribute('class','classname');
ev.stop(); // Stop the event in case the listener is inserted twice
}
});
});
</script>

Bootstrap typeahead suggestions replaced when navigation

I'm using Bootstrap Typeahead to suggest som search results. The results are returned from a ajax ressource, and since this resource creates a delay, I'm experiencing a unfortunate effect.
Example:
If typing a 4 letter word, the suggestions will appear after 2 letters, I can then go through the results with the keys up/down, but suddenly the suggestions will reload because the last request has finished.
Is there any way to "cancel" any remaining, if user is currently using the keys up/down to go through the suggestions?
('#query').typeahead({
items: 4,
source: function (query,process) {
map = {};
$.getJSON('/app_dev.php/ajax/autosuggest/'+query, function (data) {
vehicles = [];
$.each(data, function(i,vehicle){
map[vehicle.full] = vehicle;
vehicles.push(vehicle.full);
});
process(vehicles);
});
},
updater: function (item) {
// do something here when item is selected
},
highlighter: function (item) {
return item;
},
matcher: function (item) {
return true;
}
});
I think the following will satisfy your needs (its hard to reproduce exactly) :
There is no easy way to abort a delayed response, but you could extend typeahead as I figured out here (without modifying bootstrap.js)
The concept is to catch keydown, detect if the event is KEY_UP or KEY_DOWN, set a flag is_browsing, and then abort process if is_browsing is true (that is, if the user has hitted KEY_UP or KEY_DOWN and no other keys afterwards).
Extending typeahead :
// save the original function object
var _superTypeahead = $.fn.typeahead;
// add is_browsing as a new flag
$.extend( _superTypeahead.defaults, {
is_browsing: false
});
// create a new constructor
var Typeahead = function(element, options) {
_superTypeahead.Constructor.apply( this, arguments )
}
// extend prototype and add a _super function
Typeahead.prototype = $.extend({}, _superTypeahead.Constructor.prototype, {
constructor: Typeahead
, _super: function() {
var args = $.makeArray(arguments)
// call bootstrap core
_superTypeahead.Constructor.prototype[args.shift()].apply(this, args)
}
//override typeahead original keydown
, keydown: function (e) {
this._super('keydown', e)
this.options.is_browsing = ($.inArray(e.keyCode, [40,38])>-1)
}
//override process, abort if user is browsing
, process: function (items) {
if (this.options.is_browsing) return
this._super('process', items)
}
});
// override the old initialization with the new constructor
$.fn.typeahead = $.extend(function(option) {
var args = $.makeArray(arguments),
option = args.shift()
// this is executed everytime element.modal() is called
return this.each(function() {
var $this = $(this)
var data = $this.data('typeahead'),
options = $.extend({}, _superTypeahead.defaults, $this.data(), typeof option == 'object' && option)
if (!data) {
$this.data('typeahead', (data = new Typeahead(this, options)))
}
if (typeof option == 'string') {
data[option].apply( data, args )
}
});
}, $.fn.typeahead);
This typeahead-extension could be placed anywhere, eg in a <script type="text/javascript"> -section
Testing the extension :
<input type="text" id="test" name="test" placeholder="type some text" data-provide="typeahead">
<script type="text/javascript">
$(document).ready(function() {
var url='typeahead.php';
$("#test").typeahead({
items : 10,
source: function (query, process) {
return $.get(url, { query: query }, function (data) {
return process(data.options);
});
}
});
});
</script>
A "serverside" PHP script that returns a lot of randomized options with forced delay, typeahead.php :
<?
header('Content-type: application/json');
$JSON='';
sleep(3); //delay execution in 3 secs
for ($count=0;$count<30000;$count++) {
if ($JSON!='') $JSON.=',';
//create random strings
$s=str_shuffle("abcdefghijklmnopq");
$JSON.='"'.$s.'"';
}
$JSON='{ "options": ['.$JSON.'] }';
echo $JSON;
?>
It really seems to work for me. But I cannot be sure that it will work in your case. Let me now if you have success or not.

hidding elements in a layout page mvc3

ok so im having a hard time hiding some layout sections (divs in my layout page and im using mvc3).
I have this js fragment which is basically the main logic:
$('.contentExpand').bind('click', function () {
$.cookie('right_container_visible', "false");
});
//Cookies Functions========================================================
//Cookie for showing the right container
if ($.cookie('right_container_visible') === 'false') {
if ($('#RightContainer:visible')) {
$('#RightContainer').hide();
}
$.cookie('right_container_visible', null);
} else {
if ($('#RightContainer:hidden')) {
$('#RightContainer').show();
}
}
as you can see, im hidding the container whenever i click into some links that have a specific css. This seems to work fine for simple tests. But when i start testing it like
.contentExpand click --> detail button click --> .contentExpand click --> [here unexpected issue: the line $.cookie('right_container_visible', null); is read but it doesnt set the vaule to null as if its ignoring it]
Im trying to understand whats the right logic to implement this. Anyone knows how i can solve this?
The simpliest solution is to create variable outside delegate of bind.
For example:
var rightContVisibility = $.cookie('right_container_visible');
$('.contentExpand').bind('click', function () {
$.cookie('right_container_visible', "false");
rightContVisibility = "false";
});
if (rightContVisibility === 'false') {
...
}
The best thing that worked for me was to create an event that can catch the resize of an element. I got this from another post but I dont remember which one. Anyway here is the code for the event:
//Event to catch rezising============================================================================
(function () {
var interval;
jQuery.event.special.contentchange = {
setup: function () {
var self = this,
$this = $(this),
$originalContent = $this.text();
interval = setInterval(function () {
if ($originalContent != $this.text()) {
$originalContent = $this.text();
jQuery.event.handle.call(self, { type: 'contentchange' });
}
}, 100);
},
teardown: function () {
clearInterval(interval);
}
};
})();
//=========================================================================================
//Function to resize the right container============================================================
(function ($) {
$.fn.fixRightContainer = function () {
this.each(function () {
var width = $(this).width();
var parentWidth = $(this).offsetParent().width();
var percent = Math.round(100 * width / parentWidth);
if (percent > 62) {
$('#RightContainer').remove();
}
});
};
})(jQuery);
//===================================================================================================

CKeditor save event

I was following the steps written in this topic: CKEditor, AJAX Save
I tried to fire a custom 'saved.ckeditor' event if anybody press the AjaxSave button. But I did not succeeded.
ckeditor/plugins/ajaxsave/plugin.js:
(function(){
var saveCmd =
{
modes : { wysiwyg:1, source:1 },
exec : function( editor )
{
editor.fire('saved.ckeditor');
$(editor).trigger('saved.ckeditor', editor.getData());
alert(editor.getData());
}
}
var pluginName = 'ajaxsave';
CKEDITOR.plugins.add( pluginName,
{
init : function( editor )
{
var command = editor.addCommand( pluginName, saveCmd );
command.modes = { wysiwyg : !!( editor.element.$.form ) };
editor.ui.addButton( 'AjaxSave',
{
label : editor.lang.save,
command : pluginName,
className : 'cke_button_save'
});
}
});
})();
If I get or set the editor data in the function, the get and set events will automatically be fired. But I could not even fire a 'getData.ckeditor' event manually.
Any tips?
An other thing: how can I disable the button if the editor's content haven't changed since the last save (it is not dirty)?
I have a workaround.
Outside I can listen to the set event:
window.onload = function()
{
var ckparams={width: 500, height: 400, resize_enabled:false, extraPlugins: 'ajaxsave',toolbar:[['AjaxSave','Source','-','Bold','Italic','Underline','-','Undo','Redo','-','Find','Replace','-','SelectAll','-','SpellChecker','Scayt','-','About']]};
//CKEDITOR.replace('editor', ckparams);
var editor = $('textarea.editor').ckeditor(ckparams);
$(editor).bind('setData.ckeditor', function() {
//do what I want
});
};
...and when the button is pressed, set the data with its current value:
var saveCmd =
{
modes : { wysiwyg:1, source:1 },
exec : function( editor )
{
editor.setData(editor.getData());
}
}
This way at least an event is fired...
But I shold be careful when I manually set the content from outside...
try this, you need to finish the exec() function
(function() {
var saveCmd = {
modes:{wysiwyg:1,source:1 },
readOnly: 1,
exec: function( editor ) {
var data = editor.getData();
console.info(data);
}
};
var pluginName = 'ajaxSave';
// Register a plugin named "save".
CKEDITOR.plugins.add( pluginName, {
lang: 'af,ar,bg,bn,bs,ca,cs,cy,da,de,el,en-au,en-ca,en-gb,en,eo,es,et,eu,fa,fi,fo,fr-ca,fr,gl,gu,he,hi,hr,hu,is,it,ja,ka,km,ko,ku,lt,lv,mk,mn,ms,nb,nl,no,pl,pt-br,pt,ro,ru,sk,sl,sq,sr-latn,sr,sv,th,tr,ug,uk,vi,zh-cn,zh', // %REMOVE_LINE_CORE%
icons: 'save', // %REMOVE_LINE_CORE%
init: function( editor ) {
// Save plugin is for replace mode only.
if ( editor.elementMode != CKEDITOR.ELEMENT_MODE_REPLACE ) return;
editor.ui.addButton && editor.ui.addButton( 'Save', {
label: editor.lang.save.toolbar,
command: pluginName,
toolbar: 'document,10'
});
}
});
})();
and don't forget to enable the plugin in config.js
config.extraPlugins = 'ajaxSave';
You can edit the functionality of the regular save button to do what you want:
html:
<textarea id="CKEditor1"></textarea>
javascript:
<script>
// Need to wait for the ckeditor instance to finish initialization
// because CKEDITOR.instances.editor.commands is an empty object
// if you try to use it immediately after CKEDITOR.replace('editor');
CKEDITOR.on('instanceReady', function (ev) {
// Create a new command with the desired exec function
var overridecmd = new CKEDITOR.command(editor, {
exec: function(editor){
// Replace this with your desired save button code
alert(editor.document.getBody().getHtml());
}
});
// Replace the old save's exec function with the new one
ev.editor.commands.save.exec = overridecmd.exec;
});
CKEDITOR.replace('CKEditor1');
</script>

Resources