﻿var BrowserDetect = {
    init: function() {
        this.browser = this.searchString(this.dataBrowser) || "An unknown browser";
        this.version = this.searchVersion(navigator.userAgent)
			|| this.searchVersion(navigator.appVersion)
			|| "an unknown version";
        this.OS = this.searchString(this.dataOS) || "an unknown OS";
    },
    searchString: function(data) {
        for (var i = 0; i < data.length; i++) {
            var dataString = data[i].string;
            var dataProp = data[i].prop;
            this.versionSearchString = data[i].versionSearch || data[i].identity;
            if (dataString) {
                if (dataString.indexOf(data[i].subString) != -1)
                    return data[i].identity;
            }
            else if (dataProp)
                return data[i].identity;
        }
    },
    searchVersion: function(dataString) {
        var index = dataString.indexOf(this.versionSearchString);
        if (index == -1) return;
        return parseFloat(dataString.substring(index + this.versionSearchString.length + 1));
    },
    dataBrowser: [
		{
		    string: navigator.userAgent,
		    subString: "Chrome",
		    identity: "Chrome"
		},
		{ string: navigator.userAgent,
		    subString: "OmniWeb",
		    versionSearch: "OmniWeb/",
		    identity: "OmniWeb"
		},
		{
		    string: navigator.vendor,
		    subString: "Apple",
		    identity: "Safari",
		    versionSearch: "Version"
		},
		{
		    prop: window.opera,
		    identity: "Opera"
		},
		{
		    string: navigator.vendor,
		    subString: "iCab",
		    identity: "iCab"
		},
		{
		    string: navigator.vendor,
		    subString: "KDE",
		    identity: "Konqueror"
		},
		{
		    string: navigator.userAgent,
		    subString: "Firefox",
		    identity: "Firefox"
		},
		{
		    string: navigator.vendor,
		    subString: "Camino",
		    identity: "Camino"
		},
		{		// for newer Netscapes (6+)
		    string: navigator.userAgent,
		    subString: "Netscape",
		    identity: "Netscape"
		},
		{
		    string: navigator.userAgent,
		    subString: "MSIE",
		    identity: "Explorer",
		    versionSearch: "MSIE"
		},
		{
		    string: navigator.userAgent,
		    subString: "Gecko",
		    identity: "Mozilla",
		    versionSearch: "rv"
		},
		{ 		// for older Netscapes (4-)
		    string: navigator.userAgent,
		    subString: "Mozilla",
		    identity: "Netscape",
		    versionSearch: "Mozilla"
		}
	],
    dataOS: [
		{
		    string: navigator.platform,
		    subString: "Win",
		    identity: "Windows"
		},
		{
		    string: navigator.platform,
		    subString: "Mac",
		    identity: "Mac"
		},
		{
		    string: navigator.userAgent,
		    subString: "iPhone",
		    identity: "iPhone/iPod"
		},
		{
		    string: navigator.platform,
		    subString: "Linux",
		    identity: "Linux"
		}
	]

};
BrowserDetect.init();


var test_scripts = new Array();

var client = new XMLHttpRequest();
client.onreadystatechange = state_changed;

var testIndex = 0;
var repeatCount;
var resultsExpected;

var threadsActive;
var threadsTarget;

var threads;
var runningWithEval = false;

var finished = false;

var output;
var start_date_time;

function begin_loading() {
    load_next_test();
}

function load_next_test() {
    if (test_scripts.length < tests.length) {
        client.open("GET", tests[test_scripts.length] + ".html.js");
        client.send();
    } else {
        enable_start_button();
    }
}

function enable_start_button() {
    document.getElementById("RunTestsButton").value = "Run Tests";
    document.getElementById("RunTestsButton").disabled = false;
    document.getElementById("runGrid").innerHTML = "";
}

function state_changed() {
    if (this.readyState == 4) {
        if ((this.status == 200) && (this.responseText != null)) {
            test_scripts.push(this.responseText.replace("importScripts('worker.js');", ""));
            setTimeout(load_next_test, 0);
        }
        else {
            alert("There was an error loading the script " + this.channel.name);
        }
    }
}

function generate_rungrid_array() {
    var table = "<table>";
    for (var test in tests) {
        if (test != "toJSONString") {
            table += "<tr><td>" + tests[test] + "</td>";
            for (var c = 0; c < repeatCount; c++) {
                table += "<td><input disabled=\"disabled\" type=\"checkbox\" id=\"cb_" + test + "_" + c + "\" /></td>";
            }
            table += "</tr>";
        }
    }
    table += "</table>";
    return table;
}

