What does this Javascript code do? - javascript

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

Related

node "require" hoisted to top outside of script -- loses access to variables from outer function

I'm requiring different files at the top of my main script in node. All my require statements are hoisted to the top. This creates a problem because when the methods within those imported scripts are invoked they do not have access to the function within which they are invoked (Because they are inevitably defined outside those functions due to the hoisting issue). Therefore, I must always pass variables in an options object. Has anyone experiences a similar issue? Is there some sort of standard workaround that people use? Thanks!
function outer(){
//let's pretend we're in a node environment
//this required script will get hoisted to the very top and therefore lose access to the "something" variable!
var common = require('../globals/common.js');
var something = "something";
common.printsomething();//will return "something is not defined"
};
outer();
Hm.
I would assume that it'd ultimately be better to pass 'something' to the printsomething method, like so.
common.printfoo('bar'); //returns 'bar'
Typically, what you're doing there isn't how modules in node works. Yes, breaking up a large program into separate files is an excellent way to organize a project, but I'm afraid that I have to say you're doing it wrong here. In the context of 'outer', you could do:
/*script.js*/
var common = require('../globals/common.js');
function outer(str){
common.printsomething(str);//will return "something"
};
var something = 'something';
outer(something);
/*common.js*/
function printthing(str){
console.log(str);
}
module.exports = {
printsomething: function(str){
printthing(str)
}
}
module.js:
module.exports.print = function (data) {
console.log(data);
}
module.exports.add = function (a, b, callback) {
callback(a + b);
}
main.js
var mymodule = require('module');
module.print('Some data'); //Will print "Some data" in the console
module.add(25, 12, function (result) {
console.log(result); //Will print 37
});
As you can see, in main.js, I do not need to know the content of module.js to wrk. that is the goal of modules: put the hard logic somewhere else, to build better code. Modules like async or fs are huge and complex, but I just have to import them to work with it, and don't need to know how it does it.
While building your own module, think of it as a new library of tools, so that you can reuse it in another project without the need to set specific variables to use them. Imagine the chaos it would be if two module were able to get the content of your var something for unrelated goal!
Modules are self contained, to be reusable. A "de hoisting" of thoses would reduce their efficacity.
EDIT:
If you have a lot of environment variable, you can try a pattern where you set them once inside the module, but you have to make sure to provide a way to interact with them.
module:
var data = {};
function set(key, value) {
data[key] = value;
}
function get(key) {
return data[key];
}
function print(key) {
console.log(data[key]);
}
function add(keyA, keyB) {
return data[keyA] + data[keyB];
}
module.exports = {
set: set,
get: get,
print: print,
add: add
};
main.js
var mymod = require('mymod');
mymod.set('data', 'something');
mymod.set('a', 25);
mymod.set('b', 12);
mymod.print('data'); //Print "something"
var c = mymod.add('a', 'b');
console.log(c); //Print 32

Get less variables list using less.js

