Can someone format the code below so that I can set srcript variables with c# code using razor?
The below does not work, i've got it that way to make is easy for someone to help.
#{int proID = 123; int nonProID = 456;}
<script type="text/javascript">
#{
<text>
var nonID =#nonProID;
var proID= #proID;
window.nonID = #nonProID;
window.proID=#proID;
</text>
}
</script>
I am getting a design time error
You should take a look at the output that your razor page is resulting. Actually, you need to know what is executed by server-side and client-side. Try this:
#{
int proID = 123;
int nonProID = 456;
}
<script>
var nonID = #nonProID;
var proID = #proID;
window.nonID = #nonProID;
window.proID = #proID;
</script>
The output should be like this:
Depending what version of Visual Studio you are using, it point some highlights in the design-time for views with razor.
Since razor syntax errors can become problematic while you're working on the view, I totally get why you'd want to avoid them. Here's a couple other options.
<script type="text/javascript">
// #Model.Count is an int
var count = '#Model.Count';
var countInt = parseInt('#Model.ActiveLocsCount');
</script>
The quotes act as delimiters, so the razor parser is happy. But of course your C# int becomes a JS string in the first statement. For purists, the second option might be better.
If somebody has a better way of doing this without the razor syntax errors, in particular maintaining the type of the var, I'd love to see it!
This is how I solved the problem:
#{int proID = 123; int nonProID = 456;}
<script type="text/javascript">
var nonID = Number(#nonProID);
var proID = Number(#proID);
</script>
It is self-documenting and it doesn't involve conversion to and from text.
Note: be careful to use the Number() function not create new Number() objects - as the exactly equals operator may behave in a non-obvious way:
var y = new Number(123); // Note incorrect usage of "new"
var x = new Number(123);
alert(y === 123); // displays false
alert(x == y); // displays false
I've seen several approaches to working around the bug, and I ran some timing tests to see what works for speed (http://jsfiddle.net/5dwwy/)
Approaches:
Direct assignment
In this approach, the razor syntax is directly assigned to the variable. This is what throws the error. As a baseline, the JavaScript speed test simply does a straight assignment of a number to a variable.
Pass through `Number` constructor
In this approach, we wrap the razor syntax in a call to the `Number` constructor, as in `Number(#ViewBag.Value)`.
ParseInt
In this approach, the razor syntax is put inside quotes and passed to the `parseInt` function.
Value-returning function
In this approach, a function is created that simply takes the razor syntax as a parameter and returns it.
Type-checking function
In this approach, the function performs some basic type checking (looking for null, basically) and returns the value if it isn't null.
Procedure:
Using each approach mentioned above, a for-loop repeats each function call 10M times, getting the total time for the entire loop. Then, that for-loop is repeated 30 times to obtain an average time per 10M actions. These times were then compared to each other to determine which actions were faster than others.
Note that since it is JavaScript running, the actual numbers other people receive will differ, but the importance is not in the actual number, but how the numbers compare to the other numbers.
Results:
Using the Direct assignment approach, the average time to process 10M assignments was 98.033ms. Using the Number constructor yielded 1554.93ms per 10M. Similarly, the parseInt method took 1404.27ms. The two function calls took 97.5ms for the simple function and 101.4ms for the more complex function.
Conclusions:
The cleanest code to understand is the Direct assignment. However, because of the bug in Visual Studio, this reports an error and could cause issues with Intellisense and give a vague sense of being wrong.
The fastest code was the simple function call, but only by a slim margin. Since I didn't do further analysis, I do not know if this difference has a statistical significance. The type-checking function was also very fast, only slightly slower than a direct assignment, and includes the possibility that the variable may be null. It's not really practical, though, because even the basic function will return undefined if the parameter is undefined (null in razor syntax).
Parsing the razor value as an int and running it through the constructor were extremely slow, on the order of 15x slower than a direct assignment. Most likely the Number constructor is actually internally calling parseInt, which would explain why it takes longer than a simple parseInt. However, they do have the advantage of being more meaningful, without requiring an externally-defined (ie somewhere else in the file or application) function to execute, with the Number constructor actually minimizing the visible casting of an integer to a string.
Bottom line, these numbers were generated running through 10M iterations. On a single item, the speed is incalculably small. For most, simply running it through the Number constructor might be the most readable code, despite being the slowest.
#{
int proID = 123;
int nonProID = 456;
}
<script>
var nonID = '#nonProID';
var proID = '#proID';
window.nonID = '#nonProID';
window.proID = '#proID';
</script>
One of the easy way is:
<input type="hidden" id="SaleDateValue" value="#ViewBag.SaleDate" />
<input type="hidden" id="VoidItem" value="#Model.SecurityControl["VoidItem"].ToString()" />
And then get the value in javascript:
var SaleDate = document.getElementById('SaleDateValue').value;
var Item = document.getElementById('VoidItem').value;
I found a very clean solution that allows separate logic and GUI:
in your razor .cshtml page try this:
<body id="myId" data-my-variable="myValue">
...your page code here
</body>
in your .js file or .ts (if you use typeScript) to read stored value from your view put some like this (jquery library is required):
$("#myId").data("my-variable")
Not so much an answer as a cautionary tale: this was bugging me as well - and I thought I had a solution by pre-pending a zero and using the #(...) syntax. i.e your code would have been:
var nonID = 0#(nonProID);
var proID = 0#(proID);
Getting output like:
var nonId = 0123;
What I didn't realise was that this is how JavaScript (version 3) represents octal/base-8 numbers and is actually altering the value. Additionally, if you are using the "use strict"; command then it will break your code entirely as octal numbers have been removed.
I'm still looking for a proper solution to this.
It works if you do something like this:
var proID = #proID + 0;
Which produces code that is something like:
var proID = 4 + 0;
A bit odd for sure, but no more fake syntax errors at least.
Sadly the errors are still reported in VS2013, so this hasn't been properly addressed (yet).
I've been looking into this approach:
function getServerObject(serverObject) {
if (typeof serverObject === "undefined") {
return null;
}
return serverObject;
}
var itCameFromDotNet = getServerObject(#dotNetObject);
To me this seems to make it safer on the JS side... worst case you end up with a null variable.
This should cover all major types:
public class ViewBagUtils
{
public static string ToJavascriptValue(dynamic val)
{
if (val == null) return "null";
if (val is string) return val;
if (val is bool) return val.ToString().ToLower();
if (val is DateTime) return val.ToString();
if (double.TryParse(val.ToString(), out double dval)) return dval.ToString();
throw new ArgumentException("Could not convert value.");
}
}
And in your .cshtml file inside the <script> tag:
#using Namespace_Of_ViewBagUtils
const someValue = #ViewBagUtils.ToJavascriptValue(ViewBag.SomeValue);
Note that for string values, you'll have to use the #ViewBagUtils expression inside single (or double) quotes, like so:
const someValue = "#ViewBagUtils.ToJavascriptValue(ViewBag.SomeValue)";
I use a very simple function to solve syntax errors in body of JavaScript codes that mixed with Razor codes ;)
function n(num){return num;}
var nonID = n(#nonProID);
var proID= n(#proID);
This sets a JavaScript var for me directly from a web.config defined appSetting..
var pv = '#System.Web.Configuration.WebConfigurationManager.AppSettings["pv"]';
With
var jsVar = JSON.parse(#Html.Raw(Json.Serialize(razorObject)));
you can parse any razor object into a JavaScript object.
It's long but universal
Related
I have something like:
var sFunction = 'my_function("param1", "param2")';
var oMyObject = ...;
And I want to combine it so the result would be equal to:
oMyObject.my_function("param1", "param2");
Would much appreciate any tips.
Remark
As many of you suggested to find a root cause and try not to deal with the problematic input here are some pieces of information about the origins of the "problem".
The sFunction comes from database, hardcoded in one of the columns. It is custom one which should be called on object retrieved basing on other parameters of sFunction's database record.
So being backed up by your comments I will try suggesting changing data model in hope that it is not too late for that. Thank you all for your help.
I am given that as an input, it may come from db or anywhere else. I just have to deal with it in described way.
As Luca noted, you're probably best off solving the problem that brought you to the point of having code in a string that you feel you need to evaluate at runtime. The number of use cases for doing that is very low.
For instance, instead of
sFunction = 'my_function("param1", "param2")';
perhaps you could have
call = {
f: "my_function",
params: ["param1", "param2"]
};
Then it's:
oMyObject[call.f].apply(oMyObject, call.params);
call could even start life as JSON text you parse -- live example:
var json =
'{' +
'"f": "my_function",' +
'"params": ["param1", "param2"]' +
'}';
var call = JSON.parse(json);
var oMyObject = {
my_function: function(p1, p2) {
console.log(p1, p2);
}
};
oMyObject[call.f].apply(oMyObject, call.params);
That's markedly safer than an arbitrary code execution.
You can do this with your sFunction (eval("oMyObject." + sFunction)), but consider:
It lets any arbitrary code in sFunction run.
If User A supplies the code and then you run it on User B's system, you're compromising User B's privacy. (I am not a lawyer, but you could be doing so in a way that violates a country's data protection or privacy laws.)
Now, if you're loading code from a DB and you know that the code in the DB can only be put there by trusted people (for instance, developers on your team, not end users of the system), that's fine, it's largely like running a script file. But there's almost certainly a better way to do it than delivering the code as a string and evaling it.
But if the code comes from "anywhere else", it's not fine; see bullet points above. The setup is fundamentally broken and better options are available. Take that information to your boss, and if necessary to his/her boss, and if necessary his/her boss, until you find someone who can change the requirement.
Here's a string hack that doesn't use eval(), but as I (and others) have said, this is not a good solution. The better solution would be to return the function name and any arguments as a comma delimited string, which would at least make this kind of solution more straight-forward.
var sFunction = 'my_function("param1", "param2")';
// The object would have to already have the function:
var oMyObject = {
my_function: function(x,y){
return x + y;
}
};
// Remove the last ")" and split the remainder into an array at the "("
var funcParts = sFunction.replace(")","").split("(");
// Split the second part (the arguments) into its own array
var funcArgs = funcParts[1].split(",");
// Pass the function name as a string key to the object and then pass the arguments to that
console.log(oMyObject[funcParts[0]](funcArgs[0], funcArgs[1]));
The bigger question is, what ultimately are you trying to accomplish as there is almost always a better approach than this.
To do a dynamic function call you can of course eval as I did in the comments, which is of course a terrible idea. Here is a quick-and-dirty alternative:
const dynamicCallMethod = (obj, s) => {
try {
const fname = s.match(/([$\w]+\(/);
const params = s.match(/("[\w$]+")/g);
return obj[fname](...params);
} catch (e) {
return e;
}
};
Note I still think there's any easier way to do this if you describe the scenario in more detail. The above will fail for any non-ascii characters, for instance.
I'm trying to debug some code that another programmer has left for me to maintain. I've just attempted to upgrade from node.js 5 to node.js 8 and my database queries are for some requests coming back with key not found errors
We're using couchbase for the database and our document keys are "encrypted" for security. So we may have a key that starts like this "User_myemail#gmail.com" but we encrypt it using the following method:
function _GetScrambledKey(dbKey)
{
//select encryption key based on db key content
var eKeyIndex = CalculateEncryptionKeyIndex(dbKey, eKeys.length);
var sha = CalculateSHA512(dbKey + eKeyIndex);
return sha;
}
function CalculateEncryptionKeyIndex(str, max)
{
var hashBuf = CalculateSHA1(str);
var count = 0;
for (var i = 0; i < hashBuf.length; i++)
{
count += hashBuf[i];
count = count % max;
}
return count;
}
We then query couchbase for the document with
cb.get("ECB_"+encryptedKey, opts, callback);
In node5 this worked but in node8 we're getting some documents return fine and others return as missing. I outputted the "ECB_"+encryptedKey as an int array and the results have only confused me more. They are different on node5 to node8 but only by one character right in the middle of the array.
Outputting the encryptedKey as an int array on both versions shows this
188,106,14,227,211,70,94,97,63,130,78,246,155,65,6,148,62,215,47,230,211,109,35,99,21,60,178,74,195,13,233,253,187,142,213,213,104,58,168,60,225,148,25,101,155,91,122,77,2,99,102,235,26,71,157,99,6,47,162,152,58,181,21,175
Then outputting the concatenated string, in the same way, shows slightly different results
This is the node8 output
Node8 key: 69,67,66,95,65533,106,14,65533,65533,70,94,97,63,65533,78,65533,65533,65,6,65533,62,65533,47,65533,65533,109,35,99,21,60,65533,74,65533,13,65533,65533,65533,65533,65533,65533,104,58,65533,60,65533,25,101,65533,91,122,77,2,99,102,65533,26,71,65533,99,6,47,65533,65533,58,65533,21,65533
And this is the node5 output
Node5 key: 69,67,66,95,65533,106,14,65533,65533,70,94,97,63,65533,78,65533,65533,65,6,65533,62,65533,47,65533,65533,109,35,99,21,60,65533,74,65533,13,65533,65533,65533,65533,65533,65533,104,58,65533,60,65533,65533,25,101,65533,91,122,77,2,99,102,65533,26,71,65533,99,6,47,65533,65533,58,65533,21,65533
I had to run it through a diff tool to see the difference
Comparing that to the original pre-append array it looks like the 225 has just been dropped in node8. Is 225 significant? I can't understand how that would be possible otherwise unless it's a bug. Does anyone have any ideas?
Looks like this was a change in v8 5.5 https://github.com/nodejs/node/issues/21278
A lot of the issues you are facing, including the concatenation can be cleaned up using newer features from ES6 that are available in node 8.
In general, you should avoid doing string concatenations with the + operator and should use string literals instead. In your case, you should replace the "ECB_"+encryptedKey with `ECB_${encryptedKey}`.
Additionally, if you want to output the contents of the integers values from this concatenated string, then you are better off using .join, the spread operator (...) and the Buffer class from Node as follows:
let encKey = `ECB_${encryptedKey}`;
let tmpBuff = Buffer.from(encKey);
let buffArrVals = [...tmpBuff];
console.log(buffArrVals.join(','));
Also, if you can help it, you really should avoid using var inside of function blocks like it exists in your sample code. var performs something called variable hoisting and causes the variable to become available outside the scope it was declared, which is seldom intended. From node 6+ onward the recommendation is to use let or const for variable declarations to ensure they stay scoped to the block they are declared.
I've had a look around and can't seem to find a working solution, so here's the requirements.
I'm building a system that takes data from a master page and loads it into a modal style window for quick data processing. I have a javascript function passing 4 status parameters, and whilst 3 of them will always be integers, one of them can be integer or string.
The function works if all 4 parameters are integer, but fails when a string is passed.
function passJob(jobid,equipid,status,location) {
var a = document.getElementById('jobnumber');
var b = document.getElementById('equipid');
var c = document.getElementById('status');
var d = document.getElementById('location');
a.value = jobid;
b.value = equipid;
c.value = status;
d.value = location;
}
PHP
<a href='#' onclick='passJob($sr,$eid,$ss,$sl);'>Modify Job</a>
$sr, $ss and $sl will always be numeric, $eid will either be integer, or a string starting with M and then having a number after it.
I've tried adding quotes to the variables, around the variables, inside the function etc and no luck :(
You need to pass as string if you do not know what they are - also make sure you do not nest the same type of quote:
onclick='passJob($sr,"$eid",$ss,$sl);'
Just wrap it in quotes. This treats it like a string at all times to avoid any potential JavaScript parsing errors.
Modify Job
That is because you do not properly encode the variables in a Javascript notation. Try:
echo "<a href='#' onclick='passJob(".json_encode($sr).",".json_encode($eid).",".json_encode($ss).",".json_encode($sl).");'>Modify Job</a>";
Do like below
Modify Job
Can someone format the code below so that I can set srcript variables with c# code using razor?
The below does not work, i've got it that way to make is easy for someone to help.
#{int proID = 123; int nonProID = 456;}
<script type="text/javascript">
#{
<text>
var nonID =#nonProID;
var proID= #proID;
window.nonID = #nonProID;
window.proID=#proID;
</text>
}
</script>
I am getting a design time error
You should take a look at the output that your razor page is resulting. Actually, you need to know what is executed by server-side and client-side. Try this:
#{
int proID = 123;
int nonProID = 456;
}
<script>
var nonID = #nonProID;
var proID = #proID;
window.nonID = #nonProID;
window.proID = #proID;
</script>
The output should be like this:
Depending what version of Visual Studio you are using, it point some highlights in the design-time for views with razor.
Since razor syntax errors can become problematic while you're working on the view, I totally get why you'd want to avoid them. Here's a couple other options.
<script type="text/javascript">
// #Model.Count is an int
var count = '#Model.Count';
var countInt = parseInt('#Model.ActiveLocsCount');
</script>
The quotes act as delimiters, so the razor parser is happy. But of course your C# int becomes a JS string in the first statement. For purists, the second option might be better.
If somebody has a better way of doing this without the razor syntax errors, in particular maintaining the type of the var, I'd love to see it!
This is how I solved the problem:
#{int proID = 123; int nonProID = 456;}
<script type="text/javascript">
var nonID = Number(#nonProID);
var proID = Number(#proID);
</script>
It is self-documenting and it doesn't involve conversion to and from text.
Note: be careful to use the Number() function not create new Number() objects - as the exactly equals operator may behave in a non-obvious way:
var y = new Number(123); // Note incorrect usage of "new"
var x = new Number(123);
alert(y === 123); // displays false
alert(x == y); // displays false
I've seen several approaches to working around the bug, and I ran some timing tests to see what works for speed (http://jsfiddle.net/5dwwy/)
Approaches:
Direct assignment
In this approach, the razor syntax is directly assigned to the variable. This is what throws the error. As a baseline, the JavaScript speed test simply does a straight assignment of a number to a variable.
Pass through `Number` constructor
In this approach, we wrap the razor syntax in a call to the `Number` constructor, as in `Number(#ViewBag.Value)`.
ParseInt
In this approach, the razor syntax is put inside quotes and passed to the `parseInt` function.
Value-returning function
In this approach, a function is created that simply takes the razor syntax as a parameter and returns it.
Type-checking function
In this approach, the function performs some basic type checking (looking for null, basically) and returns the value if it isn't null.
Procedure:
Using each approach mentioned above, a for-loop repeats each function call 10M times, getting the total time for the entire loop. Then, that for-loop is repeated 30 times to obtain an average time per 10M actions. These times were then compared to each other to determine which actions were faster than others.
Note that since it is JavaScript running, the actual numbers other people receive will differ, but the importance is not in the actual number, but how the numbers compare to the other numbers.
Results:
Using the Direct assignment approach, the average time to process 10M assignments was 98.033ms. Using the Number constructor yielded 1554.93ms per 10M. Similarly, the parseInt method took 1404.27ms. The two function calls took 97.5ms for the simple function and 101.4ms for the more complex function.
Conclusions:
The cleanest code to understand is the Direct assignment. However, because of the bug in Visual Studio, this reports an error and could cause issues with Intellisense and give a vague sense of being wrong.
The fastest code was the simple function call, but only by a slim margin. Since I didn't do further analysis, I do not know if this difference has a statistical significance. The type-checking function was also very fast, only slightly slower than a direct assignment, and includes the possibility that the variable may be null. It's not really practical, though, because even the basic function will return undefined if the parameter is undefined (null in razor syntax).
Parsing the razor value as an int and running it through the constructor were extremely slow, on the order of 15x slower than a direct assignment. Most likely the Number constructor is actually internally calling parseInt, which would explain why it takes longer than a simple parseInt. However, they do have the advantage of being more meaningful, without requiring an externally-defined (ie somewhere else in the file or application) function to execute, with the Number constructor actually minimizing the visible casting of an integer to a string.
Bottom line, these numbers were generated running through 10M iterations. On a single item, the speed is incalculably small. For most, simply running it through the Number constructor might be the most readable code, despite being the slowest.
#{
int proID = 123;
int nonProID = 456;
}
<script>
var nonID = '#nonProID';
var proID = '#proID';
window.nonID = '#nonProID';
window.proID = '#proID';
</script>
One of the easy way is:
<input type="hidden" id="SaleDateValue" value="#ViewBag.SaleDate" />
<input type="hidden" id="VoidItem" value="#Model.SecurityControl["VoidItem"].ToString()" />
And then get the value in javascript:
var SaleDate = document.getElementById('SaleDateValue').value;
var Item = document.getElementById('VoidItem').value;
I found a very clean solution that allows separate logic and GUI:
in your razor .cshtml page try this:
<body id="myId" data-my-variable="myValue">
...your page code here
</body>
in your .js file or .ts (if you use typeScript) to read stored value from your view put some like this (jquery library is required):
$("#myId").data("my-variable")
Not so much an answer as a cautionary tale: this was bugging me as well - and I thought I had a solution by pre-pending a zero and using the #(...) syntax. i.e your code would have been:
var nonID = 0#(nonProID);
var proID = 0#(proID);
Getting output like:
var nonId = 0123;
What I didn't realise was that this is how JavaScript (version 3) represents octal/base-8 numbers and is actually altering the value. Additionally, if you are using the "use strict"; command then it will break your code entirely as octal numbers have been removed.
I'm still looking for a proper solution to this.
It works if you do something like this:
var proID = #proID + 0;
Which produces code that is something like:
var proID = 4 + 0;
A bit odd for sure, but no more fake syntax errors at least.
Sadly the errors are still reported in VS2013, so this hasn't been properly addressed (yet).
I've been looking into this approach:
function getServerObject(serverObject) {
if (typeof serverObject === "undefined") {
return null;
}
return serverObject;
}
var itCameFromDotNet = getServerObject(#dotNetObject);
To me this seems to make it safer on the JS side... worst case you end up with a null variable.
This should cover all major types:
public class ViewBagUtils
{
public static string ToJavascriptValue(dynamic val)
{
if (val == null) return "null";
if (val is string) return val;
if (val is bool) return val.ToString().ToLower();
if (val is DateTime) return val.ToString();
if (double.TryParse(val.ToString(), out double dval)) return dval.ToString();
throw new ArgumentException("Could not convert value.");
}
}
And in your .cshtml file inside the <script> tag:
#using Namespace_Of_ViewBagUtils
const someValue = #ViewBagUtils.ToJavascriptValue(ViewBag.SomeValue);
Note that for string values, you'll have to use the #ViewBagUtils expression inside single (or double) quotes, like so:
const someValue = "#ViewBagUtils.ToJavascriptValue(ViewBag.SomeValue)";
I use a very simple function to solve syntax errors in body of JavaScript codes that mixed with Razor codes ;)
function n(num){return num;}
var nonID = n(#nonProID);
var proID= n(#proID);
This sets a JavaScript var for me directly from a web.config defined appSetting..
var pv = '#System.Web.Configuration.WebConfigurationManager.AppSettings["pv"]';
With
var jsVar = JSON.parse(#Html.Raw(Json.Serialize(razorObject)));
you can parse any razor object into a JavaScript object.
It's long but universal
Can you tell me what I am missing in writing this code?
<button onclick="getBrowserName()">You Browser Name?</button>
<script>
function getBrowserName()
{
//Uses external interface to reach out to browser and grab browser useragent info.
var browserAgent:String = ExternalInterface.call("function getBrowser(){return navigator.userAgent;}");
//Determines brand of browser using a find index. If not found indexOf returns (-1).
if(browserAgent != null && browserAgent.indexOf("Firefox")>= 0)
{
alert("Firefox");
}
else if(browserAgent != null && browserAgent.indexOf("Safari")>= 0)
{
alert("Safari");
}
else if(browserAgent != null && browserAgent.indexOf("MSIE")>= 0)
{
alert("IE");
}
else if(browserAgent != null && browserAgent.indexOf("Opera")>= 0)
{
alert("Opera");
}
else
{
alert("Undefined");
}
return 0;
}
</script>
Well, there are a few things wrong here.
var browserAgent: String: it appears that you're using actionscript syntax, but JS uses dynamic typing, so var is all you need. There's no need to explicitly define the variable's data type, and if you try to do it this way in JS, it's going to give you syntax errors.
ExternalInterface.call: this is another carryover from ActionScript: you don't need this. In fact, it won't work at all because there's no ExternalInterface class in standard JS.
Your getBrowser() function is unnecessary. You're setting browserAgent equal to the result of calling a function from an ExternalInterface, but you can do this directly: var browserAgent = window.navigator.userAgent.
When I fixed those things, it worked fine.
Next time, I would recommend checking the browser console, because, if nothing is happening, the errors that appear there will help you solve your issue nine times out of ten.
Demo
If you replace this line
var browserAgent:String = ExternalInterface.call("function getBrowser(){return navigator.userAgent;}");
with this line:
var browserAgent = window.navigator.userAgent;
Then your script works fine on my side.
However, the criteria you use to test the engine are not precise. Have a look at this:
http://www.useragentstring.com/pages/useragentstring.php
There are many browsers that will tell you Firefox even if they another brand. But they are based on each other or they use a specific engine that is built in other browsers too.
If I use your script with a Chrome browser, it says "Safari" instead of "undefined".
About the punctuation: I know of only two places in Javascript where to use the double point:
the conditional operator a = b ? c : d;
the attribute - value assignment in object notation: { name : value }
Your code line containing :String = ExternalInterface... reminds me rather on ActionScript (?).
Im not quite sure what the follow code should be doing. Are you sure its correct?
var browserAgent:String =
ExternalInterface.call("function getBrowser(){return navigator.userAgent;}");
I would expect this code to simply look like this:
var browserAgent = navigator.userAgent;
Below is a example with this change.
http://jsbin.com/lukasere/1/edit