function start_tests() {
    document.getElementById("RunTestsButton").value = "Tests Running";
    document.getElementById("RunTestsButton").disabled = true;
    document.getElementById("resultsDiv").innerHTML = "";

    var repeatCountSelect = document.getElementById("RepeatCountSelect");
    var threadCountSelect = document.getElementById("ThreadCountSelect");

    runningWithEval = document.getElementById("UseEval").checked;

    testIndex = 0;
    repeatCount = repeatCountSelect.options[repeatCountSelect.selectedIndex].value;
    resultsExpected = tests.length * repeatCount;

    threadsActive = 0;
    threadsTarget = threadCountSelect.options[threadCountSelect.selectedIndex].value;

    if (threadsTarget == 0) {
        document.getElementById("UseEval").checked = true;
        runningWithEval = true;
    }
    threads = new Array();
    threads.length = threadsTarget;

    finished = false;

    output = new Array();
    output.length = repeatCount;

    for (var i = 0; i < output.length; i++) {
        output[i] = new Array();
        output[i].length = tests.length;
    }

    document.getElementById("runGrid").innerHTML = generate_rungrid_array();
    start_date_time = new Date();

    if (threadsTarget == 0) {
        run_tests_synchronous();
    } else {
        var init = function() {
            fill_thread_pool();
        };
        init();
    }
}

function run_tests_synchronous() {
    for (tc = 0; tc < tests.length; tc++) {
        eval("function record(time) { \
                var resultObject = new Object; \
                resultObject.testIndex = tc; \
                resultObject.sampleIndex = rc+1; \
                resultObject.time = time; \
                thread_callback(resultObject, true); \
            }");

        eval(test_scripts[tc]);

        for (rc = 0; rc < repeatCount; rc++) {
            testFunction();
        }
        delete Array.prototype.toJSONString;
        delete Array.toJSONString;

        testFunction = null;

    }

    finish();
}

function fill_thread_pool() {
    var startThreadsActive = threadsActive;
    while ((startThreadsActive < threadsTarget) && (testIndex < tests.length)) {
        launch_thread(testIndex++);
        startThreadsActive++;
    }
    if ((startThreadsActive == 0) && (finished == false)) {
        finished = true;
        finish();
    }
}

function thread_callback(results, nonThread) {
    var target = document.getElementById("cb_" + results.testIndex + "_" + (results.sampleIndex - 1));
    if (target != null) {
        target.checked = true;
    }

    output[results.sampleIndex - 1][results.testIndex] = results.time;
    if (nonThread != true) {
        if (results.threadCompleted == true) {
            threadsActive--;

            threads[results.testIndex].terminate();
            threads[results.testIndex] = null;
        }
        fill_thread_pool();
    }
}

function launch_thread(testIndex) {
    threadsActive++;

    var parameters = new Object();
    parameters.testIndex = testIndex;
    parameters.repeatCount = repeatCount;

    var newWorker;

    if (runningWithEval) {
        parameters.scriptBlock = test_scripts[testIndex];
        newWorker = new Worker("worker.js");
    } else {
        newWorker = new Worker(tests[testIndex] + ".html.js");
    }

    newWorker.onmessage = function(event) {
        thread_callback(JSON.parse(event.data));
    };

    threads[testIndex] = newWorker;
    newWorker.postMessage(JSON.stringify(parameters));

}

function finish() {
    var outputString = "{";
    for (var testIterate = 0; testIterate < tests.length; testIterate++) {
        outputString += '"' + tests[testIterate] + '":[';
        for (var i = 0; i < output.length; i++) {
            outputString += output[i][testIterate] + ",";
        }
        outputString = outputString.substring(0, outputString.length - 1);
        outputString += "],";
    }
    outputString = outputString.substring(0, outputString.length - 1);
    outputString += "}";

    var results = pivot(eval("(" + outputString + ")"));

    function pivot(input) {
        var output = [];
        for (var test = 0; test < tests.length; test++) {
            for (var i = 0; i < input[tests[test]].length; i++) {
                if (!output[i])
                    output[i] = {};
                output[i][tests[test]] = input[tests[test]][i];
            }
        }
        return output;
    }

    var processing = new analyzer(results, new Date() - start_date_time);
    document.getElementById("resultsDiv").innerHTML = "<PRE>" + threadsTarget + " threads running " + repeatCount + " cycles per test.\r\r" + processing.generateResults() + "</PRE>";

    enable_start_button();

}