I'm using client-side less.js. Is there a way to get all variables from my less file. I know how to modify variables:
less.modifyVars({
"#var": "#fff"
});
But I want to get all of them, like (don't work):
var variables = less.getVars();
This is going to be an unconventional answer as it seems that this data isn't publicly exposed as part of the browser facing API.
tl;dr
Save a local copy of the less.js file.
Add this function definition somewhere
function exposeVars(root) {
var r=root._variables,n=Object.keys(r),m={}
for(var k of n){m[k]=r[k].value}
less.variables = m;
}
Add the following call exposeVars(evaldRoot) just before return result on line ~2556.
Now less.variables contains all the variables from your file.
Disclaimer: Doing this is not a good idea! It's fine if you're just playing around, debugging or testing something, but don't depend on this hack for anything serious!
The basic aim here was to find the point in the source where the .less files are turned into abstract syntax trees (or some other formal structure).
Jumping straight into the source, I found a ParseTree class. It's a reasonable assumption to guess that it will contain the result of parsing the less file.
I wrote a quick test file and added it to the browser with the appropriate tag. It looks like this:
#myvar: red;
#othervar: 12px;
body {
padding: #othervar;
background: #myvar;
}
I've downloaded a local copy of less.js and added a breakpoint added to line 2556.
I had a poke around in the local scope to see what was available and found the variables in an object called evaldRoot.
evaldRoot = {
_variables: {
#myvar: {
name: '#myvar',
value: Color
},
#othervar: {
name: '#othervar',
value: Dimension
}
}
}
Next job was to work out where this data goes. It seems like the evaldRoot variable is used to generate the resulting CSS (which would make sense, as it contains information such as variables).
if (options.sourceMap) {
sourceMapBuilder = new SourceMapBuilder(options.sourceMap);
result.css = sourceMapBuilder.toCSS(evaldRoot, toCSSOptions, this.imports);
} else {
result.css = evaldRoot.toCSS(toCSSOptions);
}
Whatever happens, the variables goes out of scope after it is used to generate a string of CSS as result.css.
To expose of these variables, the script needs a small modification. You'll have to expose the variables publicly somehow. Here's an example of doing that.
function exposeVars(root) {
var varNames = Object.keys(root._variables);
var variables = varNames.reduce(function(varMap, varName) {
varMap[varName] = root._variables[varName].value;
}, {});
less.variables = variables;
}
This can be added just before the return statement with the breakpoint.
exposeVars(evaldRoot);
return result;
Now the variables will be available in a name: value object on the global less object.
You could even modify the expose function to return the variables from a call to less.getVars(). Just like your initial suggestion.
function exposeVars(root) {
// ...
less.getVars = function() {
return variables;
};
}

Can I prevent passing wrong number of parameters to methods with JS Lint, JS Hint, or some other tool?

I'm new to javascript programming (and scripting languages in general), but I've been using JS Lint to help me when I make syntax errors or accidentally declare a global variable.
However, there is a scenario that JS Lint does not cover, which I feel would be incredibly handy. See the code below:
(function () {
"use strict";
/*global alert */
var testFunction = function (someMessage) {
alert("stuff is happening: " + someMessage);
};
testFunction(1, 2);
testFunction();
}());
Notice that I am passing the wrong number of parameters to testFunction. I don't foresee myself ever in a situation where I would intentionally leave out a parameter or add an extra one like that. However, neither JS Lint nor JS Hint consider this an error.
Is there some other tool that would catch this for me? Or is there some reason why passing parameters like that shouldn't be error checked?
This is not generally possible with any static analysis tool. There are several reasons for this:
In general, JS functions can accept any number of arguments.
Most (all?) linters only work on a single file at a time. They do not know anything about functions declared in other files
There is no guarantee that a property being invoked as a function is the function that you expect. Consider this snippet:
var obj = { myFunc : function(a,b) { ... } };
var doSomething(x) { x.myFunc = function(a) { ... }; };
doSomething(obj);
obj.myFunc();
There is no way to know that myFunc now takes a different number of args after the call to doSomething.
JavaScript is a dynamic language and you should accept and embrace that.
Instead of relying on linting to catch problems that it wasn't intended to I would recommend adding preconditions to your functions that does the check.
Create a helper function like this:
function checkThat(expression, message) {
if (!expression) {
throw new Error(message);
}
}
Then use it like this:
function myFunc(a, b) {
checkThat(arguments.length === 2, "Wrong number of arguments.");
And with proper unit testing, you should never see this error message in production.
It's not natively possible in javascript. You would have to do something like this:
var testFunction = function (someMessage) {
var args = Array.prototype.slice.call(arguments);
if (args.length !== 1) throw new Error ("Wrong number of arguments");
if (typeof args[1] !== string) throw new Error ("Must pass a string");
// continue
};
Paul Irish demoed a hack for this a while back...passing undefined at the end of the parameters...
var testFunction = function (someMessage, undefined) {
alert("stuff is happening: " + someMessage);
};
testFunction("one", "two", "three");
He demos it here...see if it's what your looking for.

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 object member function referred to globally not recognized in callback

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.

Categories

Resources