I have a web application using JavaScript on the client side and C# on the server side. I need to be able to generate sets pseudorandom numbers - given a shared seed - that are identical on the client and server sides. Is there a standard way of doing this? Obviously it's no good using the built-in PRNG libraries for the respective languages because their algorithms are going to differ.
I don't want to use a server endpoint to provide the client with the random numbers because my client application requires quick responses and that would add latency. It would work but wouldn't be ideal.
Here's a javascript implementation of the C version of xorshift128+. Per the wikipedia article, the xorshift128+ pseudo random number generator (PRNG) passes BigCrush (empirical randomness testing).
Note that xorshift128+ is used by major browsers when implementing Math.random, so it's a solid algorithm, although not crypto solid...
Note also that the local variables within method nextValue make use of a BigUint64Array, as this automatically reduces any intermediate results to 64 bits.
class XorShift128 {
#state;
static bitMask64 = ( 1n << 64n ) - 1n;
static bitMask32 = ( 1n << 32n ) - 1n
constructor( uint128Seed ) {
this.#state = new BigUint64Array( [ ( uint128Seed >> 64n ) & XorShift128.bitMask64, uint128Seed & XorShift128.bitMask64 ] );
}
get nextValue() {
let r = new BigUint64Array( this.#state );
// C version t = r[0], s = r[1]
this.#state[ 0 ] = r[ 1 ];
r[ 0 ] ^= r[ 0 ] << 23n;
r[ 0 ] ^= r[ 0 ] >> 18n;
r[ 0 ] ^= r[ 1 ] ^ ( r[ 1 ] >> 5n );
this.#state[ 1 ] = r[ 0 ];
return Number( ( r[ 0 ] + r[ 1 ] ) & XorShift128.bitMask32 );
}
}
console.log( `Set the seed to 0x8a5cd789635d2dff121fd2155c472f96n and generate 5 values...` );
let PRNG0 = new XorShift128( 0x8a5cd789635d2dff121fd2155c472f96n );
for ( let i = 0; i < 5; i++ ) {
console.log( PRNG0.nextValue );
}
console.log( `Let's do it again...` );
let PRNG1 = new XorShift128( 0x8a5cd789635d2dff121fd2155c472f96n );
for ( let i = 0; i < 5; i++ ) {
console.log( PRNG1.nextValue );
}
For completeness sake, here's the same algorithm in C.
//gcc 7.4.0
#include <stdint.h>
#include <stdio.h>
struct xorshift128p_state {
uint64_t x[2];
};
/* The state must be seeded so that it is not all zero */
uint64_t xorshift128p(struct xorshift128p_state *state)
{
uint64_t t = state->x[0];
uint64_t const s = state->x[1];
state->x[0] = s;
t ^= t << 23;
t ^= t >> 18;
t ^= s ^ (s >> 5);
state->x[1] = t;
return t + s;
}
main() {
struct xorshift128p_state seed;
seed.x[0] = 0x8a5cd789635d2dff;
seed.x[1] = 0x121fd2155c472f96;
uint64_t bitMask32 = ( 1ULL << 32 ) - 1ULL;
int i;
for ( i =0; i < 5; i++ ) {
uint64_t rv = xorshift128p( &seed );
printf( "%ld\n", rv & bitMask32 );
}
}
C Code Snippet
Add an API endpoint on the server in c# to return individual or an array of random numbers, then call that from the client.
calls to the server do not have to be inefficient, depending on your logic and the frequency of the call, it might even make sense to call this from the server, especially if consistency of the values is so important, this might be an X-Y Problem, what is the frequency of the calls, what is the highest latency that you can tolerate and what type of response times are you recording from your implementation.
Yes, there is a standard solution to problems like this (replicating logic in the client and the server) and that is to use the same random number generator algorithm and the same seed value. This is for instance how we can replay specific hands in card games.
A pure C# solution that allows you to use the same code in the client and the server would be to consider Blazor instead of javascript. Other solutions would be to implement your own algorithm or to find another one that has been ported to both C# and javascript.
So basically I've come to the conclusion that the only really reliable way I could get exactly the same PRNG on both the server and client side is to use the same language on both sides - for example C# using Blazor on the client-side and C#.NET on the server-side - and to create a class in that language that has an identical implementation for the server-side and client-side code, which would get shared.
A good class to copy/paste for such an implementation would probably be the XoshiroImpl random number generator class which seems to be the latest PRNG implementation being used by .NET core.
As it is I'm using JS for the client and C# for the server, so I had to solve the problem in a different way.
Someone recently asked if there was a simple way to transform custom markup as follows, including nested markings. Examples included...
for \k[hello] the output will be <b>hello</b>
for \i[world], the output will be <em>world</em>
for hello \k[dear \i[world]], the output will be hello <b>dear <em>world</em></b>
for \b[some text](url), the output will be <a href=”url”>some text</a>
for \r[some text](url), the output will be <img alt=”some text” src=”url” />
Interestingly enough, transpiling the above to javascript, including consideration for nesting, is remarkably straightforward, particularly if the markup grammar is consistent.
//
// Define the syntax and translation to javascript.
//
const grammar = {
syntax: {
k: {markUp: `\k[`, javascript: `"+grammar.oneArg("k","`, pre: `<b>`, post: `</b>`},
i: {markUp: `\i[`, javascript: `"+grammar.oneArg("i","`, pre: `<em>`, post: `</em>`},
b: {markUp: `\b[`, javascript: `"+grammar.twoArgs("b","`, pattern: `$1`},
r: {markUp: `\r[`, javascript: `"+grammar.twoArgs("r","`, pattern: `<img alt="$1" src="$2"/>`},
close0: {markUp: `](`, javascript: `","`},
close1: {markUp: `)`, javascript: `")+"`},
close2: {markUp: `]`, javascript: `")+"`}
},
oneArg: function( command, arg1 ) {
return grammar.syntax[ command ].pre + arg1 + grammar.syntax[ command ].post;
},
twoArgs: function( command, arg1, arg2 ) {
return grammar.syntax[ command ].pattern.split( `$1` ).join( arg1 ).split( `$2` ).join( arg2 );
}
}
function transpileAndExecute( markUpString ) {
// Convert the markUp to javascript.
for ( command in grammar.syntax ) {
markUpString = markUpString.split( grammar.syntax[ command ].markUp ).join( grammar.syntax[ command ].javascript );
}
// With the markUp now converted to javascript, let's execute it!
return new Function( `return "${markUpString}"` )();
}
var markUpTest = `Hello \k[dear \i[world!]] \b[\i[Search:] \k[Engine 1]](http://www.google.com) \r[\i[Search:] \k[Engine 2]](http://www.yahoo.com)`;
console.log( transpileAndExecute( markUpTest ) );
Note that there are obviously pre-processing issues that must also be addressed, such as how to handle the inclusion of tokens in normal text. Eg, including a ']' as part of a text string will throw the transpiler a curve ball, so enforcing a rule such as using '\]' to represent a ']', and then replacing all such occurrences of '\]' with innocuous text before transpiling and then re-replacing afterwards solves this problem simply...
In terms of transpiling, using the grammar defined above, the following markup...
Hello \k[dear \i[world!]] \b[\i[Search:] \k[Engine 1]](http://www.google.com) \r[\i[Search:] \k[Engine 2]](http://www.yahoo.com)
...is transpiled to...
"Hello world! "+grammar.oneArg("k","dear "+grammar.oneArg("i","world")+"")+" "+grammar.twoArgs("b",""+grammar.oneArg("i","Search:")+" "+grammar.oneArg("k","Engine 1")+"","http://www.google.com")+" "+grammar.twoArgs("r",""+grammar.oneArg("i","Search:")+" "+grammar.oneArg("k","Engine 2")+"","http://www.yahoo.com")+""
...and once executed as a javascript Function, results in...
Hello <b>dear <em>world!</em></b> <em>Search:</em> <b>Engine 1</b> <img alt="<em>Search:</em> <b>Engine 2</b>" src="http://www.yahoo.com"/>
The real challenge though is the handling of syntax errors, particularly if one has large amounts of markup to transpile. The crystal clear answer by CertainPerformance (see Find details of SyntaxError thrown by javascript new Function() constructor ) provides a means of capturing the line number and character number of a syntax error from a dynamically compiled javascript function, but am not quite sure of the best means of mapping a syntax error of the transpiled code back to the original markup.
Eg, if an extra ']' is out of place (after "Goodbye")...
Hello World! \b[\i[Goodbye]]] \k[World!]]
...this transpiles to...
"Hello World! "+grammar.twoArgs("b",""+grammar.oneArg("i","Goodbye")+"")+"")+" "+grammar.oneArg("k","World!")+"")+""
^
...and CertainPerformance's checkSyntax function returns "Error thrown at: 1:76", as expected, marked above with the "^".
The question is, how to map this back to the original markup to aid in narrowing down the error in the markup? (Obviously in this case, it's simple to see the error in the markup, but if one has pages of markup being transpiled, then assistance in narrowing down the syntax error is a must.) Maintaining a map between the markup and the transpiled code seems tricky, as the transpiler is mutating the markup to javascript code step-by-step as it walks the grammar transformation matrix. My gut tells me there's a simpler way... Thanks for looking.
I would suggest you write a syntax checker, kinda like jsonlint or jslint etc... that checks if everything is checked and closed properly, before actually compiling the text to human readable text.
This allows for debugging, and prevents from malformed code running haywire, and allows you to provide an error highlighted document editor when they are editing the text.
Below a proof of concept which just checks if brackets are closed properly.
var grammarLint = function(text) {
var nestingCounter = 0;
var isCommand = char => char == '\\';
var isOpen = char => char == '[';
var isClose = char => char == ']';
var lines = text.split('\n');
for(var i = 0; i < lines.length; i++) {
text = lines[i];
for(var c = 0; c < text.length; c++) {
var char = text.charAt(c);
if(isCommand(char) && isOpen(text.charAt(c+2))) {
c += 2;
nestingCounter++;
continue;
}
if(isClose(char)) {
nestingCounter--;
if(nestingCounter < 0) {
throw new Error('Command closed but not opened at on line '+(i+1)+' char '+(c+1));
}
}
}
}
if(nestingCounter > 0) {
throw new Error(nestingCounter + ' Unclosed command brackets found');
}
}
text = 'Hello World! \\b[\\i[Goodbye]]] \\k[World!]]';
try {
grammarLint(text);
}
catch(e) {
console.error(e.message);
}
text = 'Hello World! \\b[\\i[Goodbye \\k[World!]]';
try {
grammarLint(text);
}
catch(e) {
console.error(e.message);
}
Chased down the ability to leverage the javascript compiler to capture syntax errors in the transpiled code, and reference this back to the original markup. In short, this involves a scheme of incorporating comments in the transpiled code to permit references back to the markup, providing the means of narrowing down the markup error. (There is a bit of shortcoming in that the error message is really a transpiler syntax error, and doesn't necessarily correspond exactly to the markup error, but gives one a fighting chance to figure out where the markup issue lies.)
This algorithm also leverages the concepts of CertainPerformance's technique ( Find details of SyntaxError thrown by javascript new Function() constructor ) of using setTimeout to capture the syntax errors of the transpiled code. I have interspersed a javascript Promise to smooth the flow.
"use strict";
//
// Define the syntax and translation to javascript.
//
class Transpiler {
static _syntaxCheckCounter = 0;
static _syntaxCheck = {};
static _currentSyntaxCheck = null;
constructor() {
this.grammar = {
syntax: {
k: {markUp: `\k[`, javascript: `"►+grammar.oneArg("k",◄"`, pre: `<b>`, post: `</b>`},
i: {markUp: `\i[`, javascript: `"►+grammar.oneArg("i",◄"`, pre: `<em>`, post: `</em>`},
b: {markUp: `\b[`, javascript: `"►+grammar.twoArgs("b",◄"`, pattern: `$1`},
r: {markUp: `\r[`, javascript: `"►+grammar.twoArgs("r",◄"`, pattern: `<img alt="$1" src="$2"/>`},
close0: {markUp: `](`, javascript: `"►,◄"`},
close1: {markUp: `)`, javascript: `"►)+◄"`},
close2: {markUp: `]`, javascript: `"►)+◄"`}
},
marker: { // https://www.w3schools.com/charsets/ref_utf_geometric.asp
begMarker: `►`, // 25ba
endMarker: `◄`, // 25c4
begComment: `◆`, // 25c6
endComment: `◇`, // 25c7
fillerChar: `●` // 25cf
},
oneArg: function( command, arg1 ) {
return this.syntax[ command ].pre + arg1 + this.syntax[ command ].post;
},
twoArgs: function( command, arg1, arg2 ) {
return this.syntax[ command ].pattern.split( `$1` ).join( arg1 ).split( `$2` ).join( arg2 );
}
};
};
static transpilerSyntaxChecker(err) {
// Uncomment the following line to disable default console error message.
//err.preventDefault();
let transpiledLine = Transpiler._syntaxCheck[ Transpiler._currentSyntaxCheck ].transpiledFunction.split(`\n`)[1];
let lo = parseInt( transpiledLine.substr( transpiledLine.substr( 0, err.colno ).lastIndexOf( `●` ) + 1 ) );
let hi = parseInt( transpiledLine.substr( transpiledLine.substr( err.colno ).indexOf( `●` ) + err.colno + 1 ) );
let markUpLine = Transpiler._syntaxCheck[ Transpiler._currentSyntaxCheck ].markUp;
let errString = markUpLine.substring( lo - 40, hi + 40 ).split(`\n`).join(`↵`) + `\n`;
errString += ( `.`.repeat( lo ) + `^`.repeat( hi - lo ) ).substring( lo - 40, hi + 40 );
Transpiler._syntaxCheck[Transpiler._currentSyntaxCheck].rejectFunction( new Error(`'${ err.message }' in transpiled code, corresponding to character range ${ lo }:${ hi } in the markup.\n${ errString }`) );
window.removeEventListener('error', Transpiler.transpilerSyntaxChecker);
delete Transpiler._syntaxCheck[Transpiler._currentSyntaxCheck];
};
async transpileAndExecute( markUpString ) {
// Convert the markUp to javascript.
console.log( markUpString );
let gm = this.grammar.marker;
let markUpIndex = markUpString;
let transpiled = markUpString;
for ( let n in this.grammar.syntax ) {
let command = this.grammar.syntax[ n ];
let markUpIndexSplit = markUpIndex.split( command.markUp );
let transpiledSplit = transpiled.split( command.markUp );
if ( markUpIndexSplit.length !== transpiledSplit.length ) {
throw `Ambiguous grammar when searching for "${ command.markUp }" to replace with "${ command.javascript }".`;
}
for ( let i = 0; i < markUpIndexSplit.length; i++ ) {
if ( i === 0 ) {
markUpIndex = markUpIndexSplit[ 0 ];
transpiled = transpiledSplit[ 0 ];
} else {
let js = command.javascript.replace( gm.begMarker, gm.begComment + gm.fillerChar + markUpIndex.length + gm.endComment );
markUpIndex += gm.fillerChar.repeat( command.markUp.length );
js = js.replace( gm.endMarker, gm.begComment + gm.fillerChar + markUpIndex.length + gm.endComment );
markUpIndex += markUpIndexSplit[ i ];
transpiled += js + transpiledSplit[ i ];
}
}
};
transpiled = transpiled.split( gm.begComment ).join( `/*` );
transpiled = transpiled.split( gm.endComment ).join( `*/` );
transpiled = `/*${ gm.fillerChar }0*/"${ transpiled }"/*${ gm.fillerChar }${ markUpIndex.length + 1 }*/`;
console.log( markUpIndex );
console.log( transpiled );
let self = this;
var id = ++Transpiler._syntaxCheckCounter;
Transpiler._syntaxCheck[id] = {};
let transpiledFunction = `"use strict"; if ( run ) return\n${ transpiled.split(`\n`).join(` `) }`;
Transpiler._syntaxCheck[id].markUp = markUpString;
Transpiler._syntaxCheck[id].transpiledFunction = transpiledFunction;
//
// Here's where it gets tricky. (See "CertainPerformance's" post at
// https://stackoverflow.com/questions/35252731
// for details behind the concept.) In this implementation a Promise
// is created, which on success of the JS compiler syntax check, is resolved
// immediately. Otherwise, if there is a syntax error, the transpilerSyntaxChecker
// routine, which has access to a reference to the Promise reject function,
// calls the reject function to resolve the promise, returning the error back
// to the calling process.
//
let checkSyntaxPromise = new Promise((resolve, reject) => {
setTimeout( () => {
Transpiler._currentSyntaxCheck = id;
window.addEventListener('error', Transpiler.transpilerSyntaxChecker);
// Perform the syntax check by attempting to compile the transpiled function.
new Function( `grammar`, `run`, transpiledFunction )( self.grammar );
resolve( null );
window.removeEventListener('error', Transpiler.transpilerSyntaxChecker);
delete Transpiler._syntaxCheck[id];
});
Transpiler._syntaxCheck[id].rejectFunction = reject;
});
let result = await checkSyntaxPromise;
// With the markUp now converted to javascript and syntax checked, let's execute it!
return ( new Function( `grammar`, `run`, transpiledFunction.replace(`return\n`,`return `) )( this.grammar, true ) );
};
}
Here are some sample runs with botched markup, and the corresponding console output. The following markup has an extra ]...
let markUp = `Hello World \k[Goodbye]] World`;
new Transpiler().transpileAndExecute( markUp ).then(result => console.log( result )).catch( err => console.log( err ));
...resulting in transpiled code of...
/*●0*/""/*●0*/+grammar.oneArg("i",/*●2*/"Hello World"/*●13*/)+/*●14*/" "/*●15*/+grammar.oneArg("k",/*●17*/""/*●17*/+grammar.oneArg("i",/*●19*/"Goodbye"/*●26*/)+/*●27*/" World"/*●34*/
Note the interspersed comments, which point back to the character position in the original markup. Then, when the javascript compiler throws an error, it is trapped by transpilerSyntaxChecker which uses the embedded comments to identify the location in the markup, dumping the following results to the console...
Uncaught SyntaxError: Unexpected token )
at new Function (<anonymous>)
at markUp.html:127
Error: 'Uncaught SyntaxError: Unexpected token )' in transpiled code, corresponding to character range 22:23 in the markup.
Hello World k[Goodbye]] World
......................^
at transpilerSyntaxChecker (markUp.html:59)
Note that the Unexpected token ) message refers to the transpiled code, not the markup script, but the output points to the offending ].
Here's another sample run, in this case missing a close ]...
let markUp = `\i[Hello World] \k[\i[Goodbye] World`;
new Transpiler().transpileAndExecute( markUp ).then(result => console.log( result )).catch(err => console.log( err ));
...which produces the following transpiled code...
/*●0*/""/*●0*/+grammar.oneArg("i",/*●2*/"Hello World"/*●13*/)+/*●14*/" "/*●15*/+grammar.oneArg("k",/*●17*/""/*●17*/+grammar.oneArg("i",/*●19*/"Goodbye"/*●26*/)+/*●27*/" World"/*●34*/
...throwing the following error...
Uncaught SyntaxError: missing ) after argument list
at new Function (<anonymous>)
at markUp.html:127
Error: 'Uncaught SyntaxError: missing ) after argument list' in transpiled code, corresponding to character range 27:34 in the markup.
i[Hello World] k[i[Goodbye] World
...........................^^^^^^^
at transpilerSyntaxChecker (markUp.html:59)
Maybe not the best solution, but a lazy man's solution. Tschallacka's response has merit (ie, a custom syntax checker or using something like Jison) in performing a true syntax check against the markup, without the setTimeout / Promise complexities nor the somewhat imprecise method of using the transpiler error messages to refer to the original markup...
I'm trying to create a Script or Macro that simply checks my HP, and then attacks or heals. My macros work, I just can't get them to play with javascript. HP Test.iim Extracts the HP from the web page. I want to take that Extracted number and make it a variable. I'm new to scripting, maybe I can get some help.
New script based on code below (thanks for that)
ten = 1
while (ten = 1) {
iimPlay("Hunt/HP Test.iim");
var extract = iimGetLastExtract ();
var HP=extract.split("/")[0];
HP=parseInt(HP);
iimSet ( "HP", HP )
if ( HP >= 1 )
{
iimPlay ("Hunt/Attack.iim")
}
else
{
iimPlay ("Hunt/Heal.iim")
}
}
IT Works! Thanks much, I've been working on that for a long time!
iimPlay("HP Test.iim");
var extract = iimGetLastExtract ();
iimSet ( "HP", extract )
if ( extract >= 1 )
{
iimPlay ("Attack.iim")
}
else
{
iimPlay ("Heal.iim")
}
This code might not work but you will get an idea how the syntax works.
Edit: example of methods >>> http://jsfiddle.net/KRV5a/
I am trying to write an Ant <scriptfilter...> to change occurrences of the string "__LINE__" to the correct line number in Java source files.
Does anyone have an example of using JavaScript (or some other embedded scripting language) to do this? In particular, how do I create a "global" variable that is initialized to 1 when the script starts and is incremented with each new line?
Thanks.
UPDATE: Just tried the solution offered by Martin Clayton (thanks!), replacing the JavaScript with Beanshell, and it worked perfectly. Here is the Ant target code:
<target name="preprocess" depends="ivy.resolve" description="Preprocess the source code">
<mkdir dir="${target.source.dir}"/>
<copy todir="${target.source.dir}" includeemptydirs="true" failonerror="true" verbose="true">
<fileset dir="${src.dir}"/>
<filterchain>
<tokenfilter>
<filetokenizer/>
<scriptfilter language="beanshell" byline="true" setbeans="true"><![CDATA[
import java.io.BufferedReader;
import java.io.StringReader;
int count = 1;
BufferedReader br = new BufferedReader(new StringReader(self.getToken()));
StringBuilder builder = new StringBuilder();
String line;
while ((line = br.readLine()) != null) {
builder.append(line.replace("\"__LINE__\"", Integer.toString(count))).append('\n');
count++;
}
self.setToken(builder.toString());
]]></scriptfilter>
</tokenfilter>
</filterchain>
</copy>
</target>
You could use an ant property to hold the 'static'.
Here's a very simplified example, for one file.
<property name="lineNumber" value="0" />
<copy file="input.txt" tofile="output.txt" >
<filterchain>
<scriptfilter language="javascript">
project.setProperty( "lineNumber",
parseInt( project.getProperty( "lineNumber" ) ) + 1 );
if ( self.getToken().indexOf("__LINE__") != -1 ) {
lineNumber = project.getProperty( "lineNumber" );
self.setToken( self.getToken( ).replaceAll( "__LINE__", lineNumber ) );
}
</scriptfilter>
</filterchain>
</copy>
The problem is: that doesn't extend to multiple files - the lineNumber doesn't reset to one between files.
You might use a filetokenizer to get the whole file into javascript in one go, then process the file line-by-line.
Here's a very noddy example (I know enough javascript to be dangerous). I'm sure this has lots of faults (not least: it doesn't handle non-newline terminated files; shocking string catenations).
But the principle is that by getting each whole file into the script, you don't need any information to persist between script invocations.
<copy todir="output">
<fileset dir="input"/>
<filterchain>
<tokenfilter>
<filetokenizer/>
<scriptfilter language="javascript"><![CDATA[
// Get the whole input file to one string.
inputContent = self.getToken( );
lineNum = 1;
fileEnd = inputContent.length( );
// Build the new file up line-by-line in this var.
outputContent = "";
lineStart = 0;
lineEnd = inputContent.indexOf( "\n" );
while ( lineEnd < fileEnd ) {
outputContent += inputContent
.substring( lineStart, lineEnd )
.replaceAll( "__LINE__", lineNum ) + "\n";
lineStart = lineEnd + 1;
fc = inputContent.substring( lineStart );
lineEnd = fc.indexOf( "\n" );
if ( lineEnd == -1 )
break;
lineEnd += lineStart;
lineNum++;
}
self.setToken( outputContent );
]]></scriptfilter>
</tokenfilter>
</filterchain>
</copy>
I'm trying to build a POC to migrate a heavy JSF application to a stateless ajax/restful application .
In the proccess i can't decide what is the best way of presenting the JSON data returned to the screen , i can see 2 major approaches one is to have templates and use something like prototype's toHTML() with them and the other is to build the objects in javascript and then use appendchild .
the first one is much more easy to understand for a new person who has to maintain the code as the templates are very clear and easier to maintain (allso the skills needed to change the html in templates are lower) but from what i understand the appendchild method is better in regards to browser speed .
what is the preferable way to handle this and am i missing other points of comparison between the two ?
append child is this a good compromise between the two ?
are there any other ways to do this ?
P.S : to be clear i'm talking about client side manipulations only
Setting html directly with innerHTML is the fastest way cross-browser. It has some bugs, however, that you should keep in mind (tables, forms, etc.).
var html = [];
for (...) {
html.push( PARTIAL_HTML );
}
element.innerHTML = html.join("");
UPDATE: The best way may be to test it for yourself:
function test( name, fn, n, next ) {
var n = n || 100; // default number of runs
var start, end, elapsed;
setTimeout(function() {
start = Number(new Date());
for ( ; n--; ) {
fn()
}
end = Number(new Date());
elapsed = end - start;
// LOG THE RESULT
// can be: $("#debug").html(name + ": " + elapsed + " ms");
console.log(name + ": " + elapsed + " ms"));
next && next();
}, 0);
}
test("dom", function() {
// ...
});
test("innerHTML", function() {
// ...
});