Ho do I use CKEditor's focusManager class? - javascript

I'm currently writing a js script that changes some styles around in order to hide a fixed footer and position a header absolutely when using a touch device.
I've been able to successfully implement it with normal text input fields and textareas, but since CKEditor doesn't parse in the DOM as a textarea, I'm forced to use it's focusManager class in order to trigger the change when the user focuses on an instance of it in my site.
The problem is that I've never used CKEditor's API before and I'm having some problems using it's focusManager class after doing some research.
Below is my current script.
It works fine for textareas and text inputs, but not on CKEditor.
From what I understand, where you see "cke_1", that is the instance name of the editor, but it's not working.
Also, I have multiple instances of CKEditor throughout my site and it needs to work on all of them.
Any help would be greatly appreciated.
Thanks!
var focusManager = new CKEDITOR.focusManager(cke_1);
var editor = CKEDITOR.instances.cke_1;
$(document)
.on("focus", "input", function(e) {
$body.addClass('fix');
$('footer').hide();
})
.on("blur", "input", function(e) {
$body.removeClass('fix');
$('footer').show();
});
$(document).on("focus", "textarea", function(e){
$body.addClass('fix');
$('footer').hide();
})
.on("blur", "textarea", function(e){
$body.removeClass('fix');
$('footer').show();
});
$(document).on("focus", editor.focusManager, function(e){
$body.addClass('fix');
$('footer').hide();
})
.on("blur", editor.focusManager, function(e){
$body.removeClass('fix');
$('footer').show();
});

I got this working. Since I have multiple instances of ckeditor, I wrote a function that is called when an instance is created and the user is on a mobile device. This is how I did it:
function renderMobile(){
console.log("Mobile device detected");
// Set focus and blur listeners for all editors to be created.
CKEDITOR.on( 'instanceReady', function() {
var editor;
for(var i in CKEDITOR.instances) {
editor = CKEDITOR.instances[i];
}
var $body = CKEDITOR.document.getBody();
editor.on('focus', function() {
$body.addClass( 'fix' );
});
editor.on('blur', function() {
$body.removeClass( 'fix' );
});
});
}

You don't need to use focusManager class at all. Simply listen on editor#focus and editor#blur (JSFiddle):
// Set focus and blur listeners for all editors to be created.
CKEDITOR.on( 'instanceReady', function( evt ) {
var editor = evt.editor,
body = CKEDITOR.document.getBody();
editor.on( 'focus', function() {
// Use jQuery if you want.
body.addClass( 'fix' );
} );
editor.on( 'blur', function() {
// Use jQuery if you want.
body.removeClass( 'fix' );
} );
} );
CKEDITOR.replace( 'editor', {
plugins: 'wysiwygarea,sourcearea,basicstyles,toolbar',
on: {
// Focus and blur listeners can be set per-instance,
// if needed.
// focus: function() {},
// blur: function() {}
}
} );

Related

CKEditor4 EnhancedImage Plugin Image added event or: How to add custom class to image

I'm playing around with CKEditor4 and I want to add a custom class to all images that I inserted. Unfortunately it seems as the dataFilter rule which works for adding classes on inserted <p> does not work on <img>-images. Image plugin I use is enhancedImage ("image2") and I do not upload the image, I pick it from a fileBrowser instance.
My code:
$(function(){
var editor = CKEDITOR.replace( 'article-ckeditor', {
customConfig: '/vendor/ckeditor/ckeditor.config.js',
filebrowserImageBrowseUrl: '/laravel-filemanager?type=Images',
filebrowserImageUploadUrl: '',
filebrowserBrowseUrl: '',
filebrowserUploadUrl: '',
on: {
instanceReady: function (ev) {
ev.editor.dataProcessor.dataFilter.addRules( {
elements : {
img: function( el ) {
el.attributes['class'] = 'img-fluid';
}
}
});
}
}
});
});
For me it seems as the event is just not fired. But I cannot find anything about it in the documentation...
Thanks for your help!
If you just want to add a custom class for inserted images, you could easily listen to dialogHide event:
editor.on( 'dialogHide', function( evt ) {
var widget = evt.data.widget;
if ( widget && widget.name === 'image' && widget.element ) {
widget.element.addClass( 'img-fluid' );
}
} );

CKEDITOR : How do set `contentdom` after every SetData

