Why can't I call function from inside chrome.downloads.onChanged? - javascript

I'm creating an extension in chrome for the first time (I'm not a web, or javascript developer). I'm adding onto a codebase that is in an older version of javascript that I've never used (Once I'm at a computer with that codebase, I'll tag which version it is, but I can't remember).
I have a class called DownloadManager, and inside of it I am calling chrome.downloads.onChanged, and within it, I call another function inside of the class, but it can't recognize the class (I think that's the issue).
// Class named DownloadManager
function DownloadManager(someData) {
this._myData = someData;
// function that does a thing, and tests run successfully
this.doAThing = function(someData) {
// Code in here that we assume works, and there's no issues.
}
if(chrome.downloads) {
chrome.downloads.onChanged.addListener(function(delta) {
// Error here
this.doAThing(delta.data);
}
}
}
The error I'm getting is on the this.doAThing(this._myData); line. The error is Error in event handler for downloads.onChanged: TypeError: Cannot read property 'doAThing' of null at <URL>.
I'm assuming it's a scoping issue, and this. doesn't mean anything there, and it can't access doAThing there. I'm certain that the argument taken in is of the same type as the function declared above.
I'll add more data when I'm back in that environment.

Inside your event handler for chrome.downloads.onChanged the this keyword now has a different context than this inside the DownloadManager. It might make sense that since you defined the event handler within the downloadManager that you could share the variable, but that just happens to be a coincidence of "where the code was defined vs where the code is invoked from".
You could probably get away with assigning this to a variable in the main scope:
function DownloadManager(someData) {
this.doAThing = function(someData) {
// Code in here that we assume works, and there's no issues.
}
window.myScope = this;
if(chrome.downloads) {
chrome.downloads.onChanged.addListener(function(delta) {
// Error here
window.myScope.doAThing(delta.data);
}
}
}

Related

Cross-origin object when everything is coming from localhost

I'm trying to record every time a user changes a text input on my web page. I'm using the following:
function formInit(socketObject) {
$('input:text').change( (eventObject) => {
console.log(document.domain);
console.log(JSON.stringify($(this)));
//... send stuff via socketObject
});
}
which I'm calling from $(document).ready(). I'm getting the following error on that fourth line, the one that logs $(this):
SecurityError: Permission denied to access property "toJSON" on cross-origin object
despite the fact that console.log(document.domain); returns localhost from both the script and from the master document it's being called from. The only thing that isn't being served up from my flask server on localhost are a couple of libraries (jquery, socketio, cleave) and possibly some components in a bokeh chart, which I'm pretty sure don't qualify as input:text, and certainly haven't been the thing I've been .change()ing in my debugging.
Can anyone suggest where I'm going wrong here, or where I should be looking? Thanks.
The error is because this is not the input:text element as you seem to be expecting, because you're using an arrow function, so lexical scope is not maintained. You either need to change the arrow function to an anonymous function:
function formInit(socketObject) {
$('input:text').change(function() {
// 'this' now refers to the element which raised the event
});
}
Alternatively keep the arrow function, but get a reference to the element from the event which is raised and provided as an argument to the handler function:
function formInit(socketObject) {
$('input:text').change(e => {
// 'e.target' now refers to the element which raised the event
});
}
Aside from the error, stringifying an entire jQuery object is a little odd. It's a better idea to just pull out only the relevant properties you need, something like this:
function formInit(socketObject) {
$('input:text').change((e) => {
console.log(document.domain);
console.log({
id: e.target.id,
value: e.target.value
// other properies here...
});
// send stuff via socketObject...
});
}

get block-scoped variable in class instance

I know (or I think I know) how scopes in JavaScript work so I suppose it can be impossible BUT: is there a way to get to the block (function) scoped variables in a class instance?
class Test {
run() {
const running = true;
}
}
const test = new Test();
test.run();
console.log(?) -> here I would like to get info that `running` variable was created
I can't create another function inside Test class. I've just got the instance.
FYI: the problem I try to solve is broader and has to do with 3rd-party library.
Unless you change the function run you can't access the property within. If you're working with 3rd-party code it's hard but a possible solution would be to overwrite the method by extending the class (you need to go very sure that the 3rd party code doesn't change that much):
class ConcreteTest extends Test {
run() {
// option A
this.running = true
// option B
return true;
// or use both A and B
}
}
The run function would be overwritten so it depends if this is an appropriate or working solution.
Doesn't your library provide any API or workaround to solve your issue?
I know how scopes in JavaScript work so I suppose it can be impossible
Yes indeed. That's what block scope means: the variable is only available to code in that very block, i.e. between the { … } braces of the run method.
test.run();
console.log(?) // here I would like to get info that `running` variable was created
No, that's not possible. Notice the the running variable doesn't even exist any longer after the .run() call ended.
The only way to make the true value available to the outside is to change the run method, e.g. by making it an object property not a local variable.
var app = { running: true };
var Test = { run: function() { return app.running; } };
var testInstance = new Test();
var testRunMethod = testInstance.run; testRunMethod();
app.running can be globally accessed switch. the run method can be a property of the test object and therefore can be copied and reused.

Javascript * is not a function (prototype function)

Coming from a C++ background, trying to work with an OO language that doesn't have explicit typing is a little more than a headache.
So I have dynamic elements for a webpage that are "controlled" by objects since there are tons of stuff I need to manage on each for it to work. The element is just the visual output of the data inside of the object itself, that's all I really need it for.
Except that I need the object to perform an internal function when it's clicked. That seems to be the biggest source of my headache thus far.
Javascript:
function onClick(file) //The external onClick function I use to try to get it to call from.
{
file.state = INUSE;
file.checkState();
}
function fileObject () { //The file object itself
this.element;
this.newElement();
//initialize stuff for the object
}
fileObject.prototype.newElement = function() { //creates a new element and sets its event listener
this.element.click(function() {onClick(this)});
}
fileObject.prototype.checkState = function() {/*does stuff*/} //apparently this is "not a function"
The error I get exactly is "file.checkState is not a function" from Firefox's console panel.
I'm still new to javascript, but after doing some debugging, I've come to find out that it's explicitly the onClick(this) function that is causing all of the errors. When used with something else, the onClick function works perfectly, but for some reason, the this keyword doesn't appear to actually be sending the reference to the fileObject since all checks show file being undefined when inside of the onClick scope.
Is there something fundamentally wrong about the way I'm trying to do this or am I just missing a step (or adding something that I don't need) that will help get this snippet working.
So you know, your initial problem isn't actually handling the action, but listening to it. click will trigger a synthetic click event, rather than liste for one.
You want ... .element.addEventListener("click", callback); that said, you face a second problem, immediately thereafter.
I will leave my example code as you've written it to not confuse the matter...
But when you see click( ) know that I mean subscribing with addEventListener, if element really does mean a browser DOM element. If it's not a standard browser element, and your own API, then ignore the previous portion, and carry on.
this is dynamically bound at the invocation time of the function (not at definition time).
The nearest function, scoped above, is your callback function that you are passing into .click( ... ).
Which is entirely different than the this which you mean outside of the callback.
Whatever is on the left-hand side of the dot is the this context for the duration of that particular invocation.
Needless to say, click() doesn't know enough to bind the this you mean, to the left-hand side of your callback.
The solution (or one of many) is to use lexical scoping and/or closure to retain the value of the object you mean.
// easy but messier
var fileObject = this;
... .click(function () { onClick(fileObject); });
// Cleaner with thunks:
function clickHandler (onClick, obj) {
return function () { onClick(obj); };
}
... .click(clickHandler(this));
Coming from c++ the way Javascript handles this will seem a little crazy, it looks like here you need to tell the function you've defined what this is - like so:
this.element.click(function() {onClick(this)}.bind(this));

