Javascript object member function referred to globally not recognized in callback - javascript

I'm having a problem with the following Javascript code (Phonegap in Eclipse):
function FileStore(onsuccess, onfail){
//chain of Phonegap File API handlers to get certain directories
function onGetSupportDirectorySuccess(dir){
//stuff
onsuccess();
}
function getDirectory(dir){
return "something" + dir;
}
}
var onFileStoreOpened = function(){
if (window.file_store instanceof FileStore){
console.log('window.file_store is a FileStore');
console.log(window.file_store.getDirectory('something'));
}
}
var onDeviceReady = function(){
window.file_store = new FileStore(onFileStoreOpened, onFileStoreFailure);
}
Here, I want to do some things to initialize file services for the app, and then use them in my initialization from the callback. I get the following error messages in LogCat:
07-03 06:26:54.942: D/CordovaLog(223): file:///android_asset/www/index.html: Line 40 : window.file_store is a FileStore
07-03 06:26:55.053: D/CordovaLog(223): file:///android_asset/www/cordova-1.8.1.js: Line 254 : Error in success callback: File7 = TypeError: Result of expression 'window.file_store.getDirectory' [undefined] is not a function.
After moving the code around and stripping out everything in getDirectory() to make sure it was valid, I'm not even sure I understand the error message, which suggested to me that getDirectory() is not seen as a member function of window.file_store, even though window.file_store is recognized as a FileStore object. That makes no sense to me, so I guess that interpretation is incorrect. Any enlightenment will be greatly appreciated.
I've since tried the following:
window.file_store = {
app_data_dir : null,
Init: function(onsuccess, onfail){
//chain of Phonegap File API handlers to get directories
function onGetSupportDirectorySuccess(dir){
window.file_store.app_data_dir = dir;
console.log("opened dir " + dir.name);
onsuccess();
}
},
GetDirectory : function(){
return window.file_store.app_data_dir; //simplified
}
}
var onFileStoreOpened = function(){
var docs = window.file_store.getDirectory();
console.log('APPDATA: ' + docs.fullPath);
}
var onDeviceReady = function() {
window.file_store.Init(onFileStoreOpened, onFileStoreFailure);
}
and I get
D/CordovaLog(224): file:///android_asset/www/base/device.js: Line 81 : opened dir AppData
D/CordovaLog(224): file:///android_asset/www/cordova-1.8.1.js: Line 254 : Error in success callback: File7 = TypeError: Result of expression 'docs' [null] is not an object.
All I want to do here is make sure certain directories exist (I've removed all but one) when I start, save the directory object for future use, and then retrieve and use it after all initialization is done, and I don't want everything in the global namespace. Of course I would like to be able to use specific instances when necessary, and I'm disturbed that I can't make it work that way since it demonstrates there is a problem with my understanding, but I can't even get this to work with a single, global one. Is this a Javascript problem or a Phonegap problem?

As it stands, your getDirectory function is a private function within FileStore. If you wanted to make it a 'member' or 'property' of FileStore, you would need to alter it a little within FileStore to make it like this:
this.getDirectory = function(dir){ };
or leave it how it is and then set a property....
this.getDirectory = getDirectory();
this way when new FileStore is called it will have getDirectory as a property because the 'this' keyword is always returned when calling a function with 'new'
Hope this quick answer helps. There's lots of stuff on the goog about constructor functions.

You understand it correctly. The getDirectory as it stands is a private function and cannot be called using the file_store instance.
Try this in the browser.
function FileStore(onsuccess, onfail){
function onGetSupportDirectorySuccess(dir){
//stuff
onsuccess();
}
this.getDirectory = function (dir){
return "something" + dir;
}
}
window.file_store = new FileStore('', ''); //the empty strings are just placeholders.
if (window.file_store instanceof FileStore){
console.log('window.file_store is a FileStore');
console.log(window.file_store.getDirectory('something'));
}
This will prove that the basic js code is working fine. If there still is a problem while using it in PhoneGap, comment.

Related

How to bind console.log to another function call so I can see line number of the script in console where it is called?

My code works but with additional parenthesis like myfunction()();. It should execute with single parenthesis just like normal e.g myfunction();.
I'm building console.time(); console.timeEnd(); polyfill for browsers (e.g <IE10) which do not have native built-in. Note: I have bind() polyfill in-case you think <IE10 does not have it.
Here is my code in "polyfill.js file".
(function() {
'use strict';
var console=window.console, timers={};
if (!console.time) {
console.time = function(name) {
var datenow = Date.now();
name = name? name: 'default';
if (timers[name]) {
console.warn('Timer "'+name+'" already exists.');
}
else timers[name] = datenow;
};
console.timeEnd = function(name) {
var datenow = Date.now();
name = name? name: 'default';
if (!timers[name]) {
console.warn('Timer "'+name+'" does not exists.');
}
else {
var endt = datenow - timers[name];
delete timers[name];
//below is the line where some changes are needed, But I don't know how.
return window.console.log.bind(window.console, name+ ': ' +endt+ 'ms');
}
};
}
}());
Now in another file "main.js file", when I use console.time(); console.timeEnd();, it should log code-line-number of this file in browser console (not the line-number of polyfill.js file). Of-course it works but notice additional parenthesis "()()" below which is not cool.
console.time();
//any code for performance test goes here.
console.timeEnd()(); //Note here "()()". It should be single "()"
I have consulted these 2 stackoverflow questions, but couldn't come up with the right answer.
Wrapping a wrapper of console log with correct file/line number?
A proper wrapper for console.log with correct line number?
I also checked new Error().stack; as an option, but it is also not supported in those browser for which I'm building my polyfill.
Note: If anyone can suggest a solution with eval();, you can. It is also acceptable for me.
There is in fact a function for that called console.trace, which you can read more about in the MDN page.
What it does is print the entire stack trace to the line where it has been called from.
So, for example, running the next code:
function firstFunc() {
secondFunc();
}
function secondFunc() {
console.trace('I was called here!');
}
console.log('Calling firstFunc:');
firstFunc();
will print out this output in the console:
Calling firstFunc:
I was called here!
secondFunc # VM141:6
firstFunc # VM141:2
(anonymous) # VM141:10 // Internal browser trace
Notice that in the given output, all functions are being called and defined in the Chrome console, hence the # VM141:. Generally, it prints the file instead of VM. So, had it been located in an index.js file, it would look like this:
Calling firstFunc:
I was called here!
secondFunc # index.js:8
Compatibility Note
The above method works for any sane browser, and IE11+. That is due to the implementation of console.trace only in IE11.
However, per OP's request, I can think of a creative way to support IE10, and that is by using the Error.prototype.stack property.
Now, of course, as MDN itself mentions it, it's a non-standard feature that should not be used in production, but neither is supporting IE6.
By creating an Error instance and then printing its stack, you can achieve a similar result.
const sumWithTrace = (num1, num2) => {
console.log(new Error().stack); // Creating a new error for its stack property
return num1 + num2;
};
sumWithTrace(1, 5); // returns 6 and prints trace in console

How to overcome issue of ".. is not a function" while referring to earlier defined function expression in same js file

On accessing function expression in the same file, gives an error of .. "is not a function".
I need this below function expression to be available both outside the js file to other .js files and as well inside the same js file.
I have tried below things from below blogs, nothing seems to work
https://github.com/nodejs/node/issues/2923
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Not_a_function
// this is in abc.js
function qpValidations() {
this.addDaystoGetDate = function(noOfDays){
...
}
this.constructDate = function(){
this.addDaystoGetDate(2);// here issue is coming, where trying to
//call function in same .js file
}
}
module.exports = new qpValidations();
Any help is most appreciated!!, though this issue occurred many times with me, tried avoiding file circular dependency and as well clubbing function expression and declaration, had solved the issues earlier, but now again it has poped up not sure what the root cause of this issue..?
If you're only having issues calling the function in the same file, it's hard to help because you haven't shown HOW you're trying to call it. But I would suggest you try something like this:
// this is in abc.js
function qpValidations() {
this.addDaystoGetDate = function(noOfDays){
...
}
this.constructDate = function(){
this.addDaystoGetDate (2);// here issue is coming
}
}
const newQp = new qpValidations()
module.exports = newQp;
newQp.addDaystoGetDate();
addDaystoGetDate function required a parameter. try sending a parameter when you call the function.
newQp.addDaystoGetDate(5);
newQp.addDaystoGetDate("some text"); //or whatever you need
Finally got the answer, it was from one of my buddy:), the scope of this.addDaystoGetDate()
gets changed when it is called inside another this.constructDate() function. So as a solution assign "this" object to some variable like below, further can be referred same wherever we come across this
function validation(){
var refObj = this;
refObj.addDaystoGetDate =function(dayscount){
}
refObj.constructDate = function(){
refObj.addDaystoGetDate(2);
}
}
module.exports = new validtaion();

