Programmatically invoking a button with CKEditor 4 - ckeditor

Hello CKEditor experts,
Can I use a javascript-code (in-page) to invoke a button and dialog of the CKEditor?
I am trying to bring back the usage of the "anchor" button into our CryptPad collaborative suite. To do so, I wish to write an integration test that invokes the "anchor" button then check that the exported content indeed includes the expected <a name="..."> element. Our tests are actually scripts running inside the page (and invoked by selenium). What function could I invoke, as part of the JS page, to invoke that button or invoke exactly the same content addition as the invocation of the button does?
thanks in advance.

Usually buttons, have defined commands. If you don't know what command is bound to button you want to invoke, then you can find it:
var editor = CKEDITOR.insances[ instanceName ];
console.log( editor.ui.get( 'Anchor' ).command ) // 'anchor'
editor.execCommand( 'anchor' ) // Dialog opens
Once we know our command, testing code would like like:
editor.on( 'dialogShow', function( evt ) {
evt.data // Dialog instance
// All code goes here
} );
editor.execCommand( 'anchor' );
Visit docs for more detailed informations on dialog methods, which might help you with proper testing.
https://docs.ckeditor.com/ckeditor4/latest/api/CKEDITOR_dialog.html

Related

create and show one use only dialog, constructed based on global state.

I have a plugin which need to show a (Modal) dialog each time the user double click on a word.
Detecting double click is no problem, but the exact fields/values in the dialog depends on exactly which word the user clicked on, and some mutable global state. So I can't create the dialog until the moment before I need to show it. And here is the problem: How do I do that?
Right now I use this code:
var dialogName="uniqueDialog" + counter++;
CKEDITOR.dialog.add(dialogName,function(editor) {
// Creating dialog here.
});
CKEDITOR.instances.editor.openDialog(dialogName);
This works, but having to add a uniquely named dialog, just to show it once and then newer use it again seems really really wrong. Also I fear this will keep using resources since the dialogs are newer removed(I could not find any remove method).
So my question is: Is there a better way to dynamical create and show a "one use" dialog?
Update:
If bootstrap is not allowed then maybe an addFrame version of the dialog is acceptable. This could then refer to a html file that can load from parameters.
NB: The plunkr only works, if you fork and edit it, otherwise it will give you a 404 for the template.
Here is a quick plunkr:
plunky
And here is the plugin in question:
CKEDITOR.plugins.add( 'insertVariable', {
requires: ['iframedialog'],
icons: 'insertvariable',
init: function( editor ) {
editor.addCommand( 'varDialog', new CKEDITOR.dialogCommand( 'varDialog' ) );
CKEDITOR.dialog.addIframe('varDialog','varDialog','sample.html?var='+item,500,400);
editor.ui.addButton( 'insertVariable', {
label: 'Insert Variable',
command: 'varDialog',
icon: this.path + '<insert gif>'
});
}
});
Obviously you are not creating dialogs anymore with different content, but you are referring to another piece of html, that can change. I've kept the bootstrap thing in there as well for reference.
I made one final edit, that will show the current contents. So I think that is roughly what you want. Check it out.
Previous Answer
If you are prepared to use bootstrap, then you can do no worse than check out their modal dialog, which can be just be shown and hidden at will.
http://getbootstrap.com/javascript/#modals
It's simple and cuts down any need to keep creating your own dialog. The dialog won't be one use type, but you will set the defaults as necessary. The varying content link is here:
http://getbootstrap.com/javascript/#modals-related-target
That would be the quickest way to get this going. It all depends on whether you want to use this framework. As CKEDITOR is already using JQuery it is an option worth considering.

How to ensure that ckeditor has focus when displayed inside of jquery-ui dialog widget

