After GNOME Shell extension's monkey-patching, this.parent is unexpected - javascript

For the raise-activated GNOME Shell 3.16 extension I'm trying to monkey-patch the AppSwitcherPopup._finish method. Like the original, the patched version calls this.parent:
function _modifiedFinish(timestamp) {
// ...monkey-patched code...
this.parent(timestamp);
}
function enable() {
_originalFinish = AltTab.AppSwitcherPopup.prototype._finish;
AltTab.AppSwitcherPopup.prototype._finish = _modifiedFinish;
}
(full code)
But I get this stack trace in the console (from running gnome-shell --replace):
(gnome-shell:24452): Gjs-WARNING **: JS ERROR: TypeError: The method '_keyReleaseEvent' is not on the superclass
_parent#resource:///org/gnome/gjs/modules/lang.js:129
_modifiedFinish#/home/lastorset/.local/share/gnome-shell/extensions/Alt_Tab_Mod_Only_Raise_Activated_Window#dsboger.com.br/extension.js:34
SwitcherPopup<._keyReleaseEvent#resource:///org/gnome/shell/ui/switcherPopup.js:199
wrapper#resource:///org/gnome/gjs/modules/lang.js:169
In this case, SwitcherPopup._keyReleaseEvent is calling this, and this should be an instance of the AppSwitcherPopup subclass. I believe this.parent should be the same after patching—why is it now trying to call the caller? And for that matter, why doesn't that succeed?
I looked up the GJS code that generated this.parent, but I can't quite spot what's missing.

After digging a bit more I found a way to fix it. In the GJS class model, the parent function is actually looking for the method's owner's superclass in order to call the method of the same name. Looks like every GJS class has a wrapFunction helper that sets _owner. I used that to patch the function instead:
AltTab.AppSwitcherPopup.prototype._finish = AltTab.AppSwitcherPopup.wrapFunction('_finish', _modifiedFinish);

Related

What is the proper use of super.call(this)

I have a class function (not originally developed by me)...
function Project()
{
Project.super.call(this);
// this._some_var = null;
/* other initial vars */
...
return DefensiveObject.create(this); // see comment below
}
function initialize()
{
//*******Initialize Project************
}
...
return Project;
This function is part of a module called "Project.js" included by running node main.js.
return DefensiveObject.create(this); // not return Object.create(this)
DefensiveObject is a class to prevent objects from getting or
setting properties that are not explicitly setup in the class.
The main.js calls Project.initialize() which resides within my Project class.
My question is why would there be a need to call "Project.super.call(this);"?
in Javascript the reserved word super is used on ES6 classes for referencing the parent of a child class, it doesn't makes sense to use it referencing a function.
please read this article where the usage of super is explained
https://jordankasper.com/understanding-super-in-javascript/
also this medium article can help you
https://medium.com/beginners-guide-to-mobile-web-development/super-and-extends-in-javascript-es6-understanding-the-tough-parts-6120372d3420
The Project.super.call(this) line was a way to allow use of a dispose method in a "Disposable" class (not included in the original question) which allowed for a clean up of code from memory.

How to pass Angular4 typescript class method as callback to a Javascript function

