Possible to extend JQueryEventObject in TypeScript - javascript

I've only just started with Typescript but I'm wondering if it is possible to extend the JQueryEventObject with a new method - I'd like to add a cancel method that sets the event to stop bubbling and propagating.
If my understanding is correct, there is no way to do this using TypeScript because the JQueryEventObject is merely an interface created to provide an intellisense of what is available on the event object provided to an event. However, I may be wrong and if someone has got a solution, then I'd appreciate someone sharing that with me.

You are correct that the typescript definition is an interface, however, you can extend it, and you can also extend the runtime to add the new function. There are two things that need to be done:
make typescript aware of the new function
make the new function available at runtime inside jQuery
Here is a code sample that does both:
// PatchJquery.ts
// Include the generated .js file for this, after jQuery, in some html file.
/// <reference path="typedef/jquery/jquery.d.ts" />
// Runtime patch: Assume jQuery is already defined in the global scope
// and patch its Event prototype.
jQuery.Event.prototype.cancel = function() {
console.log("Someone wants me to cancel this event!",this);
}
// Define the extended typescript interface
interface ExtJQueryEventObject extends JQueryEventObject {
cancel():void;
}
// Test
$(function() {
var body:JQuery = $("body");
body.on('click', (e:ExtJQueryEventObject) => {
e.cancel();
});
});
Note that if you wanted, say, an actual click event, you would need to define an extended interface for JQueryMouseEventObject. You would need to do this for all of the different JQuery event types. Alternatively, you could just modify jquery.d.ts to add your new function to BaseJQueryEventObject.
(BTW, jQuery has stopPropagation() and stopImmediatePropagation(), which might do what you want already)

Related

Forcing modules to have a specific function. eg implement an interface?

Simple in java, but how do I ensure the modules that are registering with my event bus for topic notifications have the required callback method on them?
All my modules are following the revealing module pattern and as such are defined like the following
namespace = (function() {
//Private stuff
return {
method1 : method1
}
})();
I just need to ensure the module has a notify method on it which takes a single argument. The module can be responsible for unmarshalling the payload data into the format it is expecting
Thanks
I really want to help but I'm not sure I understand your terminology. I'll answer based on my limited understanding and adjust as necessary
Sounds to me like you want to make sure the object returned has a property called notify on it, which is a function that accepts one function parameter only. If this is correct, there are two places you can ensure all modules meet your requirement.
Inside the Event Bus
If you wrote the event bus, you can write a few lines of code inside it to make sure that if a module doesn't come with the 'notify' property on it, the event bus will not subscribe the module to the event. Something like this:
if( typeof module.notify !== 'function' ){
// do error handling then ...
return false;
}
External Function for Registering Modules to Event Bus
If you didn't write the event bus, you can create an external function that is responsible for ensuring the standard. Think of it as a wrapper around #1.
function registerToEventBus( module, event ){
if( typeof module.notify !== 'function' ){
// do error handling then ...
return false;
}
// register event to event bus then ...
return true;
}
Personally I'd recommend using #2
That way you wouldn't need to know a thing about the event bus itself to implement this.
You get the added benefit of being able to keep the event bus strictly for events and not complicate it by adding the module format enforcement into it.
You will be able to take the event bus as is to another project, drop it in and edit your enforcer function to meet this project's specificiations with absolutely no risk of breaking the event bus accidentally.
I hope this helps. If there's any place I misunderstood you, let me know and I'll give it my best shot to fix.

Event handling with jQuery, AMD and require.js

