15c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)/*
25c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * Copyright (C) 2012 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
335c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * @extends {WebInspector.DataGrid}
345c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) */
355c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)WebInspector.DirectoryContentView = function()
365c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
375c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    const indexes = WebInspector.DirectoryContentView.columnIndexes;
38926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)    var columns = [
39926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)        {id: indexes.Name, title: WebInspector.UIString("Name"), sortable: true, sort: WebInspector.DataGrid.Order.Ascending, width: "20%"},
40926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)        {id: indexes.URL, title: WebInspector.UIString("URL"), sortable: true, width: "20%"},
41926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)        {id: indexes.Type, title: WebInspector.UIString("Type"), sortable: true, width: "15%"},
42926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)        {id: indexes.Size, title: WebInspector.UIString("Size"), sortable: true, width: "10%"},
43926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)        {id: indexes.ModificationTime, title: WebInspector.UIString("Modification Time"), sortable: true, width: "25%"}
44926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)    ];
455c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
465c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    WebInspector.DataGrid.call(this, columns);
47926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)    this.addEventListener(WebInspector.DataGrid.Events.SortingChanged, this._sort, this);
485c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
495c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
505c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)WebInspector.DirectoryContentView.columnIndexes = {
515c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    Name: "0",
525c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    URL: "1",
535c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    Type: "2",
545c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    Size: "3",
555c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    ModificationTime: "4"
565c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
575c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
585c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)WebInspector.DirectoryContentView.prototype = {
595c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    /**
605c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)     * @param {Array.<WebInspector.FileSystemModel.Directory>} entries
615c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)     */
625c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    showEntries: function(entries)
635c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    {
645c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        const indexes = WebInspector.DirectoryContentView.columnIndexes;
655c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        this.rootNode().removeChildren();
665c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        for (var i = 0; i < entries.length; ++i)
675c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            this.rootNode().appendChild(new WebInspector.DirectoryContentView.Node(entries[i]));
685c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    },
695c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
705c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    _sort: function()
715c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    {
72926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)        var column = /** @type {string} */ (this.sortColumnIdentifier());
73926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)        this.sortNodes(WebInspector.DirectoryContentView.Node.comparator(column, !this.isSortOrderAscending()), false);
745c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    },
755c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
765c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    __proto__: WebInspector.DataGrid.prototype
775c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
785c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
795c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)/**
805c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * @constructor
815c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * @extends {WebInspector.DataGridNode}
825c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * @param {WebInspector.FileSystemModel.Entry} entry
835c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) */
845c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)WebInspector.DirectoryContentView.Node = function(entry)
855c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
865c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    const indexes = WebInspector.DirectoryContentView.columnIndexes;
875c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    var data = {};
885c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    data[indexes.Name] = entry.name;
895c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    data[indexes.URL] = entry.url;
905c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    data[indexes.Type] = entry.isDirectory ? WebInspector.UIString("Directory") : entry.mimeType;
915c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    data[indexes.Size] = "";
925c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    data[indexes.ModificationTime] = "";
935c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
945c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    WebInspector.DataGridNode.call(this, data);
955c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    this._entry = entry;
965c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    this._metadata = null;
975c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
985c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    this._entry.requestMetadata(this._metadataReceived.bind(this));
995c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
1005c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1015c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)/**
1025c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * @param {string} column
1035c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * @param {boolean} reverse
1045c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) */
1055c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)WebInspector.DirectoryContentView.Node.comparator = function(column, reverse)
1065c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
1075c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    var reverseFactor = reverse ? -1 : 1;
1085c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    const indexes = WebInspector.DirectoryContentView.columnIndexes;
1095c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1105c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    switch (column) {
1115c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    case indexes.Name:
1125c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    case indexes.URL:
1135c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        return function(x, y)
1145c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        {
1155c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            return isDirectoryCompare(x, y) || nameCompare(x, y);
1165c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        };
1175c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    case indexes.Type:
1185c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        return function(x, y)
1195c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        {
1205c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            return isDirectoryCompare(x ,y) || typeCompare(x, y) || nameCompare(x, y);
1215c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        };
1225c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    case indexes.Size:
1235c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        return function(x, y)
1245c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        {
1255c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            return isDirectoryCompare(x, y) || sizeCompare(x, y) || nameCompare(x, y);
1265c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        };
1275c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    case indexes.ModificationTime:
1285c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        return function(x, y)
1295c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        {
1305c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            return isDirectoryCompare(x, y) || modificationTimeCompare(x, y) || nameCompare(x, y);
1315c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        };
1325c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    }
1335c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1345c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    function isDirectoryCompare(x, y)
1355c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    {
1365c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        if (x._entry.isDirectory != y._entry.isDirectory)
1375c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            return y._entry.isDirectory ? 1 : -1;
1385c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        return 0;
1395c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    }
1405c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1415c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    function nameCompare(x, y)
1425c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    {
143926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)        return reverseFactor * x._entry.name.compareTo(y._entry.name);
1445c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    }
1455c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1465c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    function typeCompare(x, y)
1475c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    {
148926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)        return reverseFactor * (x._entry.mimeType || "").compareTo(y._entry.mimeType || "");
1495c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    }
1505c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1515c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    function sizeCompare(x, y)
1525c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    {
1535c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        return reverseFactor * ((x._metadata ? x._metadata.size : 0) - (y._metadata ? y._metadata.size : 0));
1545c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    }
1555c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1565c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    function modificationTimeCompare(x, y)
1575c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    {
1585c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        return reverseFactor * ((x._metadata ? x._metadata.modificationTime : 0) - (y._metadata ? y._metadata.modificationTime : 0));
1595c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    }
1605c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
1615c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1625c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)WebInspector.DirectoryContentView.Node.prototype = {
1635c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    /**
1645c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)     * @param {number} errorCode
1655c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)     * @param {FileSystemAgent.Metadata} metadata
1665c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)     */
1675c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    _metadataReceived: function(errorCode, metadata)
1685c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    {
1695c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        const indexes = WebInspector.DirectoryContentView.columnIndexes;
1705c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        if (errorCode !== 0)
1715c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            return;
1725c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1735c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        this._metadata = metadata;
1745c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        var data = this.data;
1755c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        if (this._entry.isDirectory)
1765c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            data[indexes.Size] = WebInspector.UIString("-");
1775c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        else
1785c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            data[indexes.Size] = Number.bytesToString(metadata.size);
1795c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        data[indexes.ModificationTime] = new Date(metadata.modificationTime).toGMTString();
1805c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        this.data = data;
1815c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    },
1825c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1835c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    __proto__: WebInspector.DataGridNode.prototype
1845c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
185