Cannot Find JavaScript Namespace

I am trying to create namespaces in JavaScript as in the following script:
var hlAdmin = hlAdmin || {};
hlAdmin.editCompany = function (src) {
// function script
}
Then I call the function in HTML:
onclick="hlAdmin.editCompany(123)"
I get a reference error: Cannot find "editCompany".
Anyone know why?
Based on your comments I assume the following:
The equivalent script (and scoping is like):
<html><head>
</script>
var hlAdmin = hlAdmin || {};
hlAdmin.editCompany = function (src) {
// error in this script
}
</script>
</head></body>
<button onclick="hlAdmin.editCompany(123)">Caption</button>
</body></html>
In this example hlAdmin is indeed in the global scope (the root-scope of the host, called window in browsers).
If (in this example) you get reference error: Cannot find "editCompany", then one should look at other error-messages in your (browser's) error-log, because when there is a fatal error in the function for hlAdmin.editCompany, then that function will not be created (hence .editCompany becomes a property that points to undefined instead of a method that points to the function OR .editCompany doesn't even exist (depending on engine/error)).
To investigate if you indeed have a scoping-problem you could test this by: window['hlAdmin'] || (window['hlAdmin']={}); (or some equivalent variant). If that made the code work, then it seems you have some scoping-problem.
Hope these steps help someone in the future.
It's generally considered bad form to mix inline javascript and non-inline. The preferred way to do this would be to keep all the javascript in one place using an event handler:
window.hlAdmin = window.hlAdmin || {};
window.hlAdmin.editCompany = function (src) {
// function script
}
document.getElementById('yourElementId').onclick = function() {
hlAdmin.editCompany(123);
};
To more specifically address the issue: One thing that could cause this issue is if the hlAdmin object is not ending up in the global scope. You stated that this declaration is "at the top of the JavaScript file", but if it's in any kind of function (such as a function set to window.onload, or the jQuery $(function() { ... });) it would not end up in the global scope when declared as a var. A variable declared with var will only end up globally scoped if it's in the root scope, outside of any kind of function. If rather than using var hlAdmin you instead use window.hlAdmin, this will make sure that even if you're inside a document ready function or something similar, you're creating your hlAdmin in the global context, which will fix the problem if it is in fact an issue of scope.
I found the problem.
The browsers (at least Aurora and Chrome) are dropping the namespace in the onclick attribute. When you look at the browser html the namespace has just disappeared from the markup.

