Can't find variable - PhantomJS - javascript

I post here after many hours of fruitless searching. PhantomJS does not allow me to use a variable as in the code below, with the error message when running my script "Can not find variable".
Do you have any idea where can be my problem?
page.open(myurl, function (status) {
if (status == 'success') {
page.includeJs("http://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js", function() {
elem = page.evaluate(function () {
/* Select one element with jQuery */
myElem = $('body');
return myElem;
})
var elemHtml = page.evaluate(function() { return $(elem).html(); });
console.log(elemHtml);
})
phantom.exit();
}
})
Thanks =)

There is an important piece of information in the Quick Start tutorial (in its Code Evaluation section):
To evaluate JavaScript or CoffeeScript code in the context of the web page, use evaluate() function. The execution is "sandboxed", there is no way for the code to access any JavaScript objects and variables outside its own page context. An object can be returned from evaluate(), however it is limited to simple objects and can't contain functions or closures.
The problem with your code is thus twofold:
Variable elem is initialized outside the web page context, it's not reachable from the second evaluate.
You return a non-simple object, i.e. a DOM element.
This is an easy problem to solve, mainly by properly designing the code to fit the actual "jailed" execution model. Please carefully read all relevant documentation and explore tons of included examples.

Related

Google Scripts/Basic JavaScript - Issue fixed by debugger

I'm working on a Google Scripts add on for Google Sheets, but I'm trying to get the script working before I actually set it up on the sheet. The code below works fine if I set a breakpoint somewhere in the extractNumbers function. If I just execute the code without breakpoints, I get an error:
TypeError: Cannot call method "replace" of undefined. (line 36, file "")
Here's the code:
var myVar = phoneCheck("a1","a2","o1","o2");
Logger.log(myVar);
function phoneCheck(newCell,newHome,oldCell,oldHome) {
Logger.clear();
var newCell = extractNumbers(newCell);
var oldCell = extractNumbers(oldCell);
var newHome = extractNumbers(newHome);
var oldHome = extractNumbers(oldHome);
if (newCell === oldCell) {
return newCell;
exit;
} else if (newCell === oldHome && newHome === oldCell) {
return oldCell;
exit;
}
if (newCell === '' && oldCell !== '' ) {
return oldCell;
exit;
}
if (newCell !== oldCell && newCell !== oldHome) {
return newCell;
exit;
}
return "No value found";
exit;
}
function extractNumbers(input) {
Logger.log(input);
var str = input;
return str.replace( /\D+/g, '');
}
Now I realize my if/then logic is more than a bit inelegant, but for my purposes, quick and dirty is fine. I just need it to run.
ALSO, I have read of other novice JavaScript programmers having similar issues related to the sequence of code execution. If someone would like to link to a concise source aimed at a non-advanced audience, that would be great too. Thanks!
EDIT: I put my code into a new fiddle and it works fine, but it continues to fail in Google Scripts editor unless running in debug mode with a breakpoint. The problem seems to be that the function parameters aren't available to the function unless there is a breakpoint. Anyone have access to Google Scripts that can try my updated code from https://jsfiddle.net/hrzqg64L/ ?
None of the suggestions got to the root of your problem - and neither did your answer, although you've avoided the problem by putting an enclosure around everything.
There's no AJAX, no asynchronous behavior - it's simpler than that. "Shadowing of parameters" is likewise a red herring. Bad coding practice, yes - but not a factor here.
If someone would like to link to a concise source aimed at a non-advanced audience, that would be great too.
Sorry - no such thing. I can explain what's going on, but can't guarantee it will be accessible to novices.
The exception
Let's just clarify what causes the exception, or thrown error, that you've observed.
As written, extractNumbers() will throw an exception if it has a null parameter (or any non-string parameter) passed to it. If you choose to extractNumbers() then hit "run", you'll get:
TypeError: Cannot call method "replace" of undefined. (line 36, file "")
That is telling you that on line 36, which is return str.replace( /\D+/g, '');, the variable str contains an object that is undefined (...and has no replace() method).
For bullet-proof code, you would check your parameter(s) to ensure they are valid, and handle them appropriately. Sometimes that would be with a valid default, and other times you might return an error or throw an exception that is more explicit about the parameter problems.
Running code in Google's debugger
The only way to run code in Google's Debugger is to select a function, then choose "run" or "debug". Assuming you posted all your code, you had just two functions to choose from:
phoneCheck()
extractNumbers()
Whenever Google Apps Script runs any part of a script, the entire script is loaded and scanned to find all symbols & check syntax. The scope of all symbols is noted as well, and so are any dependencies between functions and global symbols (symbols outside of any closure, or block of code).
That takes some time. To speed things up when asked to execute a specific function, the global symbols are only evaluated if they are a dependency for the requested function or the functions it may call. There is another condition that will trigger evaluation of global symbols, and that is if there is a possibility that the debugger may need to stop and display values.
When this happens, any code that is outside a closure (outside a function, for example) will be executed. This is what you observed when you set breakpoints.
Why did it work when breakpoints were set?
As explained, just having a breakpoint set triggers evaluation of global symbols.
You start this script with a few lines of code that are not in any closure:
var myVar = phoneCheck("a1","a2","o1","o2");
Logger.log(myVar);
It is that code which makes the only proper invocation of phoneCheck() with parameters. Because myVar is evaluated, phoneCheck() gets called with parameters, and in turn calls extractNumbers() with a defined parameter.
Unfortunately, because of the way the debugger works, you cannot choose to run that code yourself. You need to rely on these side-effect behaviors.
How to fix this?
Simple. Don't rely on global code to invoke functions under test. Instead, write an explicit test function, and call that.
function test_phoneCheck() {
var myVar = phoneCheck("a1","a2","o1","o2");
Logger.log(myVar);
}
Finally found the issue, but I don't fully understand it.
This question got me thinking about scope and how it might be different in the Google Script environment. I figured a simple workaround would be to enclose the entire script in its own void function, and it worked! Also, I simplified the script quite a bit with an array:
function init () {
var numberArray = ["a3", "a2", "o3", "o10"];
var myVar = phoneCheck(numberArray);
Logger.log(myVar);
function phoneCheck(myArray) {
var phoneString = '';
Logger.clear();
var arrayLength = myArray.length;
for (i = 0; i < arrayLength; i++) {
phoneString += myArray[i].replace(/\D+/g, '');
}
return phoneString;
}
}
Also, I realize the functionality of this script is different than the original, but I was really just trying to solve this problem. Now that I have, I can finish the script properly.
Thanks for all the suggestions, everyone! I learned a lot of good things, even though they turned out not to be the answer.

