CKEDITOR.editor.on not working with replaceAll - javascript

I'm trying to capture an event from a plugin:
$(document).ready(function( ) {
CKEDITOR.replaceAll( 'js-ckeditor' );
CKEDITOR.editor.on('simpleuploads.startUpload', function (ev) {
console.log(ev.data)
});
});
...but when I run it, I see the error TypeError: CKEDITOR.editor.on is not a function. What am I doing wrong here? According to the documentation editor should have a method "on" (which I can see when I inspect the object). By the way, both the textarea and plugin function well so replaceAll seems to work fine.

CKEDITOR.editor.on is a method of a working editor instance, i.e. returned by CKEDITOR.replace, while CKEDITOR.editor is just editor constructor.
Instead attach CKEDITOR.instanceReady event before CKEDITOR.replaceAll and use event data to attach more events to particular editor instances:
CKEDITOR.on( 'instanceReady', function( evt ) {
evt.editor.on( 'simpleuploads.startUpload', function( evt ) {
...
} );
} );
CKEDITOR.replaceAll( 'js-ckeditor' );

Related

How to trigger an event with a custom event (specifically a custom dataTransfer property)?

I'm currently attempting to test some code that uses drag-and-drop. I found some other questions that were kinda related to this, but they were way too specific to help me, or not related enough.
This being a test, I'm struggling on trying to automatically execute code inside a .on('drop',function(e){....} event. The main issue is not that I can't run the code inside, but it's that I can't transfer the dataTransfer property, and I can't seem to fake it because it's read-only. Is there anyway to fake the dataTransfer property or otherwise get around it?
I came up with this JSFiddle that serves as a template of what I'm trying to do: https://jsfiddle.net/gnq50hsp/53/
Essentially if you are able to explain to me (if this is at all possible) how I can possibly fake the dataTransfer property, I should be all set.
Side notes:
I'm totally open to other ways of somehow getting inside that code, like for example, maybe its possible to trigger the event and pass in a fake event object with a fake dataTransfer object.
To see the drag-drop behavior, change the JavaScript load type from no-wrap head to on-Load, then you should see what I'm trying to simulate.
Important to note that I cannot modify any of the code inside the event handlers, only inside the outside function
Using Karma/Jasmine so use of those tools are also possible like spies
Also, I'm using Chrome.
Thanks in advance, and let me know for any questions/clarifications!
You should be able to override pretty much everything you want using Object.defineProperty. Depending on what you want to test it can be very simple or very complex. Faking the dataTransfer can be a bit tricky, since there's a lot of restrictions and behaviors linked to it, but if you simply want to test the drop function, it's fairly easy.
Here's a way, this should give you some ideas as to how to fake some events and data:
//Event stuff
var target = $('#target');
var test = $('#test');
test.on('dragstart', function(e) {
e.originalEvent.dataTransfer.setData("text/plain", "test");
});
target.on('dragover', function(e) {
//e.dataTransfer.setData('test');
e.preventDefault();
e.stopPropagation();
});
target.on('dragenter', function(e) {
e.preventDefault();
e.stopPropagation();
});
//What I want to simulate:
target.on('drop', function(e) {
console.log(e)
//Issue is that I can't properly override the dataTransfer property, since its read-only
document.getElementById('dataTransferDisplay').innerHTML = e.originalEvent.dataTransfer.getData("text");
});
function simulateDrop() {
// You'll need the original event
var fakeOriginalEvent = new DragEvent('drop');
// Using defineProperty you can override dataTransfer property.
// The original property works with a getter and a setter,
// so assigning it won't work. You need Object.defineProperty.
Object.defineProperty(fakeOriginalEvent.constructor.prototype, 'dataTransfer', {
value: {}
});
// Once dataTransfer is overridden, you can define getData.
fakeOriginalEvent.dataTransfer.getData = function() {
return 'test'
};
// TO have the same behavior, you need a jquery Event with an original event
var fakeJqueryEvent = $.Event('drop', {
originalEvent: fakeOriginalEvent
});
target.trigger(fakeJqueryEvent)
}
https://jsfiddle.net/0tbp4wmk/1/
As per jsfiddel link you want to achieve drag and drop feature. jQuery Draggable UI already provides this feature why you can not use that?
For create custom event on your way you have to follow two alternative ways
$('your selector').on( "myCustomEvent", {
foo: "bar"
}, function( event, arg1, arg2 ) {
console.log( event.data.foo ); // "bar"
console.log( arg1 ); // "bim"
console.log( arg2 ); // "baz"
});
$( document ).trigger( "myCustomEvent", [ "bim", "baz" ] );
On above example
In the world of custom events, there are two important jQuery methods: .on() and .trigger(). In the Events chapter, we saw how to use these methods for working with user events; for this chapter, it's important to remember two things:
.on() method takes an event type and an event handling function as arguments. Optionally, it can also receive event-related data as its second argument, pushing the event handling function to the third argument. Any data that is passed will be available to the event handling function in the data property of the event object. The event handling function always receives the event object as its first argument.
.trigger() method takes an event type as its argument. Optionally, it can also take an array of values. These values will be passed to the event handling function as arguments after the event object.
Here is an example of the usage of .on() and .trigger() that uses custom data in both cases:
OR
jQuery.event.special.multiclick = {
delegateType: "click",
bindType: "click",
handle: function( event ) {
var handleObj = event.handleObj;
var targetData = jQuery.data( event.target );
var ret = null;
// If a multiple of the click count, run the handler
targetData.clicks = ( targetData.clicks || 0 ) + 1;
if ( targetData.clicks % event.data.clicks === 0 ) {
event.type = handleObj.origType;
ret = handleObj.handler.apply( this, arguments );
event.type = handleObj.type;
return ret;
}
}
};
// Sample usage
$( "p" ).on( "multiclick", {
clicks: 3
}, function( event ) {
alert( "clicked 3 times" );
});
On above example
This multiclick special event maps itself into a standard click event, but uses a handle hook so that it can monitor the event and only deliver it when the user clicks on the element a multiple of the number of times specified during event binding.
The hook stores the current click count in the data object, so multiclick handlers on different elements don't interfere with each other. It changes the event type to the original multiclick type before calling the handler and restores it to the mapped "click" type before returning:

