Dispatching custom events - javascript

I have version 0.9.0.1 of the typescript compiler installed in my VS 2012 Update 3.
I want to dispatch a custom event but the ambient variable declared in lib.d.ts does not expose the expected constructor signatures.
When I use
var myEvent = new CustomEvent("my:event:type", { detail: { some: "data"} });
window.dispatchEvent(myEvent);
the type script compiler complains, because according to him, only
var myEvent = new CustomEvent();
is correct.
The latter is erroneous according to Chrome 27 and Aurora 24.02, due to "missing arguments"
MDN also lists the actually-correct-but-not-for-typescript constructor signatures.
My thinking was now to add the known-correct constructor signature to the ambient variable declaration, but without touching the shipped lib.d.ts file. Would this technically be possible? I could not come up with the correct syntax for it, and the language specification did not mention how to merge two such declarations.
Alternatively, I simply edited lib.d.ts, which after a restart of the IDE provided me with the updated signature. Nevertheless, I'd rather not tamper with 3rd-party files in such a way.
Last, is there some other mechanism I (sh|c)ould use to write type script code that dispatches a custom event?
(Update: Restarting the IDE reloads lib.d.ts correctly. Also, corrected made-up event type name)

The reason this isn't part of lib.d.ts is that Internet Explorer doesn't support this object in IE10.
If the CustomEvent definition was purely an interface, you would be able to extend it thus:
interface CustomEvent {
new(eventType: string, data: {});
}
But the CustomEvent constructor is actually defined in a variable declaration, which you can't extend:
declare var CustomEvent: {
prototype: CustomEvent;
new(): CustomEvent;
}
You can use this (nasty looking) work around to get hold of custom events while you wait for Internet Explorer / the TypeScript library to be updated.
class StandardsCustomEvent {
static get(eventType: string, data: {}) {
var customEvent = <any>CustomEvent;
var event = new customEvent(eventType, data);
return <CustomEvent> event;
}
}
var x = StandardsCustomEvent.get("myevent", { detail: { some: "data"} });
This fixes your compiler warnings - but won't make it work in IE10 or older - you would need to polyfill.

I ended up doing all of this:
Use the --nolib option on build
Add a local copy of the lib.d.ts
Patch in the expected constructor signatures
Add a polyfill for IE <= 11 as proposed by MDN

var event = <CustomEvent>(new (<any>CustomEvent)('specifiedEvent'))
Might be a solution. Nice looking and tricky.

Related

Equivalent of exportFunction or cloneInto for classes?