This object constructor is preventing my script from running

I'm working on a project (creating a browser based check list). One of my goals has been to write every piece by hand without a library like jquery or a mysql database.
Currently I'm trying to create on object for managing tasks. I'm not finished the primary function, but everything is closed, and I don't detect any errors. Furthermore, I'm haven't iterated it or called it's functions yet, so there's nothing to reference it yet. When I comment it out, the script runs normally.
I've included the xml request links up above and tested them successfully in a separate portion of the script.
I'm testing in firefox.
I'm writing this in SciTE
Here's the code:
function Task(name,node,childNode,divClass,content,onclick)
{
function retrieveTask(node,childNode)
{
var taskArray = [];
taskArray.push(xmlDoc.getElementsByTagName(name)[node].childNodes[childNode].nodeValue;)
taskArray.push(xmlDoc.getElementsByTagName(description)[node].childNodes[childNode].nodeValue;)
taskArray.push(xmlDoc.getElementsByTagName(complete)[node].childNodes[childNode].nodeValue;)
return taskArray;
}
function displayTask(name,content)
{
var task = retrieveTask(node,childNode);
var clickDiv = "";
formatDiv(name,"task",task[1],clickDiv);
task[2] === true ? formatDiv(name+1,"incompleteBox"," ",clickDiv) : formatDiv(name+1,"completeBox","O",clickDiv);
}
}
If anyone could give me some insight or tips that would be awesome. This isn't homework, it's a hobby, so it's a self teaching process.
...childNodes[childNode].nodeValue;)
should be );

javascript eval intrinsic restrictions?