CKEditor checkDirty() on source editing area

Whenever one or more of my CKEditor WYSIWYG instances is changed, I give the corresponding textarea the attribute data-dirty="1", so my application knows it has changed.
I'm using the following snippet for this:
$('textarea.wysiwyg').ckeditor(ckeditor_config, function () {
var call_once = false;
this.on('change', function () {
if (this.checkDirty()) {
if (!call_once) {
$(this.element).attr('data-dirty', 1);
$('#edit_form').change();
}
call_once = true;
}
});
});
This works nicely, unless the user only edits the HTML via the Source Button. In such cases, checkDirty() does not seem to get fired.
Does anyone know how I can get this functionality working on both views of the editor? I am using the latest version of CKEditor (CKEditor 4.5.7), full edition, so the plugins are the latest versions aswel.
Thanks in advance.
As the documentation for the change event states:
Please note that the change event is only fired in the wysiwyg mode. In order to implement similar functionality in the source mode, you can listen for example to the key event or the native input event (not supported by Internet Explorer 8).
editor.on( 'mode', function() {
if ( this.mode == 'source' ) {
var editable = editor.editable();
editable.attachListener( editable, 'input', function() {
// Handle changes made in the source mode.
} );
}
} );
If you are using jQuery adapter and multiple editor instances in one page, you can try below:
$( '#editor1' ).ckeditor( function(){ this.on( 'mode',
function( evt ) {
if ( this.mode == 'source' ) {
var editable = this.editable();
// Get the textarea
var element = $(this.element);
editable.attachListener( editable, 'input', function( ev ) {
// Handle changes made in the source mode.
console.log( ev );
// Set an attribute on the textarea for handling
element.attr('data-dirty',1);
} );
}
});
});
The above is the callback function will be executed on a single editor instance but if you specify selector covering multiple editor instances e.g. .myeditor then listener will be attached to every editor instance created by this method.

jQuery widget _on delegating Element

I am using the Widget factory from jQuery UI into a existing production environment.
This environment uses a binded event called sm2.validateNext which triggers before the page changes. However when trying to bind to this event using _on, it would try to split this event name and delegate it. (Which i believe is correct, bot not the expected functionality for me).
Code (inside the $.widget):
this._on(true, document, {
"sm2.validateNext": function () { ... },
});
And debugging this code, it gets delegated (not my expected behaviour) (from jquery-ui.widget.js)
var match = event.match( /^(\w+)\s*(.*)$/ ),
eventName = match[1] + instance.eventNamespace,
selector = match[2];
if ( selector ) {
delegateElement.delegate( selector, eventName, handlerProxy );
} else {
element.bind( eventName, handlerProxy );
}
Because of the . (dot) it gets a match and selector becomes valid delegating the event. However I need to get binded with its handlerProxy.
Is there a way to avoid get "trapped" into that match? I tried to escape sm2\.validateNext without result.
Note: $(document).on("sm2.validateNext"...) Doesnt work for me as I need the instance to be at the widget object.
Thanks for your help!
Found my solution by using $.proxy
Instead of:
this._on(true, document, {
"sm2.validateNext": function () { ... },
});
I used:
$(document).on("sm2.validateNext", $.proxy(function () { ... }, this));
And now it works just as I expected.

Event doesn't fire