I have an angular 4 service which uses an external JavaScript file, I need to pass an function as argument for one of the JavaScript function like JavascriptAPI.setMessageHandler(this.onMessage.bind(this))
JavascriptAPI.setStatusHandler(this.onStatus.bind(this)).
When i run this, I'm getting following error
polyfills.bundle.js:2610 Uncaught TypeError: JavascriptAPI.be is not a function
at WebSocket.e2.onopen (eval at webpackJsonp.../../../../script-loader/addScript.js.module.exports (scripts.bundle.js:28), :69:404)
at WebSocket.wrapFn [as __zone_symbol___onopen] (polyfills.bundle.js:3484)
at ZoneDelegate.webpackJsonp.../../../../zone.js/dist/zone.js.ZoneDelegate.invokeTask (polyfills.bundle.js:2839)
at Zone.webpackJsonp.../../../../zone.js/dist/zone.js.Zone.runTask (polyfills.bundle.js:2606)
at ZoneTask.webpackJsonp.../../../../zone.js/dist/zone.js.ZoneTask.invokeTask [as invoke] (polyfills.bundle.js:2913)
at invokeTask (polyfills.bundle.js:3779)
at WebSocket.globalZoneAwareCallback (polyfills.bundle.js:3797)
import { Injectable } from '#angular/core'
declare var JavascriptAPI: any;
#Injectable()
export class SampleService {
constructor() { }
onMessage(messages) {
console.log('onMessage', messages)
}
onStatus(messages) {
console.log('onStatus', messages)
}
onInit(token: string) {
JavascriptAPI.disconnect()
JavascriptAPI.setConnect(token)
const serverUrl = ['http://localhost:8080']
JavascriptAPI.setServers(serverUrl)
JavascriptAPI.setMessageHandler(this.onMessage.bind(this))
JavascriptAPI.setStatusHandler(this.onStatus.bind(this))
}
}
Thanks, Appreciate for your time.
So you've got this javascript functionality that lives outside the namespace for your typescript, 'Javascript API'.
Let's say the script for JavascriptAPI looks like this :
<script>
var JavascriptAPI = function(){
return {
disconnect: function() {},
setConnect: function(token),
// more functions
}
}
</script>
When you run that script, it will create a variable inside the global namespace which can be accessed via the window object. Try removing the 'script' tags and running it in your console.
While you're in the console, there are two ways you can access that variable now. You can type 'JavascriptAPI' and not surprisingly you will see the function returned. It's the second way that concerns us more. Try typing 'window.JavascriptAPI' in the console. You'll see the same value. That's the variable you want to point to in your component when you declare the variable.
Now this is just a hypothetical implementation of your JavascriptAPI function. But the point is you want to figure out where it lives in the namespace of your application once it's all running. So if it works like this example
declare var JavascriptAPI: any = window.JavascriptAPI;
Typescript might throw an error/warning saying that property JavascriptAPI doesn't exist on type window. There may be better ways to work around the error if it pops up, but here's one way you could do it.
var windowCopy = window as any;
declare var JavascriptAPI: any = windowCopy.JavascriptAPI;
In other words, you might want to use the second piece of code to not get warnings using typescript, but the concept is the same.
If you can't figure out how to reference the 'JavascriptAPI' based on looking at it's code, try putting a breakpoint in somewhere and looking at the window object to see where it lives there, and reference that.

How is modifying _init() affecting parent()?

I'm trying to update a Gnome-shell extension. In it, I override the _init method of an object, which I'm doing like this:
function newInitAppSwitcherPopup() {
this.parent();
...
}
AltTab.AppSwitcherPopup.prototype._init = newInitAppSwitcherPopup;
The new method fails with:
JS ERROR: TypeError: The method 'parent' cannot be called
What I find very surprising here is that the parent method actually exists (if I change the name I get a "not defined" error).
What I don't understand is that the original AppSwitcherPopup._init is still using this call to parent (https://git.gnome.org/browse/gnome-shell/tree/js/ui/altTab.js?h=gnome-3-16#n54).
This was working well under Gnome 3.12, but is broken for Gnome 3.16...I guess they changed something in their GObject or inheritance models?
i have a similar code working for my config widget
const MenuConfigWidget = new GObject.Class({
Name: 'SimpleMenu.Prefs.MenuConfigWidget',
GTypeName: 'SimpleMenuMenuConfigWidget',
Extends: Gtk.Grid,
_init: function(params) {
this.parent({... });
...
}
});
Do you extend the class our just monkey patch the _init function?

JSHint with ECMAScript6: method is not defined