I have a situation in which I have to use eval in Javascript. Yes, I know it is evil, but in this case it is required, because two requisites:
The Javascript code is big.
It has to be evaluated (and downloaded) only in a few situations, so I don't want the script to be downloaded always, but on demand.
With these requisites, I have written an XMLHttpRequest to request the code (600+K), and I want to eval that code to take place immediately. As I'll show later, the script contains just data to be inserted in two variables. The problem is that evaluating this javascript fails always with syntax error. If I put the code verbatim (without eval), the code executes fine. This is the minimal example. I have a search.js file like this:
// search.js for functional mind. Generated statically
// from the set of posts.
posts = { 'postlist' : ['YYY', 'other link...' ] };
posts_for_word = { 'word1' : [0,1], 'word2' : [Z,K] };
The last variable, posts_for_word includes indexes of posts containing that word. The problem comes when I evaluate that Javascript inside eval:
// global environment
var posts = {}; // Initial value, empty
var posts_for_word = {}; // same
function() {
var request = // create XMLHTTPREQUEST request
var response = // obtain response (the javascript)
eval( response ); // <- syntax error
// access posts or posts_for_word
posts.postlist[ posts_for_word['word1'][0] ]
....
}
The problems:
When I evaluate the javascript shown above with eval, firefox tells me that it is bad formed in the first line.
I then removed the comments. I supposed that eval would evaluate it as if it was a normal Javascript program, but it doesn't seem to accept comments (why?).
After removing the comments, the code works, but still the Javascript console of Firefox tells me that there is a syntax error in the first line (that defining the JSON first variable). Why?
You shouldn't use AJAX and eval() to make a script happen on your website, instead, create a <script> element with a link to the file and append it to the body:
(function() {
var jsCode = document.createElement('script');
jsCode.setAttribute('src', 'http://localhost/tests/doStuff.js');
document.body.appendChild(jsCode);
}());
(This code is actually taken directly from a bookmarklet of mine, and it does the trick nicely.)
As for the variable scope, if you declare them as var variable in your inner file, it would override the file scope and become global.
However, should you require a synchronous load of a script, you will have to (despite the fact it is considered bad practice) to use a synchronous AJAX call. How to do that was already written before me, Dynamically loading JavaScript synchronously

Auto-load/include for JavaScript