I have these bits of code that are supposed to bug two functions and fire events for them. For some reason, however, the event does not seem to trigger. They log to the console as expected, but the event never gets fired.
//Show backstage event
(function( $, oldRem ){
backstage.show = function(){
console.log("yup, I'm the right one");
var resp = oldRem.apply( this, arguments );
$("#backstageArea").trigger("showBackstage");
return(resp);
};
})( jQuery, backstage.show );
//Hide backstage event
(function( $, oldRem ){
backstage.hide = function(){
console.log("And so am I.");
var resp = oldRem.apply( this, arguments );
if(anim && config.chkAnimate) {setTimeout( 'jQuery("#backstageArea").trigger("hideBackstage")', config.animDuration);}
else {$("#backstageArea").trigger("hideBackstage");}
return(resp);
};
})( jQuery, backstage.hide );
//Resize never logs its console message.
jQuery.bind("hideBackstage", function(){topbar.resize();});
jQuery.bind("showBackstage", function(){topbar.resize();});
You need to specify what element(s) to bind to, so try:
jQuery("#backstageArea").bind(...
instead of
jQuery.bind(...
(And if you're using jQuery version 1.7+ you may like to switch to the .on() method - will have the same effect for this purpose, but is the recommended way of doing things from v1.7 on.)

In javascript, how can I wrapper an onclick callback?

I want to decorate or wrapper a specific onclick callback such that my code is executed after the original installed callback is called.
I started with this in order to do it in a semi-python'ic way using function generator with closure:
var original_onclick = node.onclick;
function wrapper_onclick() {
original_onclick.apply(this,arguments);
my_code();
}
node.onclick = wrapper_onclick;
However, when I use the above code, the original callbacks aren't working properly. Am I passing in the wrong first parameter (context parameter?) this. Do I need to tweak the arguments? Is this already in list arguments?
This is exactly why lots of people worked to find a proper solution for cross browser event listening. Some browsers don't fire the function in the right context, some browsers don't pass the event argument and so on. It's a lot easier to use a framework, but if you insist on using no frameworks...
First if you have really horrible code like:
<a href="javascript:someFunction( this , evaledVariable );otherFn( ohDearVariable )">
Then sorry, no one can help you.
Otherwise...
In any case, you can't attach events directly to a node by simply setting the .onclick attribute and expect it to behave equally cross browser or even work properly in the browser you're developing with. You must use both addEventListener and attachEvent methods.
The most simplest way of forking for cross browser events is:
var addListener = function( node , event , listener ) {
if ( node.addEventListener ) {
node.addEventListener( event , listener , false );
} else if (node.attachEvent ) {
node.attachEvent( "on" + event , listener );
}
}
Same for removing listeners:
var removeListener = function( node , event , listener ) {
if ( node.addEventListener ) {
node.removeEventListener( event , listener , false );
} else if (node.attachEvent ) {
node.detachEvent( "on" + event , listener );
}
}
And use it thus:
var nodeListener = function( e ) {
alert(e.target);
// run once for demo
removeListener( node , "click" , nodeListener );
}
// notice we omit the "on" part.
addListener( node , "click" , nodeListener );
So if you want to wrap listeners appended to node the WRONG onclick way:
var wrongOnClick = node.onclick;
var listener = function( e ) {
before();
wrongOnClick.call( this , e );
after();
};
addListener( node , "click" , listener );
This will serve most modern browsers including IE, though actually a lot more optimalisation and memory leak prevention code is needed for a good implementation, but instead of writing this code yourself or having others do it for you, use a framework/tookit! Because by the time you have a good implementation running that works cross browser, you will have more code bloat then when you actually used a framework...
** Addendum **
As a hack, I still suggest to use the addEventListener method and keep references to your listeners.
var aListener = function( e ) { }
node.addEventListener( "click" , aListener , false );
var wrappedListener = function ( e ) {
before( e );
aListener( e );
after( e );
}
node.removeEventListener( "click" , aListener , false );
node.addEventListener( "click" , wrappedListener , false );
This obviously only works with your listeners, if you want it to wrap existing listeners in a page (listeners from others), then there's simply no way of knowing how people appended their listeners to nodes. If they used addEventListener/attachEvent libraries or the dirty example in the beginning of this post, you simply cannot reliably wrap them without manual coding and looking up the references or manually exposing them.
Based on help from Matt and BGerrissen, the following code works great for chrome:
var original_onclick = node.onclick;
function wrapper_onclick(e ) {
var ret = original_onclick(e);
remove_story_onclicks();
return ret;
}
node.onclick = wrapper_onclick;
Still not exactly sure how to do a bulletproof wrapper like the following in python:
def wrappee(foo,bar,baz):
print foo,bar,baz
def wrapper(*args,**kwargs):
print 'before'
wrappee(*args,**kwargs)
print 'after'
wrapper('a','b','c')
which prints
before
a b c
after

Categories

Resources