JavaScript Autoloader: How To Recover From Errors

I have a proof-of-concept working for a JavaScript autoloader, but it currently suffers from a major flaw: it requires the code to be re-executed in its entirety rather than simply trying again from the line that failed.
Here is the prototype:
<!doctype html>
<html>
<head>
</head>
<body>
<script type="text/javascript">
var app = function(){
console.log('Initialize App');
var test = new Test();
var foo = new Foo();
var bar = new Bar();
};
var autoload = function(app){
var recover = function(error){
var name = error.message.split(' ')[0];
console.log('Loading '+name);
//A file could be synchronously loaded here instead
this[name] = function(){
console.log(name+' has been dynamically created');
};
load(app);
};
var load = function(app){
try {
app();
} catch (error){
if (error.name == "ReferenceError"){
console.log(error.message);
recover(error, app);
}
}
};
load(app);
};
autoload(app);
</script>
</body>
</html>
How It's Supposed To Work
The idea is that all of your application code would get executed within the app function. Eventually, if I can get it working properly, you could also pass in a dependency map to autoloader with the app function to synchronously load dependencies when a function is not defined. The dependency map would simply be an object mapping function names to file names.
How It Currently Works
If you don't feel like trying it out, the above code outputs the following to the console:
Initialize App
Test is not defined
Loading Test
Initialize App
Test has been dynamically created
Foo is not defined
Loading Foo
Initialize App
Test has been dynamically created
Foo has been dynamically created
Bar is not defined
Loading Bar
Initialize App
Test has been dynamically created
Foo has been dynamically created
Bar has been dynamically created
The complete app function is re-executed each time the autoloader catches an error. Obviously, this is less than ideal for a number of reasons.
Recovering From The Error
To move to the next step for making this work, I need to find a way to recover from the error without re-executing the entire app function. The error object from the catch block does provide both the line number and file name where the error occurred, but so far, I haven't been able to find a way to take advantage of that information. There are three general approaches that I can think of:
Restart script execution at the given line
Restart script execution at the beginning, but skip all lines until the given line
Grab the file as a string, split it into an array by line number, and eval the remaining lines.
Unfortunately, I wasn't able to find information on either of the first two approaches. Of the three, #1 seems like it would be more ideal, but I would certainly be open to other creative suggestions as well. As far as I can tell, JavaScript doesn't provide a way to start script execution at an arbitrary line number. #3 might work, but I'm not sure it would be very performant. The only way I can think of doing it would be to require an extra request each time to load the file text into a string.
The Questions
This is admittedly pushing the boundaries of how dependencies could be loaded in JavaScript. I'm not even sure if it is possible because I don't know if JavaScript allows for this type of error recovery. That said, I'm interested in exploring it further until I find out it's absolutely impossible.
In the interests of getting this working:
Is there a way to start script execution at an arbitrary line of JavaScript?
Are there other approaches to this that might be more fruitful? Feel free to be creative!
Taking a step back to look at the bigger picture (assuming I can get this to work):
Is a JavaScript autoloader something that people would even want?
What are the advantages/disadvantages to an autoloader like this vs. an approach like AMD?
What kind of performance issues would be encountered with this approach? Would the performance hit be too much to make it worth it?
It's kind of complicated to make, throwing and catching is kind of expensive as well. You could use typeof window["Test"]!=="function" and then create it instead of using the try catch like this.
But for a general recover and continue approach the following code would do the trick.
var app = (function(i){
var objects=new Array(3),
fnNames=["Test","Foo","Bar"];
return function(){
var len=fnNames.length
,test,foo,bar;
//check if closure vars have been reset, if so
// this has ran successfully once so don't do anything?
if(objects===false){
console.log("nothing to do, initialized already");
return;
}
while(i<len){
try{
objects[i] = new window[fnNames[i]]();
i++;
}catch(e){
if (e.name == "TypeError"){//different syntax different error
throw {"fnName":fnNames[i]};
}
}
}
//store instances in the variables
test = objects[0];
foo = objects[1];
bar = objects[2];
//reset closure vars assuming you only call app once
// when it's successful
i=null;objects=false;
console.log("Got all the instances",test,foo,bar);
};
}(0));
var autoload = function(app){
var recover = function(name){
console.log('Loading '+name);
//A file could be synchronously loaded here instead
this[name] = function(){
this.name=name;
console.log(this.name+' has been dynamically created');
};
load(app);
};
var load = function(app){
try {
app();
} catch (error){
//if statement here no longer needed
// object thrown has fnName (function name)
recover(error.fnName, app);
}
};
load(app);
};
autoload(app);
autoload(app);