I have file called common.js and it's included in each page of my site using <script />.
It will grow fast as my sites functionality will grow (I hope; I imagine). :)
Lets example I have a jQuery event:
$('#that').click(function() {
one_of_many_functions($(this));
}
For the moment, I have that one_of_many_functions() in common.js.
Is it somehow possible that JavaScript automatically loads file one_of_many_functions.js when such function is called, but it doesn't exist? Like auto-loader. :)
The second option I see is to do something like:
$('#that').click(function() {
include('one_of_many_functions');
one_of_many_functions($(this));
}
That not so automatically, but still - includes wanted file.
Is any of this possible? Thanks in an advice! :)
It is not possible to directly auto-load external javascripts on demand. It is, however, possible to implement a dynamic inclusion mechanism similar to the second route you mentioned.
There are some challenges though. When you "include" a new external script, you aren't going to be able to immediately use the included functionality, you'll have to wait until the script loads. This means that you'll have to fragment your code somewhat, which means that you'll have to make some decisions about what should just be included in the core vs. what can be included on demand.
You'll need to set up a central object that keeps track of which assets are already loaded. Here's a quick mockup of that:
var assets = {
assets: {},
include: function (asset_name, callback) {
if (typeof callback != 'function')
callback = function () { return false; };
if (typeof this.assets[asset_name] != 'undefined' )
return callback();
var html_doc = document.getElementsByTagName('head')[0];
var st = document.createElement('script');
st.setAttribute('language', 'javascript');
st.setAttribute('type', 'text/javascript');
st.setAttribute('src', asset_name);
st.onload = function () { assets._script_loaded(asset_name, callback); };
html_doc.appendChild(st);
},
_script_loaded: function (asset_name, callback) {
this.assets[asset_name] = true;
callback();
}
};
assets.inlude('myfile.js', function () {
/* do stuff that depends on myfile.js */
});
Sure it's possible -- but this can become painful to manage. In order to implement something like this, you're going to have to maintain an index of functions and their corresponding source file. As your project grows, this can be troublesome for a few reasons -- the 2 that stick out in my mind are:
A) You have the added responsibility of maintaining your index object/lookup mechanism so that your scripts know where to look when the function you're calling cannot be found.
B) This is one more thing that can go wrong when debugging your growing project.
I'm sure that someone else will mention this by the time I'm finished writing this, but your time would probably be better spent figuring out how to combine all of your code into a single .js file. The benefits to doing so are well-documented.
I have created something close to that a year ago. In fact, I have found this thread by search if that is something new on the field. You can see what I have created here: https://github.com/thiagomata/CanvasBox/blob/master/src/main/New.js
My project are, almost 100% OOP. So, I used this fact to focus my solution. I create this "Class" with the name "New" what is used to, first load and after instance the objects.
Here a example of someone using it:
var objSquare = New.Square(); // Square is loaded and after that instance is created
objSquare.x = objBox.width / 2;
objSquare.y = objBox.height / 2;
var objSomeExample = New.Stuff("some parameters can be sent too");
In this version I am not using some json with all js file position. The mapping is hardcore as you can see here:
New.prototype.arrMap = {
CanvasBox: "" + window.MAIN_PATH + "CanvasBox",
CanvasBoxBehavior: "" + window.MAIN_PATH + "CanvasBoxBehavior",
CanvasBoxButton: "" + window.MAIN_PATH + "CanvasBoxButton",
// (...)
};
But make this more automatic, using gulp or grunt is something what I am thinking to do, and it is not that hard.
This solution was created to be used into the project. So, the code may need some changes to be able to be used into any project. But may be a start.
Hope this helps.
As I said before, this still is a working progress. But I have created a more independent module what use gulp to keep it updated.
All the magic que be found in this links:
https://github.com/thiagomata/CanvasBox/blob/master/src/coffee/main/Instance.coffee
https://github.com/thiagomata/CanvasBox/blob/master/src/node/scripts.js
https://github.com/thiagomata/CanvasBox/blob/master/gulpfile.js
A special look should be in this lines of the Instance.coffee
###
# Create an instance of the object passing the argument
###
instaceObject = (->
ClassElement = (args) ->
window[args["0"]].apply this, args["1"]
->
ClassElement:: = (window[arguments["0"]])::
objElement = new ClassElement(arguments)
return objElement
)()
This lines allows me to initialize a instance of some object after load its file. As is used in the create method:
create:()->
#load()
return instaceObject(#packageName, arguments)

jquery tmpl in a closure

i am working on this little javascript library and following various suggestions i am wrapping my functionality in a closure for the various reasons (encapsulation of variables, hidding of code and so on). since I query a JSON webservice and display the results I also use the jquery tmpl engine.
I think I understand what closures are good for but I sure don't understand them in general. meaning I get totally lost between all those scope changes and whatnot. especially annoying is this exception I get. consider the following code (a simplified ugly version of the code in question, but it reproduces the problem)
// something would be the object that handles all the library functionality
var something = function(){
// creating a local function that goes as a parameter into the Array.filter
function isBar(data){
return data.name === "bar";
}
// the template code
var bla = "<h1>${name}<\h1><h2>${attribute.filter(isBar)[0].value}</h2>";
// precompiling the the template
$.template("test", bla);
// and returning a function that should render the template with the provided data
return {
funny: function(){
$.tmpl("test", [{"name":"Fo", "attribute":[{"name":"bar", "value":"how"}]},
{"name":"Foo", "attribute":[{"name":"fnord","value":"can"}]},
{"name":"Fooo", "attribute":[{"name":"bar","value":"this"}]},
{"name":"Foooo", "attribute":[{"name":"Bar", "value":"be"}]}
]);
}
}
}();
// calling the function
something.funny();
So when calling the something.funny() I would the following expect to happen: the function funny, being a closure gets called in its original context (e.g. the function isBar and the variable bar are defined). So when I call $.tmpl I hoped that attribute.filter(isBar) within the template would also be in this scope. but it isn't. I Chrome i get ReferenceError: isBar is not defined.
If someone would be so nice to show me the error of my ways I would be very happy.
edit oops I missed the "()".
OK, well the problem is that those references to the local variables in the closure are not really references to local variables - they're part of a string. The template code has to parse that string, so when it does that the fact that there was a function called "isBar()" in the closure from where "$.tmpl()" was called really doesn't matter; jQuery can't access them because you just can't do that in JavaScript.
You can, however, pass in an "options" third parameter to "$.tmpl()" and provide extra stuff there. I'm not 100% sure how to do it as I've only played with the template plugin a little bit, but I'll try a jsfiddle when I have a chance. I think that you'd basically do something like this:
funny: function(){
$.tmpl("test", [{"name":"Fo", "attribute":[{"name":"bar", "value":"how"}]},
{"name":"Foo", "attribute":[{"name":"fnord","value":"can"}]},
{"name":"Fooo", "attribute":[{"name":"bar","value":"this"}]},
{"name":"Foooo", "attribute":[{"name":"Bar", "value":"be"}]}
], { isBar: isBar });
}
What I'm not sure of is whether you refer to that as "${isBar()}" or "${item.isBar()}" inside the template text.

Categories

Resources