I have used CKEDITOR.appendTo( "my_div" , null , my_string ) to create an instance of ckeditor ... no problem.
however, the LINK button opens a non-interactive LINK dialog box.
So, is there some config setting that it supposed to be manually set to true, perhaps?
EDIT 1 ... I will explain what I meant by non-interactive LINK dialog box ...
When I click the ckeditor's LINK button (the one that looks like a chain-link), it opens a LINK dialog box which has a input field for me to enter a URL, plus a pulldown to choose protocol, plus a couple of other form elements.
However, none of these are use-able ... if I try to type in the url input field, nothing happens (the field will not accept focus); likewise the pulldowns do not open if I click them.
EDIT 2 ... added screenshot
When the modal option is set to true for the dialog, the dialog blocks any interaction with elements outside of it. (https://github.com/jquery/jquery-ui/blob/master/ui/dialog.js#L818)
You can override this to allow interaction with elements inside ckeditor.
Just include this somewhere after jquery ui and it should work:
orig_allowInteraction = $.ui.dialog.prototype._allowInteraction;
$.ui.dialog.prototype._allowInteraction = function(event) {
if ($(event.target).closest('.cke_dialog').length) {
return true;
}
return orig_allowInteraction.apply(this, arguments);
};
If you want to allow interaction with any element outside of the dialog, include this instead:
$.ui.dialog.prototype._allowInteraction = function(event) {
return true;
};
Add this:
$(document).on('focusin', function(e) {e.stopImmediatePropagation();});
I was using:
jquery-1.8.2
jquery-ui-1.10.3
ckeditor 4.3.1
then I replaced: jquery-ui-1.10.3 with: jquery-ui-1.9.0 and it seems to work as expected.
If reverting back to jquery-ui 1.9 is not good for you, also look at:
jquery-ui forum ... "can't edit fields of CKEditor in jQuery UI modal dialog"
jquery-ui bugs ... "Dialog: CKEditor in Modal Dialog is not editable"

The view area of ckEditor sometimes shows empty at the start

I am using the following directive to create a ckEditor view. There are other lines to the directive to save the data but these are not included as saving always works for me.
app.directive('ckEditor', [function () {
return {
require: '?ngModel',
link: function ($scope, elm, attr, ngModel) {
var ck = ck = CKEDITOR.replace(elm[0]);
ngModel.$render = function (value) {
ck.setData(ngModel.$modelValue);
setTimeout(function () {
ck.setData(ngModel.$modelValue);
}, 1000);
}; }
};
}])
The window appears but almost always the first time around it is empty. Then after clicking the [SOURCE] button to show the source and clicking it again the window is populated with data.
I'm very sure that the ck.setData works as I tried a ck.getData and then logged the output to the console. However it seems like ck.setData does not make the data visible at the start.
Is there some way to force the view window contents to appear?
You can call render on the model at any time and it will simply do whatever you've told it to do. In your case, calling ngModel.$render() will grab the $modelValue and pass it to ck.setData(). Angular will automatically call $render whenever it needs to during its digest cycle (i.e. whenever it notices that the model has been updated). However, I have noticed that there are times when Angular doesn't update properly, especially in instances where the $modelValue is set prior to the directive being compiled.
So, you can simply call ngModel.$render() when your modal object is set. The only problem with that is you have to have access to the ngModel object to do that, which you don't have in your controller. My suggestion would be to do the following:
In your controller:
$scope.editRow = function (row, entityType) {
$scope.modal.data = row;
$scope.modal.visible = true;
...
...
// trigger event after $scope.modal is set
$scope.$emit('modalObjectSet', $scope.modal); //passing $scope.modal is optional
}
In your directive:
ngModel.$render = function (value) {
ck.setData(ngModel.$modelValue);
};
scope.$on('modalObjectSet', function(e, modalData){
// force a call to render
ngModel.$render();
});
Its not a particularly clean solution, but it should allow you to call $render whenever you need to. I hope that helps.
UPDATE: (after your update)
I wasn't aware that your controllers were nested. This can get really icky in Angular, but I'll try to provide a few possible solutions (given that I'm not able to see all your code and project layout). Scope events (as noted here) are specific to the nesting of the scope and only emit events to child scopes. Because of that, I would suggest trying one of the three following solutions (listed in order of my personal preference):
1) Reorganize your code to have a cleaner layout (less nesting of controllers) so that your scopes are direct decendants (rather than sibling controllers).
2) I'm going to assume that 1) wasn't possible. Next I would try to use the $scope.$broadcast() function. The specs for that are listed here as well. The difference between $emit and $broadcast is that $emit only sends event to child $scopes, while $broadcast will send events to both parent and child scopes.
3) Forget using $scope events in angular and just use generic javascript events (using a framework such as jQuery or even just roll your own as in the example here)
There's a fairly simple answer to the question. I checked the DOM and found out the data was getting loaded in fact all of the time. However it was not displaying in the Chrome browser. So the problem is more of a display issue with ckEditor. Strange solution seems to be to do a resize of the ckEditor window which then makes the text visible.
This is a strange issue with ckeditor when your ckeditor is hidden by default. Trying to show the editor has a 30% chance of the editor being uneditable and the editor data is cleared. If you are trying to hide/show your editor, use a css trick like position:absolute;left-9999px; to hide the editor and just return it back by css. This way, the ckeditor is not being removed in the DOM but is just positioned elsewhere.
Use this java script code that is very simple and effective.Note editor1 is my textarea id
<script>
$(function () {
CKEDITOR.timestamp= new Date();
CKEDITOR.replace('editor1');
});
</script>
Second way In controller ,when your query is fetch data from database then use th
is code after .success(function().
$http.get(url).success(function(){
CKEDITOR.replace('editor1');
});
I know, that this thread is dead for a year, but I got the same problem and I found another (still ugly) solution to this problem:
instance.setData(html, function(){
instance.setData(html);
});

How to call the same function that the ToolBar editbutton does

So I have created a context box upon right click that has Add/Edit/Delete Rows. I also have a bunch of code launched before the Dialog is shown. My problem is that when I use the context menu it doesn't go through some of the code. I have tried to call on the functions directly but it doesn't format correctly.
I am mainly concerned with the edit button, here is the code I am using to bring up the edit Dialog
function editRow() {
var grid = jQuery("#<%= Jqgrid1.ClientID %>");
var rowKey = grid.getGridParam("selrow");
if (rowKey) {
// I have tried calling functions here and it still doesn't work
grid.editGridRow(rowKey, grid.editDialogOptions);
}
else {
alert("No rows are selected");
}
}
So if I use this to display the editform it isn't formatted correctly nor does it go through the functions all correctly.
I am using the ASP Webforms version of Jqgrid so I call the function by doing this
<cc1:JQGrid1 ID="Jqgrid1
//other attributes
ClientSideEvents-BeforeEditDialogShown="ChangeMonitor"
//Rest of code />
So this works just fine, and I'm trying to get the Edit button on the context menu to display correctly.
My thought was to use Jquery to trigger a click on the actual Edit button once someone used the context menu. I couldn't find an ID that would work however.
Is there an easy way to connect my context menu Edit button, with the actual Edit button in the toolbar?
Well I found a solution to my problem.
The id field of the button was edit_ct100_cpMainContent_Jqgrid1_top so I just triggered a click with this code.
$("td[id^=edit][id$=top]").trigger("click")
For some reason when I used the _ct100_cpMainContent_Jqgrid1 it wasn't working, but now it does. Hope this helps someone.

Firefox plugin that ask for some input at startup

I would like to know how to implement a dialog that show up when you first start Firefox to ask the user to enter some input. This input will be stored somewhere temporarily, and should be used later on by the plugin when required.
I have full understand of how to implement firefox plugin (this includes understanding of XUL and Javascript), so no need for full plugin example. The specific question is how to show a dialog when firefox start that ask for input, and how to store the input in a temporary storage.
Any help would be appreciated.
Add an event listener to your overlay.xul:
<window>
<script type="text/javascript">
var your_func = function (e) {
var pref = window.prompt ("Your name:","");
}
window.addEventListener ("load", your_func, false);
</script>
</window>
The your_func() will be called, whenever a new window (not a new tab) is loaded. If it should only be on start-up, you'll have to make an additional test. You find details here: developer.mozilla.org
For persistence you could store the found value as a preference: Preference Code Snippets. It would be useful then, to check in your_func, if such a preference exists, before opening the prompt.
Instead of a plain prompt, you could do the following:
window.open ("chrome://my-plugin/content/prompt.xul", "MyWindow", "chrome,modal,alwaysRaised,centerscreen");
The magic lies in the "modal" value in the third parameter.
Cheers,

Resources