15c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)/*
25c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * Copyright (C) 2010 Google Inc. All rights reserved.
35c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) *
45c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * Redistribution and use in source and binary forms, with or without
55c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * modification, are permitted provided that the following conditions are
65c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * met:
75c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) *
85c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) *     * Redistributions of source code must retain the above copyright
95c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * notice, this list of conditions and the following disclaimer.
105c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) *     * Redistributions in binary form must reproduce the above
115c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * copyright notice, this list of conditions and the following disclaimer
125c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * in the documentation and/or other materials provided with the
135c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * distribution.
145c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) *     * Neither the name of Google Inc. nor the names of its
155c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * contributors may be used to endorse or promote products derived from
165c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * this software without specific prior written permission.
175c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) *
185c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
195c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
205c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
215c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
225c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
235c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
245c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
255c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
265c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
275c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
285c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
295c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) */
305c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
315c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)/**
325c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * @constructor
33d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles) * @extends {WebInspector.VBox}
34a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles) * @param {!WebInspector.NetworkRequest} request
355c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) */
365c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)WebInspector.RequestTimingView = function(request)
375c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
38d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)    WebInspector.VBox.call(this);
39a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)    this.element.classList.add("resource-timing-view");
405c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
415c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    this._request = request;
425c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
435c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
445c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)WebInspector.RequestTimingView.prototype = {
455c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    wasShown: function()
465c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    {
47926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)        this._request.addEventListener(WebInspector.NetworkRequest.Events.TimingChanged, this._refresh, this);
4809380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)        this._request.addEventListener(WebInspector.NetworkRequest.Events.FinishedLoading, this._refresh, this);
49926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)
505c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        if (!this._request.timing) {
515c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            if (!this._emptyView) {
525c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                this._emptyView = new WebInspector.EmptyView(WebInspector.UIString("This request has no detailed timing info."));
535c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                this._emptyView.show(this.element);
545c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                this.innerView = this._emptyView;
555c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            }
565c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            return;
575c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        }
585c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
595c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        if (this._emptyView) {
605c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            this._emptyView.detach();
615c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            delete this._emptyView;
625c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        }
635c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
645c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        this._refresh();
655c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    },
665c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
67926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)    willHide: function()
68926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)    {
69926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)        this._request.removeEventListener(WebInspector.NetworkRequest.Events.TimingChanged, this._refresh, this);
7009380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)        this._request.removeEventListener(WebInspector.NetworkRequest.Events.FinishedLoading, this._refresh, this);
71926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)    },
72926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)
735c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    _refresh: function()
745c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    {
755c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        if (this._tableElement)
76591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch            this._tableElement.remove();
775c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
785c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        this._tableElement = WebInspector.RequestTimingView.createTimingTable(this._request);
795c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        this.element.appendChild(this._tableElement);
805c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    },
815c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
82d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)    __proto__: WebInspector.VBox.prototype
835c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
845c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
855c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
865d92fedcae5e801a8b224de090094f2d9df0b54aTorne (Richard Coles)/**
875d92fedcae5e801a8b224de090094f2d9df0b54aTorne (Richard Coles) * @param {!WebInspector.NetworkRequest} request
885d92fedcae5e801a8b224de090094f2d9df0b54aTorne (Richard Coles) * @return {!Element}
895d92fedcae5e801a8b224de090094f2d9df0b54aTorne (Richard Coles) */
905c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)WebInspector.RequestTimingView.createTimingTable = function(request)
915c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
929e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)    var tableElement = document.createElementWithClass("table", "network-timing-table");
939e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)
949e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)    /**
959e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)     * @param {string} title
969e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)     * @param {string} className
979e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)     * @param {number} start
989e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)     * @param {number} end
999e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)     */
1005c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    function addRow(title, className, start, end)
1015c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    {
1029e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)        var tr = tableElement.createChild("tr");
1039e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)        tr.createChild("td").createTextChild(title);
1049e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)        var td = tr.createChild("td");
1059e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)        td.width = chartWidth + "px";
1069e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)        var row = td.createChild("div", "network-timing-row");
1079e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)
1089e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)        var bar = row.createChild("span", "network-timing-bar " + className);
1099e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)        bar.style.left = Math.floor(scale * start) + "px";
1109e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)        bar.style.right = Math.floor(scale * (total - end)) + "px";
1119e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)        bar.textContent = "\u200B"; // Important for 0-time items to have 0 width.
1129e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)
1139e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)        var label = row.createChild("span", "network-timing-bar-title");
1149e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)        if (total - end < start)
1159e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)            label.style.right = (scale * (total - end)) + "px";
1169e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)        else
1179e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)            label.style.left = (scale * start) + "px";
1189e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)        label.textContent = Number.secondsToString((end - start) / 1000, true);
1195c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    }
1205c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1219e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)    /**
1229e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)     * @param {!Array.<number>} numbers
1239e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)     * @return {number|undefined}
1249e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)     */
12509380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)    function firstPositive(numbers)
12609380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)    {
12709380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)        for (var i = 0; i < numbers.length; ++i) {
12809380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)            if (numbers[i] > 0)
12909380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)                return numbers[i];
13009380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)        }
13109380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)        return undefined;
13209380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)    }
13309380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)
1347242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci    function createCommunicationTimingTable()
1357242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci    {
1367242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci        if (blocking > 0)
1377242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci            addRow(WebInspector.UIString("Stalled"), "blocking", 0, blocking);
1387242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci
1397242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci        if (timing.proxyStart !== -1)
1407242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci            addRow(WebInspector.UIString("Proxy negotiation"), "proxy", timing.proxyStart, timing.proxyEnd);
1417242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci
1427242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci        if (timing.dnsStart !== -1)
1437242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci            addRow(WebInspector.UIString("DNS Lookup"), "dns", timing.dnsStart, timing.dnsEnd);
1447242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci
1457242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci        if (timing.connectStart !== -1)
1467242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci            addRow(WebInspector.UIString("Initial connection"), "connecting", timing.connectStart, timing.connectEnd);
14709380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)
1487242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci        if (timing.sslStart !== -1)
1497242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci            addRow(WebInspector.UIString("SSL"), "ssl", timing.sslStart, timing.sslEnd);
15006f816c7c76bc45a15e452ade8a34e8af077693eTorne (Richard Coles)
1517242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci        addRow(WebInspector.UIString("Request sent"), "sending", timing.sendStart, timing.sendEnd);
1527242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci        addRow(WebInspector.UIString("Waiting (TTFB)"), "waiting", timing.sendEnd, timing.receiveHeadersEnd);
1535c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1547242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci        if (request.endTime !== -1)
1557242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci            addRow(WebInspector.UIString("Content Download"), "receiving", (request.responseReceivedTime - timing.requestTime) * 1000, total);
1567242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci    }
1575c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1587242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci    function createServiceWorkerTimingTable()
1597242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci    {
1607242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci        addRow(WebInspector.UIString("Stalled"), "blocking", 0, timing.serviceWorkerFetchStart);
1615c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1627242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci        addRow(WebInspector.UIString("Request to ServiceWorker"), "serviceworker", timing.serviceWorkerFetchStart, timing.serviceWorkerFetchEnd);
1637242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci        addRow(WebInspector.UIString("ServiceWorker Preparation"), "serviceworker", timing.serviceWorkerFetchStart, timing.serviceWorkerFetchReady);
1647242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci        addRow(WebInspector.UIString("Waiting (TTFB)"), "waiting", timing.serviceWorkerFetchEnd, timing.receiveHeadersEnd);
1655c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1667242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci        if (request.endTime !== -1)
1677242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci            addRow(WebInspector.UIString("Content Download"), "receiving", (request.responseReceivedTime - timing.requestTime) * 1000, total);
1687242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci    }
1697242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci
1707242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci    var timing = request.timing;
1717242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci    var blocking = firstPositive([timing.dnsStart, timing.connectStart, timing.sendStart]);
1727242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci    var endTime = firstPositive([request.endTime, request.responseReceivedTime, timing.requestTime]);
1737242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci    var total = (endTime - timing.requestTime) * 1000;
1747242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci    const chartWidth = 200;
1757242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci    var scale = chartWidth / total;
17609380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)
1777242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci    if (request.fetchedViaServiceWorker)
1787242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci        createServiceWorkerTimingTable();
1797242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci    else
1807242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci        createCommunicationTimingTable();
18109380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)
18209380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)    if (!request.finished) {
18309380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)        var cell = tableElement.createChild("tr").createChild("td", "caution");
18409380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)        cell.colSpan = 2;
18509380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)        cell.createTextChild(WebInspector.UIString("CAUTION: request is not finished yet!"));
18609380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)    }
18709380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)
1885c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    return tableElement;
1895c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
190