JavaScript Factory class throws errors if function name is not a 'constructed string'

I have a Factory class that I use in JavaScript to dynamically load a class file over AJAX and then return an object. I have run into a VERY peculiar bug in the system though that throws errors in EVERY browser, but under a condition that is beyond my ability to explain.
Here is a simplified version of my Factory class (I removed a lot of type checking and error handling to cut it down to bare minimum).
function Factory(){
// This holds which files have already been loaded
var loaded=new Object();
// Returns a new object
this.getObject=function(className,methodName,methodData){
if(loadFile('class.'+className+'.js')){
// Making sure that the object name is defined
if(window[className]!=null){
// Has to be an object
if(typeof(window[className])=='function'){
// Creating a temporary object
return new window[className];
}
}
}
}
// Loads a file over AJAX
var loadFile=function(address){
// Loads as long as the file has not already been loaded
if(loaded[address]==null){
// Required for AJAX connections (without ASYNC)
var XMLHttp=new XMLHttpRequest();
XMLHttp.open('GET',address,false);
XMLHttp.send(null);
// Result based on response status
if(XMLHttp.status===200 || XMLHttp.status===304){
// Getting the contents of the script
var data=XMLHttp.responseText;
// Loading the script contents to the browser
(window.execScript || function(data){
window['eval'].call(window,data);
})(data);
// makes sure that file is loaded only once
loaded[address]=true;
}
}
}
This is what the user does:
var Factory=new Factory();
var alpha=Factory.getObject('example');
alpha.set(32);
alpha.get();
var beta=Factory.getObject('example');
beta.set(64);
alpha.get();
beta.get();
This fails, it says 'object is not a function' when the function is run for the second time (at the return new window[className]; line). I understand if I am missing something here, BUT here's the kicker:
If I prefix className in my window[] calls, then it works. For example, if I change my 'example' class filename to 'test_example' and then have these lines:
... if(window['test_'+className]!=null){ ...
... if(typeof(window['test_'+className])=='function'){ ...
... return new window['test_'+className]; ...
Then it works and both alpha and beta objects work as expected. When I refer to them purely through a variable, they fail. I tried things like className.toString() without success and even this fails:
className+''
This is really weird, I don't know where to look and what to try out anymore, does anyone know why this happens?
EDIT: Here is an example of the 'example.js' script that is being loaded:
function example(){
var myVar=16;
this.set=function(value){
myVar=value;
}
this.get=function(){
alert(myVar);
}
}
(and if I rename this to test_example() and load the functions as shown above with constructed strings, then it again works)
I figured out where the error was, which my above, cut-down version does not show. Apparently I named my new variable the same that the name of the class itself was, thus after first initialization it got overwritten.

What does this Javascript code do?

I've been looking at Sharepoint script files and I've come across this bit that I don't get:
function ULSTYE() {
var o = new Object;
o.ULSTeamName = "Microsoft SharePoint Foundation";
o.ULSFileName = "SP.UI.Dialog.debug.js";
return o;
}
SP.UI.$create_DialogOptions = function() {
ULSTYE:; <----------------------------- WTF?
return new SP.UI.DialogOptions();
}
Actually every function definition in this file starts with the same ULSTYE:; line right after the opening brace. Can anybody explain what does the first line in the second function do?
Firefox/Firebug for instance interprets this function as something that I can't understand either:
function () {
ULSTYE: {
}
return new (SP.UI.DialogOptions);
}
And I thought I knew Javascript through and through... ;) Must be some obscure feature I never used in the past and is obviously seldomly used by others as well.
After wondering about this for a long time, I finally sat down and worked it out. It's all part of a relatively sophisticated mechanism for collecting diagnostic information on the client which includes the ability to send a javascript callstack (including function name, and javascript file) back to the server.
Take a look at the first 250 lines of the file init.debug.js which is located at
%Program Files%\Common Files\Microsoft Shared\Web Server Extensions\14\TEMPLATE\LAYOUTS\1033\init.debug.js
This file defines all the functions the 'ULS' implementation on the client.
Of course, you'll need to have SharePoint 2010 installed for the file to exist on your local machine.
UPDATE -- The following is an overview of roughly how the mechanism works. The real implementation does more than this
Consider the following html page with a few js includes, each of which can call out into each other.
<html>
<head>
<script type="text/javascript" src="ErrorHandling.js"></script>
<script type="text/javascript" src="File1.js"></script>
<script type="text/javascript" src="File2.js"></script>
</head>
<body>
<button onclick="DoStuff()">Do stuff</button>
</body>
</html>
We have two js include files, File1.js
function ULSabc() { var o = new Object; o.File = "File1.js"; return o; }
/* ULSabc is the unique label for this js file. Each function in
this file can be decorated with a label corresponding with the same name */
function DoStuff() {
ULSabc: ;
//label matches name of function above
DoMoreStuff();
}
and File2.js
function ULSdef() { var o = new Object; o.File = "File2.js"; return o; }
function DoMoreStuff() {
ULSdef: ;
DoEvenMoreStuff();
}
function DoEvenMoreStuff() {
ULSdef: ;
try {
//throw an error
throw "Testing";
} catch (e) {
//handle the error by displaying the callstack
DisplayCallStack(e);
}
}
Now, say our ErrorHandling file looks like this
function GetFunctionInfo(fn) {
var info = "";
if (fn) {
//if we have a function, convert it to a string
var fnTxt = fn.toString();
//find the name of the function by removing the 'function' and ()
var fnName = fnTxt.substring(0, fnTxt.indexOf("(")).substring(8);
info += "Function: " + fnName;
//next use a regular expression to find a match for 'ULS???:'
//which is the label within the function
var match = fnTxt.match(/ULS[^\s;]*:/);
if (match) {
var ULSLabel = match[0];
//if our function definition contains a label, strip off the
// : and add () to make it into a function we can call eval on
ULSLabel = ULSLabel.substring(0, ULSLabel.length - 1) + "()";
//eval our function that is defined at the top of our js file
var fileInfo = eval(ULSLabel);
if (fileInfo && fileInfo.File) {
//add the .File property of the returned object to the info
info += " => Script file: " + fileInfo.File;
}
}
}
return info;
}
function DisplayCallStack(e) {
//first get a reference to the function that call this
var caller = DisplayCallStack.caller;
var stack = "Error! " + e + "\r\n";
//recursively loop through the caller of each function,
//collecting the function name and script file as we go
while (caller) {
stack += GetFunctionInfo(caller) + "\r\n";
caller = caller.caller;
}
//alert the callstack, but we could alternately do something
//else like send the info to the server via XmlHttp.
alert(stack);
}
When we click the button on the page, our script file will call through each of the functions and end at DisplayCallStack, at which point it will recursively loop through and collect the stack trace
Error! Testing
Function: DoEvenMoreStuff => Script file: File2.js
Function: DoMoreStuff => Script file: File2.js
Function: DoStuff => Script file: File1.js
Function: onclick
The first bit defines a function that creates an object with a couple of properties and returns it. I think we're all clear on that bit. :-)
The second bit, though, is not using that function. It's defining a label with the same name. Although it uses the same sequence of characters, it is not a reference to the function above. Firefox's interpretation makes as much sense as anything else, because a label should be followed by something to which it can refer.
For more about labelled statements, see Section 12.12 of the spec.
Off-topic: I would avoid using code from this source. Whoever wrote it is apparently fairly new to JavaScript and doesn't show much sign that they know what they're doing. For instance, they've left the () off the new Object() call, and while that's allowed, it's fairly dodgy thing to do. They could argue that they were doing it to save space, but if they were, they'd be better off using an object literal:
function ULSTYE() {
return {
ULSTeamName: "Microsoft SharePoint Foundation",
ULSFileName: "SP.UI.Dialog.debug.js"
};
}
There's never much reason to write new Object() at all; {} is functionally identical.
And, of course, there's no justification for the second bit at all. :-)
Isn't it just a statement label? The fact the label has the same name as the earlier function doesn't mean anything, I think.
it looks, like it creates an empty object which should be filled with some data, but due to code generator that creates this code it is not deleted so it sits there empty

Categories

Resources