I am very new to AMD and require.js and have been struggling with a little problem for almost over a day. I've tried different ways but not sure what is the correct way/right approach. I would appreciate some feedback from all the JavaScript gurus.
I am trying to implement an event handler for a text box that will listen for any input/changes. In the event handler, I would like to update a marker currently being shown on a map. So, I defined two modules - open for openlayers and one containing my custom code for displaying the map, updating markers etc.
The custom module looks like below:
define('mymodule', [ 'open-layers', 'jquery', 'openstreetmaps','t5/core/console' ], function(
openLayers, $,openStreetMaps ,console) {
var init = function() {
}
var listenForChange = function(clientId) {
clientId = clientId;
var textBox = $(document.getElementById(clientId));
console.debug(textBox);
$('#addressLineTwo').on('change paste keypress input', function() {
console.debug(textBox);
console.debug('OnChange');
console.debug($(this));
console.debug($(textBox).val());
openstreetmaps.clearMarkersAndShowAddress();
});
}
return {
init: init,
listenForChange: listenForChange
};
});
The problem is, when the event handler gets called as a result of input in the text field, the openstreetmaps variable in function defined as handler for "on" method is undefined.
They only way I could get around this was to change that line to something like this:
require(['openstreetmaps'], function(openstreetmaps) { openstreetmaps.clearMarkersAndShowAddress(newAddress);});
Also, if I use such a construct how do I manage variables and passing data between all those closures? It seems like that the inner closure cannot access variables from the outer closures or functions.
I would really appreciate your help and feedback.
The variables from your other modules are accessible from within the event handler, you just need to change the define function parameter of openStreetMaps to openstreetmaps. That's why the extra require call works; you just need to match up the variable naming with your required module.
For managing variables, just expose everything public by returning them from the module, and those public variables can access whatever is defined in the module which creates a closure. And each module only gets registered once so you can retain whatever state you want in each module.

setReadOnly causes error when called on instanceReady of CKEditor

I'm trying to set my CKEditor instance to be "readOnly" after the instance has fully loaded but I'm getting a Javascript error: Cannot call method 'setReadOnly' of null. When I dig into it, the error is coming from this line in the ckeditor.js, within the editor.setReadOnly method: this.editable().setReadOnly(a); That means that the editor exists, but the editable method/attribute (on the CKEditor instance) does not.
Below is my code, and I'll explain it a little. My app is a combination of GWT and Backbone. The CKEditor itself is created by the Backbone code but the parent element is in GWT so that's where I initiate the setEnabled action.
private native void setEnabledOnLoad(boolean enabled, String id) /*-{
CKEDITOR.on("instanceReady", function(evt) {
if(evt.editor.name === id) {
Namespace.trigger(Namespace.Events.SET_ENABLED, enabled);
}
});
}-*/;
setEnabled: function(enabled) {
this.editor.setReadOnly(!enabled);
if(enabled){
this.editor.focusManager.focus();
} else {
this.editor.focusManager.blur();
}
}
The Backbone class has a listener for Namespace.Events.SET_ENABLED that triggers setEnabled.
Is there another CKEditor event that I should listen for? There doesn't appear to be an instanceReady event on editable. What am I missing?
EDIT
this.editor is created in the Backbone class render function like this:
this.editor = CKEDITOR.replace(this.$(this.id)[0], config);
The reason I don't add the instanceReady listener right after it's created is because the function setEnabledOnLoad is called in GWT before the instance has been fully initialized. This is a result of having the code in two places. GWT has said "ok, create the instance" but Backbone hasn't finished by the time GWT goes to the next line of code and wants to set it enabled/disabled.
Two years later, but here is my solution. Maybe someone else will find it useful.
As stated above, the event is appearantly triggered before the editable() function is fully set up, and therefore one solution is to simply wait for it to finish before setting it to readonly. This may be an ugly way to do it, but it works.
//Delayed execution - ckeditor must be properly initialized before setting readonly
var retryCount = 0;
var delayedSetReadOnly = function () {
if (CKEDITOR.instances['bodyEditor'].editable() == undefined && retryCount++ < 10) {
setTimeout(delayedSetReadOnly, retryCount * 100); //Wait a while longer each iteration
} else {
CKEDITOR.instances['bodyEditor'].setReadOnly();
}
};
setTimeout(delayedSetReadOnly, 50);
You could try subscribing to instanceReady event this way:
CKEDITOR.instances.editor.on("instanceReady", onInstanceReadyHandler)
However, the editor instance must have been already created by then (inspect CKEDITOR.instances in the debugger).
I'm a bit confused about the difference between editable and editor. Could you show the fragments of your code where this.editor and this.editable get assigned?
[EDITED] I guess I see what's going on. CKEDITOR is a global object, you may think of it as of a class which holds all CKEDITOR instances. Trying to handle events with CKEDITOR.on isn't right, you need to do it on a specific instance (like I've shown above). I assume, "editor" is the ID of your parent element you want to attach a CKEDITOR instance to (please correct me if I'm wrong). I'm not familiar with Backbone, but usually it's done with replace:
var editorInstance = CKEDITOR.replace("editor", { on: {
instanceReady: function(ev) { alert("editor is ready!"); }}});
Here we attach a new instance of CKEDITOR to the editor parent element and subscribe to the instanceReady event at the same time. The returned object editorInstance should provide all the APIs you may need, including setReadOnly. You could also access it through the global CKEDITOR object using the parent element ID, i.e. CKEDITOR.instances.editor. On the other hand, editable is rather a service object available on editor. I can't think of any specific case where you might need to use it directly.
I apologize for never updating this with my solution. I needed to decouple the GWT function further from the CKEditor behavior. So, I added a function in GWT 'setEnabled' that is called from the parent object when it wants to update the enabled state of the CKEditor object.
public void setEnabled(boolean enabled) {
this.enabled = enabled;
toggleCKEditorEnabled(enabled);
}
Then changed the function referenced above 'setEnabledOnLoad' to be 'toggleCKEditorEnabled' which triggers the SET_ENABLED event with the enabled value.
Instead of attaching the listener to the specific instance of CKEditor, I added in to the Backbone MessageEntryView class that is the container of the CKEditor instance. In the initialize function of the MessageEntryView, I added this line
Namespace.on(Namespace.Events.SET_ENABLED, this.setEnabled);
This only works because I have one instance of CKEditor loaded on the screen at any given time. This problem and its solution stopped us from being able to add more CKEditor instances to the page at a time, which is something we discussed before moving on and replacing our whole client with Backbone.

