/*
    The JSON file format expected by this script is as follows:
    [
        [ "load finish order", "load start order", "measurements", "mimetype", "url", "compressed", "filesize dl bytes", "HTTP status" ],
        [...],
        [...],
        ...
    ]
*/

// The PHP JS namespace.
var $PHP = new PHP_JS();

// The table object.
var tableObj = null;

// List of the individual bar parts.
var barNames = ["offset", "queue", "dns", "connect", "redirect", "ttfb", "download", "load"]

// List of the individual bar parts.
var barDesc = ["Offset", "Time in queue", "DNS lookup time", "Connection time", "Redirect time", "Time to first byte", "Download time", "Load time"]

// Current time scale resolution.
var timeScaleResolution = 60000;

// Max time so far.
var timeScaleMaxTimeMs = 0;

// Variable keeping track of our result offset when live updating the table.
var liveRowOffset = 0;

// The public ID of the current analysis.
var currentPublicID = "";

// Check whether URL to analyze is valid.
function isValidUrl() {
	// TODO: Opera refuse to pass anything with our regexp
    // RG 090811: I suspect this could be because of the dashes in the middle of
    //            the range clauses which some regex engines seem to confuse for
    //            a range indicator!
	if (navigator.userAgent.search(/opera/i) != -1) {
		return true;
    }
	var r = new RegExp();
	r.compile(/^(http(s)?:\/\/)?([a-z0-9-_\.:@]+\.)*(([a-z0-9-])+(\.)([a-z0-9]){1,4})+(\/[a-z0-9_\.~%&\?\/=\+-\[\]\|:#,;@\$]*)*$/i);
	if (r.test($('#inp-test').val().toLowerCase())) {
		return true;
	} else {
		return false;
	}
};

// Submit URL for analysis.
function analyze() {
    // Enable working status indicators.
    $("#inp-test").attr("disabled", true);
    $("#inp-useragent").attr("disabled", true);
    if ($("#pageanalyzer-settings").is(":visible")) {
        $("#pageanalyzer-settings").hide();
        $("#pageanalyzer-settings-button").css("border-bottom", "1px solid #ccc");
    }
    $("#inp-advanced").html('Advanced');
    $("#btn-start-test").hide();
    $("#pageanalyzer-analysis-summary").hide();
    $("#errormsg").html("");
    $("#errormsg").hide();
    $("#pageanalyzer-loadtest").html("");
    $("#pageanalyzer-workingstatus").html("<img src=\"/images/loader-f2.gif\" style=\"padding-top:10px;\" />");
    $("#pageanalyzer-status").css("color", "black");
    $("#pageanalyzer-status").html("- Submitting");

    // Reset table.
    if (tableObj != null) {
        tableObj.fnClearTable();
        liveRowOffset = 0;
        timeScaleMaxTimeMs = 0;
    }

    // Post URL to analyzer queue.
    $.post("/ajax/pageanalyzer.php", { 
            action: 'queue',
            url: $('#inp-test').val(),
            useragent: $('#inp-useragent').val(),
            username: $('#inp-username').val(),
            password: $('#inp-password').val(),
            maxdlspeed: $('#inp-maxdlspeed').val()
        },
        function(msg) {
            var ret = msg.split(':::');
            if (ret[0] == 'OK') {
                $("#pageanalyzer-container").show();
                if (tableObj == null) {
                    initPageAnalyzerTable("#pageanalyzer", ret[1], true);
                } else {
                    tableObj.fnLiveAjaxUpdate(ret[1]);
                }
                $("#pageanalyzer-url").html(ret[2]);
                $("#pageanalyzer-url").attr("href", ret[2]);
                $("#pageanalyzer-status").html("- Queued");
                $("#pageanalyzer .dataTables_empty").html("Waiting for analysis...");
            } else if (ret[0] == 'ERROR') {
                $("#pageanalyzer-container").hide();
                $("#pageanalyzer-workingstatus").html("");
                $("#btn-start-test").show();
                $("#inp-test").removeAttr("disabled");
                $("#inp-useragent").removeAttr("disabled");
                addErrorMessage(ret[1], true);
            }

            // See if we should clear auth settings.
            if ($("#inp-clearauth").is(':checked')) {
                $("#inp-username").val("");
                $("#inp-password").val("");
            }
        },
        "text"
    );
};

// Add a message to the error message div.
function addErrorMessage(msg, show) {
    var html = $("#errormsg").html();
    if (html == "") {
        $("#errormsg").html(msg);
    } else {
        $("#errormsg").html(html + "<br />" + msg);
    }
    if (show) {
        $("#errormsg").show();
    }
};

// Return a humanized percent representation string.
function humanizePercent(sub, total) {
    sub = parseInt(sub);
    total = parseInt(total);
    if (sub == 0 || total == 0) {
        return "0%"
    }
    return (sub / total * 100.0).toFixed(2) + "%";
};

// Return a humanized size representation string.
function humanizeSize(sizeInBytes) {
    sizeInBytes = parseInt(sizeInBytes);
    if (sizeInBytes > 1073741824) {
        return (sizeInBytes / 1073741824).toFixed(2) + " GiB";
    }
    else if (sizeInBytes > 1048576) {
        return (sizeInBytes / 1048576).toFixed(2) + " MiB";
    }
    else if (sizeInBytes > 1024) {
        return (sizeInBytes / 1024).toFixed(2) + " KiB";
    }
    else {
        return sizeInBytes + " B";
    }
};

// Return a humanized size representation string.
function humanizeTime(timeInMs) {
    timeInMs = parseInt(timeInMs);
    if (timeInMs > 1000) {
        return (timeInMs / 1000.0).toFixed(2) + " s"
    } else {
        return timeInMs + " ms";
    }
};

// Function to get user-agent settings information.
function getUserAgentInfo() {
    var uaid = $("#inp-useragent").val();
    $("#pageanalyzer-settings-ua-content").hide();
    $("#pageanalyzer-settings-ua-workingstatus").html("<img src=\"/images/loader-f2.gif\" style=\"padding-top:10px;\" />");
	$.getJSON('/ajax/pageanalyzer.php?action=uainfo&ua=' + uaid, null,
        function(json) {
            if (json.info != "ERROR") {
                // Set user-agent information.
                $("#pageanalyzer-settings-useragent").html(json.info.user_agent_string);
                $("#pageanalyzer-settings-maxtotcons").html(json.info.max_total_cons);
                $("#pageanalyzer-settings-maxpersconsperhost").html(json.info.max_persistent_cons_per_host);
                $("#pageanalyzer-settings-defkato").html(json.info.default_keep_alive_timeout);
                $("#pageanalyzer-settings-acccharset").html(json.info.accept_charset);
                $("#pageanalyzer-settings-accenc").html(json.info.accept_encoding);
                $("#pageanalyzer-settings-acclang").html(json.info.accept_language);
                $("#pageanalyzer-settings-dlfeeds").html(json.info.download_feeds);
                $("#pageanalyzer-settings-parallelcssres").html(json.info.parallel_stylesheet_res);
                $("#pageanalyzer-settings-parallelscripts").html(json.info.parallel_scripts);
                $("#pageanalyzer-settings-parallelcssinljs").html(json.info.parallel_stylesheet_inline_js);
                $("#pageanalyzer-settings-parallelscriptres").html(json.info.parallel_script_res);
            }
            $("#pageanalyzer-settings-ua-workingstatus").html("");
            $("#pageanalyzer-settings-ua-content").show();
        }
    );
};

// Function to get general information about a page analysis.
function getAnalysisInfo(analysislogid, isLive, fetchHistory) {
    $("#pageanalyzer-analysis-summary").show();
    $("#pageanalyzer-summary-statusinfo").hide();
    $("#pageanalyzer-analysis-summary-content").hide();
    $("#pageanalyzer-summary-workingstatus").html("<img src=\"/images/loader-f2.gif\" style=\"padding-top:10px;\" />");
	$.getJSON('/ajax/pageanalyzer.php?action=info&analysislogid=' + analysislogid, null,
        function(json) {
            if (json.info != "ERROR") {
                // Update "share by email".
                $("#pageanalyzer-sharebyemail").attr("href", "mailto:?subject=" + 
                    encodeURIComponent("Load Impact Page Analysis for " + json.info.url) + 
                    "&body=%0A" + encodeURIComponent(json.info.complete_public_url));

                // Set page analysis summary information.
                if (json.info.status == 'DONE') {
                    $("#pageanalyzer-summary-status").html("Done");
                    $("#pageanalyzer-summary-status").css("color", "green");
                } else {
                    $("#pageanalyzer-summary-status").css("color", "red");
                    if (json.info.status == 'ABORTED') {
                        $("#pageanalyzer-summary-status").html("Aborted");
                    } else if (json.info.status == 'TIMEDOUT') {
                        $("#pageanalyzer-summary-status").html("Timed out");
                    }
                }
                $("#pageanalyzer-summary-publicurl").val(json.info.complete_public_url);
                $("#pageanalyzer-summary-useragent").html(json.info.useragent);
                $("#pageanalyzer-summary-resources").html(tableObj.fnGetData().length);
                $("#pageanalyzer-summary-loadtime").html(humanizeTime(json.info.loadtime_ms));
                $("#pageanalyzer-summary-reqs").html(json.info.requests);
                $("#pageanalyzer-summary-rxdata").html(humanizeSize(json.info.rx_bytes));
                $("#pageanalyzer-summary-extresources").html(json.info.external_resources);
                $("#pageanalyzer-summary-compresources").html(json.info.compressed_resources);
                $("#pageanalyzer-summary-avgcompratio").html(json.info.avgcomp_ratio + "%");
                $("#pageanalyzer-summary-effcompratio").html(json.info.effcomp_ratio + "%");
                $("#pageanalyzer-summary-html").html(json.info.html_count + " (" + humanizeSize(json.info.html_bytes) + ", " + humanizePercent(json.info.html_bytes, json.info.rx_bytes) + " of total)");
                $("#pageanalyzer-summary-feed").html(json.info.feed_count + " (" + humanizeSize(json.info.feed_bytes) + ", " + humanizePercent(json.info.feed_bytes, json.info.rx_bytes) + " of total)");
                $("#pageanalyzer-summary-xml").html(json.info.xml_count + " (" + humanizeSize(json.info.xml_bytes) + ", " + humanizePercent(json.info.xml_bytes, json.info.rx_bytes) + " of total)");
                $("#pageanalyzer-summary-css").html(json.info.css_count + " (" + humanizeSize(json.info.css_bytes) + ", " + humanizePercent(json.info.css_bytes, json.info.rx_bytes) + " of total)");
                $("#pageanalyzer-summary-script").html(json.info.script_count + " (" + humanizeSize(json.info.script_bytes) + ", " + humanizePercent(json.info.script_bytes, json.info.rx_bytes) + " of total)");
                $("#pageanalyzer-summary-img").html(json.info.img_count + " (" + humanizeSize(json.info.img_bytes) + ", " + humanizePercent(json.info.img_bytes, json.info.rx_bytes) + " of total)");
                $("#pageanalyzer-summary-flash").html(json.info.embed_count + " (" + humanizeSize(json.info.embed_bytes) + ", " + humanizePercent(json.info.embed_bytes, json.info.rx_bytes) + " of total)");
                $("#pageanalyzer-summary-applet").html(json.info.applet_count + " (" + humanizeSize(json.info.applet_bytes) + ", " + humanizePercent(json.info.applet_bytes, json.info.rx_bytes) + " of total)");
                $("#pageanalyzer-summary-redirs").html(json.info.redir_count);

                // Check if we've got an error message.
                if ((isLive || json.info.show_errors) && json.info.msg_string != "ok" && json.info.msg_string != "timed out") {
                    addErrorMessage("<strong>Error:</strong> " + json.info.msg_string, true);
                }

                // Fetch history listing for URL.
                currentPublicID = json.info.public_url;
                if (fetchHistory && $(".info-column").length == 1) {
                    if ($("#analysishistory-list-holder").length == 1) {
                        $("#analysishistory-list-holder").html('<script type="text/javascript">analysisHistoryListUpdater();</script>');
                    } else {
                        $(".info-column").append('<div class="info-box"><div class="info-box-top"><div class="info-box-holder" style="padding:0px 5px;"><h2 style="margin-left:16px;">MY HISTORY</h2><div id="analysishistory-list-holder"><script type="text/javascript">analysisHistoryListUpdater();</script></div></div></div></div>');
                    }
                }
            }
            $("#pageanalyzer-summary-workingstatus").html("");
            $("#pageanalyzer-summary-statusinfo").show();
            $("#pageanalyzer-analysis-summary-content").show();
        }
    );
};

function analysisHistoryListUpdater() {
	$.get('/ajax/analysishistory-list.php?pid='+currentPublicID, {}, analysisHistoryListUpdaterCallback);
};

function analysisHistoryListUpdaterCallback(data) {
	$("#analysishistory-list-holder").html(data);
	setTimeout("analysisHistoryListUpdater();", 1000 * 55);
};

// Function to continuously load new results from database.
$.fn.dataTableExt.oApi.fnLiveAjaxUpdate = function(oSettings, analysislogid) {
	this.oApi._fnProcessingDisplay(oSettings, true);
	var that = this;
	$.getJSON('/ajax/pageanalyzer.php?action=live&analysislogid=' + analysislogid + '&offset=' + liveRowOffset, null,
        function(json) {
            // Call continuously every other second until done!
            var delay = function() { tableObj.fnLiveAjaxUpdate(analysislogid); };

            // See what to do based on status returned by server.
            if (json.status == 'QUEUED') {
                // TODO: display status message to user.
                setTimeout(delay, 1000);
            } else if (json.status == 'IN_PROGRESS') {
                $("#pageanalyzer-status").html("- Analyzing");
                // Got the data, add it to the table.
                if (json.results.length > 0) {
                    for (var i = 0 ; i < json.results.length ; i++) {
                        that.oApi._fnAddData(oSettings, json.results[i]);
                        if (json.errors[i] != "ok") {
                            addErrorMessage(json.errors[i], true);
                        }
                        ++liveRowOffset;
                    }
                } else if ($("#pageanalyzer .dataTables_empty").length > 0) {
                    $("#pageanalyzer .dataTables_empty").html("Waiting for analysis results...");
                }
                that.fnDraw(that);
                that.oApi._fnProcessingDisplay(oSettings, false);
                setTimeout(delay, 1000);
            } else if (json.status == 'DONE' || json.status == 'ABORTED' || json.status == 'TIMEDOUT') {
                if (json.status == 'DONE') {
                    $("#pageanalyzer-status").html("- Done!");
                    $("#pageanalyzer-status").css("color", "green");

                    // Update load-test link.
                    var url = $("#pageanalyzer-url").attr("href");
                    $("#pageanalyzer-loadtest").html("(<a href=\"/freetest.php?targetdomain=" + 
                        encodeURIComponent(url) + "\">Load test this website</a>)");
                } else if (json.status == 'ABORTED') {
                    $("#pageanalyzer-status").html("- Aborted!");
                    $("#pageanalyzer-status").css("color", "red");
                } else if (json.status == 'TIMEDOUT') {
                    $("#pageanalyzer-status").html("- Timed out!");
                    $("#pageanalyzer-status").css("color", "red");
                }
                // Got the data, add it to the table.
                for (var i = 0 ; i < json.results.length ; i++) {
                    that.oApi._fnAddData(oSettings, json.results[i]);
                    if (json.errors[i] != "ok") {
                        addErrorMessage(json.errors[i], true);
                    }
                    ++liveRowOffset;
                }
                that.fnDraw(that);
                that.oApi._fnProcessingDisplay(oSettings, false);
                $("#pageanalyzer-workingstatus").html("");
                $("#btn-start-test").show();
                $("#inp-test").removeAttr("disabled");
                $("#inp-useragent").removeAttr("disabled");
                $("#pageanalyzer-analysis-summary").show();
                getAnalysisInfo(analysislogid, true, true);
            }
        }
    );
};

// Extensions to DataTable to allow sorting of file sizes.
jQuery.fn.dataTableExt.oSort["filesize-asc"] = function(a, b) {
    if (a.indexOf("MiB") >= 0) {
        a = parseFloat(a.replace(/[^\d]/g, "")) * 1048576.0;
    } else if (a.indexOf("KiB") >= 0) {
        a = parseFloat(a.replace(/[^\d]/g, "")) * 1024.0;
    } else {
        a = parseFloat(a.replace(/[^\d]/g, ""));
    }
    if (b.indexOf("MiB") >= 0) {
        b = parseFloat(b.replace(/[^\d]/g, "")) * 1048576.0;
    } else if (b.indexOf("KiB") >= 0) {
        b = parseFloat(b.replace(/[^\d]/g, "")) * 1024.0;
    } else {
        b = parseFloat(b.replace(/[^\d]/g, ""));
    }
    return ((a < b) ? -1 : ((a > b) ?  1 : 0));
};

jQuery.fn.dataTableExt.oSort["filesize-desc"] = function(a, b) {
    if (a.indexOf("MiB") >= 0) {
        a = parseFloat(a.replace(/[^\d]/g, "")) * 1048576.0;
    } else if (a.indexOf("KiB") >= 0) {
        a = parseFloat(a.replace(/[^\d]/g, "")) * 1024.0;
    } else {
        a = parseFloat(a.replace(/[^\d]/g, ""));
    }
    if (b.indexOf("MiB") >= 0) {
        b = parseFloat(b.replace(/[^\d]/g, "")) * 1048576.0;
    } else if (b.indexOf("KiB") >= 0) {
        b = parseFloat(b.replace(/[^\d]/g, "")) * 1024.0;
    } else {
        b = parseFloat(b.replace(/[^\d]/g, ""));
    }
    return ((a < b) ?  1 : ((a > b) ? -1 : 0));
};

// Function used to initialize other stuff once data table is done.
function addToolTips(oSettings) {
    // Apply the load-time bar tooltips.
    var allTimeBars = $('div.bar-container', tableObj.fnGetNodes());
    allTimeBars.tooltip({
        track: true,
        showURL: false,
        delay: 0,
        bodyHandler: function() {
            var index = allTimeBars.index(this);
            var aData = tableObj.fnGetData(index);
            var isRedirect = $.inArray(aData[11], ["301", "302", "303", "307"]) != -1;
            var measurements = $("span", this).html().split(/\s*,\s*/);
            var output = "<div>";
            for (var i = 1; i < measurements.length; i++) {
                m = parseInt(measurements[i].replace(/\s+/, ""));
                width = 10;
                if (i == (measurements.length - 1)) {
                    // Total load time.
                    output += "<div>";
                    output += "<span style=\"font-weight:bold; text-align:left; margin-left:20px;\">" + barDesc[i] + ": " + humanizeTime(aData[3]) + "</span>";
                    output += "</div>";
                } else {
                    // Don't show:
                    // - "Time lost to redirect" if no redirect has occured.
                    // - "Time to first byte" if redirect has occured.
                    // - "Download time" if redirect has occured.
                    if ((i == 4 && !isRedirect)
                        || (i == 5 && isRedirect)
                        || (i == 6 && isRedirect))
                    {
                        continue;
                    }
                    output += "<div>";
                    output += "<div class=\"bar bar-" + barNames[i] + 
                        "\" style=\"width:" + width + "px;\"></div>";
                    output += "<span style=\"text-align:left; margin-left:10px;\">" + humanizeTime(m) + " - " + barDesc[i] + "</span>";
                    output += "<div style=\"clear:left;\"></div></div>";
                }
            }
            output += "</div>";
            return output;
        }
	});

    // Apply the image link tooltips.
    $('a.link-image', tableObj.fnGetNodes()).tooltip({
        track: true,
        showURL: false,
        delay: 0,
        bodyHandler: function() {
            return "<div style=\"text-align:center;\">" + 
                "<img src=\"" + this.href + "\" /><br />" +
                this.href +
                "</div>";
        }
	});

    // Apply the other link tooltips.
    $('a.link-other', tableObj.fnGetNodes()).tooltip({
        track: true,
        showURL: false,
        delay: 0,
        bodyHandler: function() {
            return $PHP.wordwrap($(this).attr('href'), 76, '<br />\n', true);
        }
	});

    // Apply the file-size tooltips.
    $('td.filesize', tableObj.fnGetNodes()).tooltip({
        track: true,
        showURL: false,
        delay: 0,
        bodyHandler: function() {
            var aPos = tableObj.fnGetPosition(this);
            var aData = tableObj.fnGetData(aPos[0]);
            if (aData[7].indexOf("compressed") != -1) {
                var comp = aData[7].split(/\s*:\s*/);
                var compRatioInPerc = parseFloat(comp[1]).toFixed(2);
                if (compRatioInPerc < 100.0) {
                    return "File size (compressed): " + $(this).html() + " (" + compRatioInPerc + "% of " + humanizeSize(aData[10]) + ")";
                } else {
                    return "File size (over compressed, compressed >= original): " + $(this).html() + " (" + compRatioInPerc + "% of " + humanizeSize(aData[10]) + ")";
                }
            } else {
                return "File size: " + $(this).html();
            }
        }
	});

    // Apply the HTTP status tooltips.
    $('td.httpstatus', tableObj.fnGetNodes()).tooltip({
        track: true,
        showURL: false,
        delay: 0,
        bodyHandler: function() {
            var aPos = tableObj.fnGetPosition(this);
            var aData = tableObj.fnGetData(aPos[0]);
            if (aData[11] == "Err") {
                if (aData[6].substr(0, 5) == "https") {
                    return "Err: The HTTPS feature requires that you have a Load Impact premium account (BASIC/PRO/ADV).";
                } else {
                    return "Err: A connection timeout or other error occured while trying to connect to server.";
                }
            } else {
                return "HTTP status code: " + aData[11];
            }
        }
	});
};

// Function used to add tooltips to every row.
function drawCallback() {
    // NOTE: when adding tooltips make sure table is ready first,
    //       otherwise IE will complain, see: KB927917
    if (tableObj) {
        $(tableObj).ready(function() {
            addToolTips(tableObj.fnSettings());
        });
    }
};

// Function used to update each row in the table with class names and style rules.
function rowCallback(row, data, display_index) {
    // TOOD: update/scale all bars added before.
    $(".bar-container", row).each(function(i) {
        var measurements = $("span", this).html().split(/\s*,\s*/);
        var totalWidth = 0;
        var largestMs = 0;
        var largestBar = null;
        $(".bar", this).each(function(j) {
            m = parseInt(measurements[j].replace(/\s+/, ""));
            width = Math.round(m * (400 / timeScaleResolution));
            /*if (width == 0 && m > 0) {
                width = 1;
            }*/
            $(this).css("width", width + "px");
            if (j > 1) { // Don't count offset time and in-queue time!
                totalWidth += width;
                if (m > largestMs) {
                    largestMs = m;
                    largestBar = this;
                }
            }
        });
        if (totalWidth == 0) {
            if (largestBar != null) {
                $(largestBar).css("width", "1px");
            }
        }
    });

    // Add classname and styles to URL columns.
    $("td:eq(2)", row).attr("title", data[6]);
    if (data[5] == "applet") {
        $("td:eq(2)", row).css({
            "background": "url(/images/pageanalyzer/icon_java.gif) 2px center no-repeat",
            "padding-left": "20px"
        });
    } else if (data[5] == "document") {
        $("td:eq(2)", row).css({
            "background": "url(/images/pageanalyzer/icon_html.gif) 2px center no-repeat",
            "padding-left": "20px"
        });
    } else if (data[5] == "embed") {
        $("td:eq(2)", row).css({
            "background": "url(/images/pageanalyzer/icon_swf.gif) 2px center no-repeat",
            "padding-left": "20px"
        });
    } else if (data[5] == "feed") {
        $("td:eq(2)", row).css({
            "background": "url(/images/pageanalyzer/icon_feed.gif) 2px center no-repeat",
            "padding-left": "20px"
        });
    } else if (data[5] == "icon") {
        $("td:eq(2)", row).css({
            "background": "url(/images/pageanalyzer/icon_img.gif) 2px center no-repeat",
            "padding-left": "20px"
        });
    } else if (data[5] == "image") {
        $("td:eq(2)", row).css({
            "background": "url(/images/pageanalyzer/icon_img.gif) 2px center no-repeat",
            "padding-left": "20px"
        });
    } else if (data[5] == "opensearch") {
        $("td:eq(2)", row).css({
            "background": "url(/images/pageanalyzer/icon_xml.gif) 2px center no-repeat",
            "padding-left": "20px"
        });
    } else if (data[5] == "redirect") {
        $("td:eq(2)", row).css({
            "background": "url(/images/pageanalyzer/icon_redirect.gif) 2px center no-repeat",
            "padding-left": "20px"
        });
    } else if (data[5] == "script") {
        $("td:eq(2)", row).css({
            "background": "url(/images/pageanalyzer/icon_script.gif) 2px center no-repeat",
            "padding-left": "20px"
        });
    } else if (data[5] == "stylesheet") {
        $("td:eq(2)", row).css({
            "background": "url(/images/pageanalyzer/icon_css.gif) 2px center no-repeat",
            "padding-left": "20px"
        });
    }

    // Add classname and styles to file size columns.
    if (data[7].indexOf("compressed") != -1) {
        $("td:eq(3)", row).addClass("filesize");
        var comp = data[7].split(/\s*:\s*/);
        var compRatioInPerc = parseFloat(comp[1]).toFixed(2);
        if (compRatioInPerc < 100.0) {
            var widthInclPadding = 80;
            var notVisibleInPx = widthInclPadding - Math.floor(widthInclPadding * (compRatioInPerc / 100.0));
            $("td:eq(3)", row).css({
                "background": "url(/images/pageanalyzer/bg_compressed.gif) #aaa -" + notVisibleInPx + "px center no-repeat",
                "color": "white",
                "text-align": "right"
            });
        } else {
            var widthInclPadding = 80;
            $("td:eq(3)", row).css({
                "background": "url(/images/pageanalyzer/bg_compressed_over.gif) #aaa 0px center no-repeat",
                "color": "white",
                "text-align": "right"
            });
        }
    } else {
        $("td:eq(3)", row).addClass("filesize");
        $("td:eq(3)", row).css({
            "text-align": "right"
        });
    }

    // Add classname and styles to HTTP status columns.
    $("td:eq(4)", row).addClass("httpstatus");
    $("td:eq(4)", row).attr("title", "HTTP status code: " + data[11]);
    if (data[11] == "200" || data[11] == "204") {
        $("td:eq(4)", row).addClass("status200");
        $("td:eq(4)", row).css({
            "color": "green",
            "font-weight": "bold",
            "text-align": "right"
        });
    } else if (data[11] == "301" || data[11] == "302" || data[11] == "303" || data[11] == "307") {
        $("td:eq(4)", row).addClass("status30x");
        $("td:eq(4)", row).css({
            "color": "blue",
            "font-weight": "bold",
            "text-align": "right"
        });
    } else if (data[11].substr(0, 1) == "4") {
        $("td:eq(2)", row).css({
            "background": "url(/images/pageanalyzer/icon_40x.gif) 2px center no-repeat"
        });
        $("td:eq(4)", row).addClass("status40x");
        $("td:eq(4)", row).css({
            "color": "red",
            "font-weight": "bold",
            "text-align": "right"
        });
    } else if (data[11].substr(0, 1) == "5") {
        $("td:eq(2)", row).css({
            "background": "url(/images/pageanalyzer/icon_50x.gif) 2px center no-repeat"
        });
        $("td:eq(4)", row).addClass("status50x");
        $("td:eq(4)", row).css({
            "color": "red",
            "font-weight": "bold",
            "text-align": "right"
        });
    } else if (data[11] == "Err") {
        $("td:eq(2)", row).css({
            "background": "url(/images/pageanalyzer/icon_err.gif) 2px center no-repeat"
        });
        $("td:eq(4)", row).addClass("status-err");
        $("td:eq(4)", row).css({
            "color": "red",
            "font-weight": "bold",
            "text-align": "right"
        });
    }
    
    return row;
};

// Get max time given an array of measurements for a resource.
// Each measuremtn array looks like this:
// - offset
// - time-in-queue
// - DNS-lookup
// - connect-time
// - redirect-file
// - time-to-first-byte
// - donwload-time
// - load-time
function getMaxTimeFromMeasurements(measurements) {
    time = parseInt(measurements[0]) + parseInt(measurements[1]) + 
           parseInt(measurements[2]) + parseInt(measurements[3]) + 
           parseInt(measurements[4]) + parseInt(measurements[5]) + 
           parseInt(measurements[6]);
    if (time > timeScaleMaxTimeMs) {
        timeScaleMaxTimeMs = time;
    }
    return timeScaleMaxTimeMs;
};

// Function used to render the load time bars in the table.
function renderLoadtimeBar(obj) {
    var output = "";
    var measurements = obj.aData[4].split(/\s*,\s*/);

    // Get max load time so far.
    var maxLoadTimeInMs = getMaxTimeFromMeasurements(measurements);
    var timeScaleUnit = "s"
    if (maxLoadTimeInMs < 1000) {
        timeScaleUnit = "ms";
    }

    // Find resolution that fits.
    var oldTimeScaleResolution = timeScaleResolution
    if (maxLoadTimeInMs < 1000) {
        timeScaleResolution = (Math.floor(maxLoadTimeInMs / 100.0) + 1) * 100;
    } else if (maxLoadTimeInMs < 10000) {
        timeScaleResolution = (Math.floor(maxLoadTimeInMs / 500.0) + 1) * 500;
    } else {
        timeScaleResolution = (Math.floor(maxLoadTimeInMs / 1000.0) + 1) * 1000;
    }

    // Only update if necessary.
    if (timeScaleResolution != oldTimeScaleResolution) {
        // Calculate time scale.
        var timeScaleDiv = timeScaleResolution > 1000 ? 10000 : 10;
        
        // Render time scale.
        for (var i = 0; i < 10; ++i) {
            if (timeScaleResolution > 1000) {
                $("#grid-bottom-nr" + i).html((i * timeScaleResolution / timeScaleDiv).toFixed(2));
            } else {
                $("#grid-bottom-nr" + i).html((i * timeScaleResolution / timeScaleDiv).toFixed(0));
            }
        }
        $("#grid-bottom-nr9").html($("#grid-bottom-nr9").html() + " " + timeScaleUnit);
    }

    // Add load-bar for resource.
    output += "<div class=\"bar-container\"><span style=\"display:none;\">" + obj.aData[4] + "</span>";
    for (var i = 0; i < (measurements.length - 1); i++) {
        m = parseInt(measurements[i].replace(/\s+/, ""));
        width = Math.round(m * (400 / timeScaleResolution));
        if (i == 6 && width < 2 && !(obj.aData[9] == "301" || obj.aData[9] == "302" || obj.aData[9] == "303" || obj.aData[9] == "307" || obj.aData[9] == "401" || obj.aData[9] == "404")) {
            width = 2;
        } else if (width == 0 && m > 0) {
            width = 1;
        }
        output += "<div class=\"bar bar-" + barNames[i] + 
            "\" id=\"bar-" + barNames[i] + "_" + obj.iDataRow + 
            "\" style=\"width:" + width + "px;\"></div>";
    }
    output += "</div>";
    return output;
};

// Function used to render the URL in the table.
function renderURL(obj) {
    var urlMaxLength = 20;
    var url = obj.aData[6];
    var type = obj.aData[5];
    if (url.length > urlMaxLength) {
        url = "..." + url.substr(url.length - (urlMaxLength - 3))
    }
    if (type == "image") {
        return "<a href=\"" + obj.aData[6] + "\" class=\"link-image\" rel=\"nofollow\" target=\"_blank\">" + url + "</a>";
    }
    return "<a href=\"" + obj.aData[6] + "\" class=\"link-other\" rel=\"nofollow\" target=\"_blank\">" + url + "</a>";
};

// Function used to render the file size in the table.
function renderFileSize(obj) {
    return humanizeSize(obj.aData[9]);
};

function initPageAnalyzerTable(table, analysislogid, live) {
    tableObj = $(table).dataTable({
        "aaSorting": [[2, 'asc']],
        "bPaginate": true,
        "bProcessing": false,
        "bLengthChange": true,
        "bFilter": true,
        "bSort": true,
        "bSortClasses": false,
        "bInfo": true,
        "bAutoWidth": false,
        "fnDrawCallback": drawCallback,
        "fnRowCallback": rowCallback,
        "iDisplayLength": 25,
        "sPaginationType": "full_numbers",
        "oLanguage": {
            "sLengthMenu": "Show _MENU_ resources",
            "sInfo": "Showing _START_ to _END_ of _TOTAL_ resources",
            "sInfoEmpty": "Showing 0 to 0 of 0 resources",
            "sZeroRecords": "No resources found",
            "sSearch": "Filter:"
        },
        "aoColumns": [
            {
                "bSearchable": false,
                "bVisible": false,
                "sType": "numeric"
            },
            {
                "bSearchable": false,
                "bVisible": false,
                "sType": "numeric"
            },
            {
                "bSearchable": false,
                "bUseRendered": false,
                "fnRender": function(obj) {
                    return parseInt(obj.aData[2]) + 1;
                },
                "sType": "numeric"
            },
            {
                "bSearchable": false,
                "bVisible": false,
                "sType": "numeric"
            },
            {
                "fnRender": renderLoadtimeBar,
                "iDataSort": 3,
                "sClass": "loadtime-column",
                "sWidth": "400px"
            },
            {
                "bVisible": false
            },
            {
                "bUseRendered": false,
                "fnRender": renderURL,
                //"sWidth": "300px",
                "sType": "string"
            },
            {
                "bSearchable": true,
                "bVisible": false,
                "bUseRendered": true,
                "fnRender": function(obj) {
                    if (obj.aData[7] == "1") {
                        return "compressed:" + obj.aData[8];
                    }
                    return "0";
                }
            },
            {
                "bSearchable": false,
                "bVisible": false
            },
            {
                "bUseRendered": true,
                "fnRender": renderFileSize,
                "sType": "filesize",
                "sWidth": "60px"
            },
            {
                "bSearchable": false,
                "bVisible": false
            },
            {
                "sType": "string",
                "bUseRendered": true,
                "fnRender": function(obj) {
                    if (obj.aData[11] == "-1") {
                        return "Err";
                    }
                    return obj.aData[11];
                }
            }
        ]
    });
    if (live) {
        tableObj.fnLiveAjaxUpdate(analysislogid);
    } else {
        $(tableObj).ready(function() {
            addToolTips(tableObj.fnSettings());
        });
        getAnalysisInfo(analysislogid, false, false);
    }
};
