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)function RebaselineQueue()
325c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
335c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    this._selectNode = $('queue-select');
345c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    this._rebaselineButtonNode = $('rebaseline-queue');
355c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    this._toggleNode = $('toggle-queue');
365c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    this._removeSelectionButtonNode = $('remove-queue-selection');
375c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
385c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    this._inProgressRebaselineCount = 0;
395c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
405c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    var self = this;
415c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    $('add-to-rebaseline-queue').addEventListener(
425c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        'click', function() { self.addCurrentTest(); });
435c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    this._selectNode.addEventListener('change', updateState);
445c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    this._removeSelectionButtonNode.addEventListener(
455c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        'click', function() { self._removeSelection(); });
465c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    this._rebaselineButtonNode.addEventListener(
475c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        'click', function() { self.rebaseline(); });
485c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    this._toggleNode.addEventListener(
495c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        'click', function() { toggle('queue'); });
505c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
515c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
525c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)RebaselineQueue.prototype.updateState = function()
535c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
545c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    var testName = getSelectedTest();
555c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
565c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    var state = results.tests[testName].state;
575c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    $('add-to-rebaseline-queue').disabled = state != STATE_NEEDS_REBASELINE;
585c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
595c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    var queueLength = this._selectNode.options.length;
605c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    if (this._inProgressRebaselineCount > 0) {
615c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)      this._rebaselineButtonNode.disabled = true;
625c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)      this._rebaselineButtonNode.textContent =
635c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)          'Rebaseline in progress (' + this._inProgressRebaselineCount +
645c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)          ' tests left)';
655c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    } else if (queueLength == 0) {
665c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)      this._rebaselineButtonNode.disabled = true;
675c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)      this._rebaselineButtonNode.textContent = 'Rebaseline queue';
685c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)      this._toggleNode.textContent = 'Queue';
695c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    } else {
705c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)      this._rebaselineButtonNode.disabled = false;
715c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)      this._rebaselineButtonNode.textContent =
725c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)          'Rebaseline queue (' + queueLength + ' tests)';
735c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)      this._toggleNode.textContent = 'Queue (' + queueLength + ' tests)';
745c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    }
755c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    this._removeSelectionButtonNode.disabled =
765c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        this._selectNode.selectedIndex == -1;
775c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)};
785c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
795c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)RebaselineQueue.prototype.addCurrentTest = function()
805c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
815c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    var testName = getSelectedTest();
825c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    var test = results.tests[testName];
835c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
845c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    if (test.state != STATE_NEEDS_REBASELINE) {
855c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        log('Cannot add test with state "' + test.state + '" to queue.',
865c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            log.WARNING);
875c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        return;
885c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    }
895c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
905c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    var queueOption = document.createElement('option');
915c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    queueOption.value = testName;
925c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    queueOption.textContent = testName;
935c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    this._selectNode.appendChild(queueOption);
945c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    test.state = STATE_IN_QUEUE;
955c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    updateState();
965c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)};
975c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
985c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)RebaselineQueue.prototype.removeCurrentTest = function()
995c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
1005c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    this._removeTest(getSelectedTest());
1015c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)};
1025c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1035c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)RebaselineQueue.prototype._removeSelection = function()
1045c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
1055c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    if (this._selectNode.selectedIndex == -1)
1065c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        return;
1075c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1085c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    this._removeTest(
1095c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        this._selectNode.options[this._selectNode.selectedIndex].value);
1105c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)};
1115c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1125c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)RebaselineQueue.prototype._removeTest = function(testName)
1135c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
1145c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    var queueOption = this._selectNode.firstChild;
1155c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1165c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    while (queueOption && queueOption.value != testName) {
1175c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        queueOption = queueOption.nextSibling;
1185c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    }
1195c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1205c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    if (!queueOption)
1215c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        return;
1225c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1235c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    this._selectNode.removeChild(queueOption);
1245c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    var test = results.tests[testName];
1255c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    test.state = STATE_NEEDS_REBASELINE;
1265c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    updateState();
1275c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)};
1285c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1295c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)RebaselineQueue.prototype.rebaseline = function()
1305c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
1315c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    var testNames = [];
1325c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    for (var queueOption = this._selectNode.firstChild;
1335c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)         queueOption;
1345c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)         queueOption = queueOption.nextSibling) {
1355c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        testNames.push(queueOption.value);
1365c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    }
1375c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1385c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    this._inProgressRebaselineCount = testNames.length;
1395c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    updateState();
1405c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1415c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    testNames.forEach(this._rebaselineTest, this);
1425c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)};
1435c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1445c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)RebaselineQueue.prototype._rebaselineTest = function(testName)
1455c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
1465c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    var baselineTarget = getSelectValue('baseline-target');
1475c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    var baselineMoveTo = getSelectValue('baseline-move-to');
1485c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1495c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    var xhr = new XMLHttpRequest();
1505c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    xhr.open('POST',
1515c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        '/rebaseline?test=' + encodeURIComponent(testName) +
1525c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        '&baseline-target=' + encodeURIComponent(baselineTarget) +
1535c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        '&baseline-move-to=' + encodeURIComponent(baselineMoveTo));
1545c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1555c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    var self = this;
1565c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    function handleResponse(logType, newState) {
1575c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        log(xhr.responseText, logType);
1585c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        self._removeTest(testName);
1595c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        self._inProgressRebaselineCount--;
1605c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        results.tests[testName].state = newState;
1615c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        updateState();
1625c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        // If we're done with a set of rebaselines, regenerate the test menu
1635c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        // (which is grouped by state) since test states have changed.
1645c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        if (self._inProgressRebaselineCount == 0) {
1655c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            selectDirectory();
1665c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        }
1675c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    }
1685c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1695c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    function handleSuccess() {
1705c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        handleResponse(log.SUCCESS, STATE_REBASELINE_SUCCEEDED);
1715c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    }
1725c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    function handleFailure() {
1735c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        handleResponse(log.ERROR, STATE_REBASELINE_FAILED);
1745c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    }
1755c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1765c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    xhr.addEventListener('load', function() {
1775c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)      if (xhr.status < 400) {
1785c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)          handleSuccess();
1795c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)      } else {
1805c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)          handleFailure();
1815c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)      }
1825c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    });
1835c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    xhr.addEventListener('error', handleFailure);
1845c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1855c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    xhr.send();
1865c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)};
187