Access to public and private methods from event handler closure

I have a difficulty in understanding, how my current JavaScript code works. I've managed to solve a problem in accessing private object method from event handler closure, but I'd like to know why does it work so.
The code utilizes the well-known module/plugin metaphor:
(function(module, $, undefined)
{
function myPrivateCode(e){ /*...*/ }
module.myPublicCode = function(e) { /*...*/ }
module.init = function()
{
var that = this;
$('.clickable').click(function(e)
{
if($(e.target).hasClass('classX'))
{
that.myPublicCode(e.target); // requires 'that' to work
}
else
{
// that.
myPrivateCode(e.target); // will fail if 'that' uncommented
}
});
}
}(window.module = window.module || {}, jQuery ));
In the code I set a click handler which invokes either public or private method. It's perfectly conceivable that we need to pass an object reference into the event handler closure, which is done by that local variable. What is strange to me is that myPrivateCode does neither require that as a refernce, nor fails due to its "privacy". This makes me think that myPrivateCode accesses not the appropriate object, and works somehow differently to expected way. Could someone explain what happens? Certainly I'm missing something.
Both that and myPrivateCode are available to your event handler through a closure. In short, what's going on is that every variable and function you declare inside of another function has access to the outer scope.
myPublicCode, on the other hand, is not available through closures, because it's being assigned to your module object specifically. So the only way to call it is by using module.myPublicCode() (or that.myPublicCode() as you did – but you don't actually need that there, since module is also available).
Your call to myPrivateCode(e.target); is running in the context of the anonymous function that you pass as a handler to the click function.
For more information, read up on closures.
For a simpler example, try out this code:
var foo = function () {
var a = 1;
return function (b) {
return a+b;
}
};
var bar = foo();
bar(1); // 2
bar(1) will always always gives 2, because a = 1 was in scope when the function was created. In your case, a is your that and your handler is the closed function.
http://jsfiddle.net/Fh8d3/

Categories

Resources