Defining and triggering custom events on custom module in YUI3

This is how we can create a custom module in YUI3,
<script type="text/javascript">
YUI.add('my-module', function (Y) {
// Write your module code here, and make your module available on the Y
// object if desired.
Y.MyModule = {
sayHello: function () {
console.log('Hello!');
}
};
});
</script>
But now I would like to, on this module, define some custom events and later trigger them, I just couldn't find any information about this on the YUI3 official website.
How can we actually do this?
Custom events are actually pretty important throughout YUI. This documentation page describes them in detail: http://yuilibrary.com/yui/docs/event-custom/. Read this page and some of the examples in the sidebar.
The easiest and simplest way to fire a custom event is to fire it from the Y, as in Y.fire("myEvent"). However, if you want to fire an event from your object, you would need to give your object the EventTarget API and call this.fire("myEvent"). Most people do this by extending Y.Base, which includes Y.EventTarget. See http://yuilibrary.com/yui/docs/base/ -- if you extend Base, you get a fire() method, the ability to listen for events with on() or after(), plus lots of other goodies.

extend a javascript option to add functionality

I need to call "MyOtherFunction" when "MyFunction"(which creates an element) completes, without MyFunction knowing what MyOtherFunction is.
The reason I need this is for extension of a jquery powered fileupload User Control that is used in several places with different functionality. A specific page shows a header and file count for it, and when the upload completes, I need to modify the file count according to how many files are displayed(by created elements) I thought :
$(UserControl).on(MyFunction, UploadElem, MyOtherFunction);
but this route is not accomplishing anything. The most I can alter the User Control is add in a function call, but without effecting the original user control functionality.
I'm not sure if because MyFunction isn't an event and doesn't bubble up or if it just isn't possible to use a defined function as a parameter of .on() is the reason I cannot get this code to work. Any suggestions?
Easiest way I can think of, is duck punching respectively hooking that method:
var _oldMyFunction = MyFunction;
MyFunction = function() {
_oldMyFunction.apply( this, arguments );
MyOtherFunction();
};
I managed to solve my own issue, but the context is important for the answer:
// Using a Global JavaScript object I created:
GlobalNameSpace.ExtensionFunction = function(oParam1, oParam2, oParam3)
{
/// <summary>All parameters are optional</summary>
return; // For instances when it is not being overwritten, simply return
}
//In the Code for the user control:
GlobalNameSpace.UploadControl.UploadComplete(oSender, oArgs)
{
///<summary>Handles the Upload process</summary>
// process the upload
GlobalNameSpace.ExtensionFunction(oSender, oArgs);
}
//and finally in the code to extend the functionality
GlobalNameSpace.Page.Init
{
///<summary>Initializes the page</summary>
// redefine the extension function
GlobalNameSpace.ExtensionFunction = function(oSender, oArgs)
{
GlobalNameSpace.Page.Function(oSender, oArgs);
}
}
This allows me to extend anything I need it to without polluting my objects, and having something generic already existing to call on to make my changes. This solution solves my problem of needing a onCreate function for the elements I create to represent my uploaded items to trigger the header displaying the number of files. Very useful

Categories