I'm implementing a client-side application using ECMAScript6 and use JSHint for static code analysis. I often use the following pattern in my code:
class MyClass {
constructor() {
//This is how I would like to call myMethod
myMethod();
//This is how I should call myMethod to make JSHint analysis pass
this.myMethod();
}
myMethod(){
//Implementation
}
}
My primary language is Java so I expect that simply calling myMethod() should be ok. However without adding this to method call I'm getting "'myMethod' is not defined" warning from JSHint. My questions are:
Is it correct to make calls without this in such situation? (e.g. in PHP you always need to add $this-> to non-static method call)
If that's correct to make calls without this is there any way (any .jshintrc flag) to turn off this warning in JSHint?
No, this is and never was correct in JavaScript. Methods always need to be called on a receiver explicitly to make this work, and they need to be referred to using property access notation because methods are just functions on properties in javascript. They're not available as functions in the scope of your other methods. It's the same for properties, btw.
JsHint is right here, and there's no reason to turn that warning off. Even if that may possible, executing your program in spite of that would just make it not work.
Is it correct to make calls without this in such situation? (e.g. in
PHP you always need to add $this-> to non-static method call)
No, it is not. You always have to specify the receiver of the method.
If that's correct to make calls without this is there any way (any
.jshintrc flag) to turn off this warning in JSHint?
JSHint returns "'myMethod' is not defined" warning correctly as there is not function called myMethod in the scope of the constructor.
In the code you provided the identifier myMethod isn't defined, but the inherited property myMethod of instances of MyClass is defined.
If you define myMethod as a Function under a closure which isn't available elsewhere then you can access as it in the form you desire
var MyClass = (function () {
function myMethod() {
//Implementation
}
class MyClass {
constructor() {
myMethod();
}
}
return MyClass;
}());
I don't get to write much ES6 so I'm not sure if putting the function myMethod inside MyClass's definition is a SyntaxError
Please note however that this is required to reference your specific instance of MyClass, so you'll probably need to use it somewhere if you want MyMethod to act on the instance.
function myMethod(obj) {...}
// ...
myMethod(this);
If you read the MDN's description of class
JavaScript classes are introduced in ECMAScript 6 and are syntactical sugar over JavaScript's existing prototype-based inheritance. The class syntax is not introducing a new object-oriented inheritance model to JavaScript. JS classes provide a much simpler and clearer syntax to create objects and dealing with inheritance.
This is saying using class is just shorthand for the old way of doing it, not a new model, so it may be easier to think of what your current code would look like if written in ES5,
var MyClass = (function () {
function MyClass() {
this.constructor.apply(this, arguments);
}
MyClass.prototype = Object.create(null);
MyClass.prototype.constructor = function () {
myMethod(); // referenceError
this.myMethod(); // works
};
MyClass.prototype.myMethod = function () {
//Implementation
};
return MyClass;
}());

Jasmine-node - Creating a spy on a constructor called inside other function

I'm novice in jasmine and I need to write some unit tests for node.js app in this framework.
I have some problems, one of them is this described below:
var sampleFunction = function(){
var loader = new Loader(params);
// rest of logic here
}
I want to write unit test for sampleFunction. To do this I need to create spy on Loader constructor and check what this constructor gets as params and what kind of object is it returning.
Any ideas how to do that? I tried to create spy on Loader.prototype.constructor but it wasn't a solution to this problem.
OK, so normally in client-side JavaScript you would use the window object like so jasmine.spyOn(window, 'Loader')
In node, however, there is no window object and despite claims to the contrary global is not a substitute (unless you are in the REPL which runs in global scope).
function MyConstructor () {}
console.log(global.MyConstructor); --> undefined
console.log(this.MyConstructor); --> undefined
So, in node you need to attach your constructor to an object. So just do something like this
var Helpers = {
Loader: Loader
};
var constSpy = jasmine.spyOn(Helpers, 'Loader').andCallThrough();
sampleFunction();
expect(constSpy).toHaveBeenCalled();
The andCallThrough call is only necessary if you want your constructor to do something (often with constructor you do).
This is a little hacky, but it works and seems to be the only way to achieve this through jasmine's implementation within node.

Categories

Resources