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. :-)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With