I am creating a CKEditor plugin and I face some issues on this.
Model of My Plugin:
CKEDITOR.plugins.add("myplugin", {
//for lang,require,icon
init:function(a){
editor.on('contentDom', function () {
editor.document.on('key', function (evt) {
console.log("Key Pressed");
});
});
}
});
This is Working Fine.But,my problem is setData.
I am setting data when the user is clicking a file.
After setData the key event is not Working.
Is there any way to attach the listener to document after every setData() within plugin file?
And what are the other type of methods which are used in CKEditor like init ?
(OR)
Is there any way to setData() without affecting contentdom,key events?
Please add the listener to the editor and not to the document. That way it wil not get removed when document is removed:
editor.on( 'instanceReady', function( e ) {
editor.on( 'key', function( e ) {
console.log('test');
});
});
Please see: https://docs.ckeditor.com/ckeditor4/latest/api/CKEDITOR_editor.html#event-key
Finally, I found the Answer.
Refer the below Question
CKEDITOR.setData prevents attaching of events with .on function
And goes to the Document in CKEditor Docs Page.
#contentDomUnload
Finally My Code Like this,
editor.on('contentDom', function () {
var editable = editor.editable();
editable.attachListener(editable, 'keyup', function (evt) {
console.log('for key events');
});
editable.attachListener(editable, 'mousedown', function (evt) {
console.log('for click events');
});
});
And It worked very well.

IntroJS callback function upon skip or done?

I'm using introjs to build a tour of my application. I've searched in quite a few places online and through the documentation but can't seem to find anywhere a method of how to run a function upon skipping or clicking done on the tour. I'm trying to make it so a cookie is stored and the tour isn't run again until a user requests it or a new user comes to the site. Any help would be great, thanks!
$(function(){
var introguide = introJs();
introguide.setOptions({
showProgress: true,
steps: [
{ hidden }
]
});
introguide.start();
});
This code allows to store the tour info
var introguide = introJs();
window.addEventListener('load', function () {
var doneTour = localStorage.getItem('MyTour') === 'Completed';
if (doneTour) {
return;
}
else {
introguide.start()
introguide.oncomplete(function () {
localStorage.setItem('MyTour', 'Completed');
});
introguide.onexit(function () {
localStorage.setItem('MyTour', 'Completed');
});
}
});
Yes, there is a way but with some caveats.
First, after intro.js is loaded you will have a global called introJs with a property fn (standard jquery plug-in approach).
By setting a function using the oncomplete() function under introJS.fn, you can perform some actions when the user hits the 'Done' button.
Here's an example that just displays a console message:
introJs.fn.oncomplete(function() { console.log("Finished"); });
This works as expected. You can put this in a script anytime after the intro.js library is included.
The 'skip' button functionality, however, will only call the 'oncomplete' handler if you are on the last step. The author of the code views that as not complete and so doesn't run that code as you can see by this extract from the code:
skipTooltipButton.onclick = function() {
if (self._introItems.length - 1 == self._currentStep && typeof (self._introCompleteCallback) === 'function') {
self._introCompleteCallback.call(self);
}
_exitIntro.call(self, self._targetElement);
};
This basically says it must be at the last step for this to consider calling the complete callback.
Of course, you could fork the code and remove the restriction. I would suggest if you are going to do that, create a _introSkipCallback in a fashion similar to _introlCompleteCallback and invoke that unless on last step where you might invoke both functions if present.
Hope this helps.
Use oncomplete for functions after 'Done' is clicked
Use onexit for functions after 'Skip' is clicked
Bonus function: onchange will log each step, this can be used to call functions on a particular step
document.getElementById('startButton').onclick = function() {
// log each step
introJs().onchange(function(targetElement) {
console.log(this._currentStep)
if (this._currentStep === 3){
stepThreeFunc()
}
}).start()
// clicking 'Done'
.oncomplete(function(){
someFunc()
})
// clicking 'Skip'
.onexit(function(){
someOtherFunc()
});
};
I've noticed that onexit will be called when you click the done button (which is skip until the last step). onexit does not appear to bind this to the introjs object, so I was able to solve the issue of having onexit called when the walkthrough was completed like this:
// during setup
introJs.oncomplete(handleOnComplete);
introJs.onexit(() => handleOnExit(introJs));
function handleOnComplete() {
console.log(this._currentStep); // this is bound to the introJs object
}
function handleOnExit(introJs) {
const currentStep = introJs._currentStep;
if (currentStep < introJs._options.steps.length) {
doSomethingOnSkip();
}
};
I was going to add a comment, but my rep is too low. I didn't want to answer because I haven't actually tested this, but in version 2.5.0 (maybe previous versions too), there is the onexit function, which I believe is supposed to handle interrupts as well as clicking done at the end. Did you try that?
if ($(".introjs-skipbutton").is(":visible")) {
$( document ).on('click', '.introjs-skipbutton', function(event) {
event.stopPropagation();
event.stopImmediatePropagation();
self.exitTourguide();
});
}
I am using introJS tool in my application to give tour guide information of my application.
I used some functions for handling it dynamically. Here stepsData sending in an array format.
var intro = introJs();
intro.setOptions( {
'nextLabel': 'Next >',
'prevLabel': '< Back',
'tooltipPosition': 'right',
steps: this.stepsData,
showBullets: false,
showButtons: true,
exitOnOverlayClick: false,
keyboardNavigation: true,
} );
hope it will help for handling skip button action.
var self = this; intro.start().onbeforechange( function() { /* skip action*/
if ( $( ".introjs-skipbutton" ).is( ":visible" ) ) {
$( document ).on( 'click', '.introjs-skipbutton', function( event ) {
self.exitTourguide();
});
}
});
skip and done action handling.
/Done click action/
intro.oncomplete( function(){ if ( $( ".introjs-skipbutton" ).is( ":visible" ) ) { $( document ).on( 'click', '.introjs-skipbutton', function( event ) { event.stopPropagation(); event.stopImmediatePropagation(); self.exitTourguide(); }); } });
/* clicking 'Skip' action */ intro.onexit(function(){ if ( $( ".introjs-skipbutton" ).is( ":visible" ) ) { $( document ).on( 'click', '.introjs-skipbutton', function( event ) { event.stopPropagation(); event.stopImmediatePropagation(); self.exitTourguide(); }); } });