The title says it all. I'm trying to inject a class into the global object / default scope of affected webpages, from a WebExtension. (I have no objections to supporting Chrome; however, I'm tagging this question as Firefox-specific for now, since it seems that Chrome does not yet support this use-case at all.)
For functions, exportFunction() may be used to inject functions into the global object.
For Objects, cloneInto() may be used.
However, neither of these seemed to work for a class.
Here is a sample content_script that I've been using for troubleshooting:
console.log("Content script initializing...");
function patch_function(f, name, dest=window) {
exportFunction(f, dest, {defineAs: name});
console.log(`Patched function ${name} into window!`);
}
function patch_object(o, name, dest=window) {
window.wrappedJSObject[name] = cloneInto(o, dest, {cloneFunctions: true});
console.log(`Patched object ${name} into window!`);
}
let basicObject = {'hello': 'world'};
class MyClass {
constructor(x=7) {
this.x = x;
}
return_x() {
return this.x;
}
}
console.log("Patching...");
// This works PERFECTLY from the webpage...
patch_object(basicObject, 'basicObject');
// ...but neither of these works! Swap the comment to try out the other one
//patch_function(MyClass, 'MyClass');
patch_object(MyClass, 'MyClass');
console.log("Content script done!")
In particular, running a statement such as (new MyClass(3)).return_x() yields Permission denied to access property "return_x" no matter how it was injected!
How can I inject a custom class (which — crucially — might even call WebExtension-privileged code) into a webpage from a WebExtension? (Or, if not possible: what is the tracker # for this — I can't currently find it.)

How to create KeyboardEvent using Typescript in IE and PhantomJS

I am using Angular2 and have a method that takes a KeyboardEvent object. I would like to write tests for this method (using Karma and Jasmine if that matters).
I wrote some using the KeyboardEvent constructor which worked fine in Chrome, but fails in IE and PhantomJS. Is there a way to do it that will work in these browsers?
Method signature:
public onDateKeypress(evt: KeyboardEvent): boolean {
// ...
}
Current test example:
let ke = new KeyboardEvent("keypress", { "key": "" + i, });
onDateKeypress(ke);
This is what I ended up with, I'm open to better solutions.
let ke = <KeyboardEvent>{ "key": "" + i, };
onDateKeypress(ke);
Explanation:
The typescript code will be compiled down to Javascript and the Javascript doesn't care what "type" the data is, only that the object has the correct properties/methods on it.
In my case the onDateKeypress method only uses the "key" propertyof the evt param, so that's all I need to set on my test object.
Casting the annonymous object to Keyboard event is required so that the Typescript will compile without complaining, since the method expects an object of type KeyboardEvent.

Preserve prototypes in ADVANCED mode

I need to compile my code with closure compiler in ADVANCED mode. I also need to keep prototypes of my objects in my application because I'm looping on Javascript objects prototypes. Trying to get both results in some ReferenceError when starting the application.
When compiling with ADVANCED mode, some prototypes are removed and replaced by a function that is using an object parameter in order to recover "this" keyword. This is due to crossModuleCodeMotionNoStubMethods attribute of CompilerOptions.java.
Example of code before compilation :
function MyClass() = { // Some code }
MyClass.prototype.someFunc = function() { // Some code calling someOtherFunc };
MyClass.prototype.someOtherFunc = function(someParam) { // Some code };
Example of code after compilation :
function MyCompiledClass = { // Some code }
MyCompiledClass.prototype.someCompiledFunc = function() { // Some code calling someOtherFunc }
function someOtherCompiledFunc(that, someParam) = { // Some code }
I first tried to use #this and #preserve JSDoc tags to solve the problem, without success. Using #export is not a solution, because functions will then keep their original names.
I've found two options to solve my problem for now :
Refactor the code as seen here
Build a custom version of Closure Compiler as seen here
Option 1 will need to much modifications in my code and will make it less readable, if it's the only solution, I will have a go for this one.
Option 2 seems to be a nice workaround, but I've read that some changes on CompilationLevel.java may violate some core assumptions of the compiler. Can someone tell me if by modifying setCrossModuleMethodMotion from true to false, will it still respect all core assumptions of the compiler ?
I'm currently building a custom version of the compiler to check if the code is compiling properly, but even if the code is usable, I need to be sure it will be properly obfuscated.
Thank you !
The specific optimization pass you are referring to is DevirtualizePrototypeMethods. The best way to block the optimization would be to use the #nocollapse annotation. It will allow your method to be renamed but not allow it to be removed from the prototype.
I'm not 100% sure it will work for this case, but if it doesn't it should and you can file an issue to have that fixed: https://github.com/google/closure-compiler/issues
You can export constructors and prototype properties in the same way.
For example:
MyClass = function(name) {
this.myName = name;
};
MyClass.prototype.myMethod = function() {
alert(this.myName);
};
window['MyClass'] = MyClass; // <-- Constructor
MyClass.prototype['myMethod'] = MyClass.prototype.myMethod;
As in https://developers.google.com/closure/compiler/docs/api-tutorial3

how a "property" can be call like it's a method without using parenthesis

With the lib 'chai', I can do this:
expect(display).to.be.true;
if display is false, the test fail, if display is true, it work.
My issue is, how the lib chai can know that the ".true" property have-been used? There is no parenthesis and it's not even a method! What is the name of this syntaxe? How can I declare similar thing?
this lib have also a .false, and a .empty property, who can be used why the same syntaxe:
expect([]).to.be.empty;//OK
expect([1]).to.be.empty;//test fail
expect(false).to.be.false;//OK
expect(true).to.be.false;//test fail
Objects can have "getter" and "setter" properties. These invoke the function you provide upon access or update, respectively.
var o = {
get test() { console.log("foo!") }
}
o.test;
This is an ECMAScript 5 feature, and is widely available in modern implementations.

Object doesn't support this property or method in IE8: _normalizeServiceName

I'm upgrading my app from .NET 4.0 to 4.5, and upgrading Breeze.js from 1.4.17 to 1.5.4. Things seem to be working well in IE11 and other browsers, but in IE8 (or in IE11 in IE8 mode) I am getting an "Object doesn't support this property or method" error related to the _normalizeServiceName method in this section of code:
function updateWithConfig(obj, config) {
if (config) {
assertConfig(config)
.whereParam("serviceName").isOptional()
.whereParam("adapterName").isString().isOptional()
.whereParam("uriBuilderName").isString().isOptional()
.whereParam("hasServerMetadata").isBoolean().isOptional()
.whereParam("jsonResultsAdapter").isInstanceOf(JsonResultsAdapter).isOptional()
.whereParam("useJsonp").isBoolean().isOptional()
.applyAll(obj);
obj.serviceName = obj.serviceName && DataService._normalizeServiceName(obj.serviceName); // <<< Error here
obj.adapterInstance = obj.adapterName && __config.getAdapterInstance("dataService", obj.adapterName);
obj.uriBuilder = obj.uriBuilderName && __config.getAdapterInstance("uriBuilder", obj.uriBuilderName);
}
return obj;
}
I can see that the _normalizeServiceName method is defined right after updateWithConfig:
ctor._normalizeServiceName = function (serviceName) {
serviceName = serviceName.trim();
if (serviceName.substr(-1) !== "/") {
return serviceName + '/';
} else {
return serviceName;
}
};
If I trace through where DataService is defined, ctor does have the _normalizeServiceName method defined when it is returned, but by the time updateWithConfig is called it is missing from DataService.
The error occurs when I create a new EntityManager:
this.manager = new breeze.EntityManager(appRoot + "breeze/myapp");
The Breeze website seems to say that IE8 is still supported. I have the ES5 Shim/Sham scripts referenced in a conditional comment:
<!--[if lt IE 9]>
<script src="/myapp/js/respond.js"></script>
<script src="/myapp/js/es5-shim.js"></script>
<script src="/myapp/js/es5-sham.js"></script>
<script src="/myapp/js/json3.js"></script>
<![endif]-->
So, is IE8 still supported in Breeze? Did I miss something I need to update in my code when going from Breeze 1.4.x to 1.5.x (the only thing I changed was related to the Promise API seeming to change)? Or is this a bug in Breeze?
Perhaps not an answer to "does Breeze support IE8", but I thought I would document what I did to get things running again, in case it helps anyone else.
The first issue was that IE8 doesn't like the named constructor functions (if that's even the right terminology) introduced in this commit to breeze.js. To get around that, I added a replace step to my gulpfile.js to remove the function names:
gulp.task("breeze", function () {
var js = gulp
.src(src.bower + "breeze-client/build/breeze.debug.js")
.pipe(replace(/var ctor = function (\w+)/g, "var ctor = function "))
.pipe(rename("breeze.js"))
.pipe(gulp.dest(dest.js));
return merge(js);
});
Also, since I am using TypeScript, the current typings for Breeze define an IPromise interface (changed from Q.Promise in this commit, presumably to support Angular) that uses method names that IE8 doesn't like (catch, finally) and doesn't define the ES3-friendly aliases (fail, fin). I added my own definitions, and also defined the Q done() method:
declare module breeze.promises {
// IE8 (and other ES3 browsers?) don't like .catch and .finally
// Also define Q's .done method.
interface IPromise<T> {
done<U>(): IPromise<U>;
fail<U>(onRejected: (reason: any) => U): IPromise<U>;
fail<U>(onRejected: (reason: any) => IPromise<U>): IPromise<U>;
fin(finallyCallback: () => any): IPromise<T>;
}
}
Finally, I had updated es5-shim to v4.5.7 as part of my upgrade, which seems to break IE8 too, although I have no idea how/why at the moment. I have no idea what version I was using before (can't find a version number in the .js file) since I had manually copied it in a long time ago. I had to go all the way back to v4.1.7 to find a version that worked, so presumably it's something in v4.1.8 that breaks IE8.
With all that, things seem to be working in IE8 again!

Categories

Resources