How to remove event listeners while destroying CKEditor

I have a ckeditor plugin like this:
CKEDITOR.plugins.add('testplugin', {
init: function (editor) {
editor.on('contentDom', function (e) {
var body = editor.document.getBody();
body.on('mouseup', function (e) {
alert('run!!!!');
});
});
});
});
It work perfect in CKEDITOR version 3 (iframe base)
But when i upgrade to CKEDITOR version 4 (lastest - contenteditable base),
all event fire multiple times when i destroy then re-init ckeditor.
(using CKEDITOR.instants.testEditor.destroy() and CKEDITOR.replace('testEditor',options);)
i use: removeAllListeners( ) to remove all event listeners to body but no change.
How can i complete destroy CKEDITOR 4 + All event listeners on it?
It seems that you use an inline instance, so editor.document is CKEDITOR.document. It means that every single instance shares the same var body = editor.document.getBody();.
To avoid duplicated events as a leftover of attached by "dead editors", you should either listen on editor#destroy and call event#removeListener on every single one, or use editable#attachListener which automates that job for you (jsFiddle):
editor.on( 'contentDom', function() {
var body = editor.document.getBody();
// This listener will be deactivated once editor dies.
editor.editable().attachListener( body, 'mouseup', function() {
console.log( 'run!!!!' );
} );
} );

Map click blocks document click

$(document).on('click', '.entity', function (e) {
...
})
I'm using the above to handle clicking of any "entity" anywhere on my site. It works great everywhere except on maps. I'm creating custom overlays that are each an element with the entity class. This is confirmed in the DOM. All CSS I apply works, however the click event does not seem to propagate up to the document level. I put the alert in for testing, and it does fire. With the click function blank, nothing happens. How can I keep it from blocking?
mymap.gmap3({
map:{
options:{
...
}
},
overlay:{
values: [
...
],
events:{
click: function () {
alert('clicked map entity');
},
...
Checked the following plugin:
/*!
* GMAP3 Plugin for JQuery
* Version : 5.1.1
* Date : 2013-05-25
Line 343: there is function OverlayView(map, opts, latLng, $div) with the following code:
...
this.onAdd = function() {
var panes = this.getPanes();
if (opts.pane in panes) {
$(panes[opts.pane]).append($div);
}
$.each("dblclick click mouseover mousemove mouseout mouseup mousedown".split(" "), function(i, name){
listeners.push(
google.maps.event.addDomListener($div[0], name, function(e) {
$.Event(e).stopPropagation();
google.maps.event.trigger(that, name, [e]);
that.draw();
})
);
});
listeners.push(
google.maps.event.addDomListener($div[0], "contextmenu", function(e) {
$.Event(e).stopPropagation();
google.maps.event.trigger(that, "rightclick", [e]);
that.draw();
})
);
}; ...
which calls $.Event(e).stopPropagation(); for click and some other events. That obviously stops propagation of events.
Commenting those two lines could resolve this specific issue. But, question is what are the possible consequences.

Categories