<html>
  <head>
    <meta content="text/html; charset=windows-1252"
      http-equiv="Content-Type">
  </head>
  <body text="#000000" bgcolor="#FFFFFF">
    <br>
    <div class="moz-cite-prefix">On 09/12/2014 11:32 PM, Aline Manera
      wrote:<br>
    </div>
    <blockquote cite="mid:5413121C.1050804@linux.vnet.ibm.com"
      type="cite">
      <br>
      On 09/12/2014 06:42 AM, Hongliang Wang wrote:
      <br>
      <blockquote type="cite">Implemented download and upload volumes
        functions.
        <br>
        <br>
        Signed-off-by: Hongliang Wang <a class="moz-txt-link-rfc2396E" href="mailto:hlwang@linux.vnet.ibm.com">&lt;hlwang@linux.vnet.ibm.com&gt;</a>
        <br>
        ---
        <br>
          ui/css/theme-default/storagepool-add-volume.css |  36 ++++
        <br>
          ui/js/src/kimchi.storagepool_add_volume_main.js | 243
        ++++++++++++++++++++++++
        <br>
          ui/pages/storagepool-add-volume.html.tmpl       |  80 ++++++++
        <br>
          3 files changed, 359 insertions(+)
        <br>
          create mode 100644
        ui/css/theme-default/storagepool-add-volume.css
        <br>
          create mode 100644
        ui/js/src/kimchi.storagepool_add_volume_main.js
        <br>
          create mode 100644 ui/pages/storagepool-add-volume.html.tmpl
        <br>
        <br>
        diff --git a/ui/css/theme-default/storagepool-add-volume.css
        b/ui/css/theme-default/storagepool-add-volume.css
        <br>
        new file mode 100644
        <br>
        index 0000000..6e8a551
        <br>
        --- /dev/null
        <br>
        +++ b/ui/css/theme-default/storagepool-add-volume.css
        <br>
        @@ -0,0 +1,36 @@
        <br>
        +/*
        <br>
        + * Project Kimchi
        <br>
        + *
        <br>
        + * Copyright IBM, Corp. 2014
        <br>
        + *
        <br>
        + * Licensed under the Apache License, Version 2.0 (the
        "License");
        <br>
        + * you may not use this file except in compliance with the
        License.
        <br>
        + * You may obtain a copy of the License at
        <br>
        + *
        <br>
        + *     <a class="moz-txt-link-freetext" href="http://www.apache.org/licenses/LICENSE-2.0">http://www.apache.org/licenses/LICENSE-2.0</a>
        <br>
        + *
        <br>
        + * Unless required by applicable law or agreed to in writing,
        software
        <br>
        + * distributed under the License is distributed on an "AS IS"
        BASIS,
        <br>
        + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
        or implied.
        <br>
        + * See the License for the specific language governing
        permissions and
        <br>
        + * limitations under the License.
        <br>
        + */
        <br>
        +#sp-add-volume-window {
        <br>
        +    height: 400px;
        <br>
        +    width: 500px;
        <br>
        +}
        <br>
        +
        <br>
        +#sp-add-volume-window .textbox-wrapper input[type="text"] {
        <br>
        +    box-sizing: border-box;
        <br>
        +    width: 100%;
        <br>
        +}
        <br>
        +
        <br>
        +#sp-add-volume-window .textbox-wrapper label {
        <br>
        +    vertical-align: middle;
        <br>
        +}
        <br>
        +
        <br>
        +#sp-add-volume-window input[type="text"][disabled] {
        <br>
        +    color: #bbb;
        <br>
        +    background-color: #fafafa;
        <br>
        +    cursor: not-allowed;
        <br>
        +}
        <br>
        diff --git a/ui/js/src/kimchi.storagepool_add_volume_main.js
        b/ui/js/src/kimchi.storagepool_add_volume_main.js
        <br>
        new file mode 100644
        <br>
        index 0000000..9435e28
        <br>
        --- /dev/null
        <br>
        +++ b/ui/js/src/kimchi.storagepool_add_volume_main.js
        <br>
        @@ -0,0 +1,243 @@
        <br>
        +/*
        <br>
        + * Project Kimchi
        <br>
        + *
        <br>
        + * Copyright IBM, Corp. 2014
        <br>
        + *
        <br>
        + * Licensed under the Apache License, Version 2.0 (the
        'License');
        <br>
        + * you may not use this file except in compliance with the
        License.
        <br>
        + * You may obtain a copy of the License at
        <br>
        + *
        <br>
        + *     <a class="moz-txt-link-freetext" href="http://www.apache.org/licenses/LICENSE-2.0">http://www.apache.org/licenses/LICENSE-2.0</a>
        <br>
        + *
        <br>
        + * Unless required by applicable law or agreed to in writing,
        software
        <br>
        + * distributed under the License is distributed on an 'AS IS'
        BASIS,
        <br>
        + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
        or implied.
        <br>
        + * See the License for the specific language governing
        permissions and
        <br>
        + * limitations under the License.
        <br>
        + */
        <br>
        +kimchi.sp_add_volume_main = function() {
        <br>
        +    // download from remote server or upload from local file
        <br>
        +    var type = 'download';
        <br>
        +
        <br>
        +    var addButton = $('#sp-add-volume-button');
        <br>
        +    var remoteURLBox = $('#volume-remote-url');
        <br>
        +    var localFileBox = $('#volume-input-file');
        <br>
        +    var typeRadios = $('input.volume-type');
        <br>
        +
        <br>
        +    var isValidURL = function() {
        <br>
        +        var url = $(remoteURLBox).val();
        <br>
        +        return kimchi.template_check_url(url);
        <br>
        +    };
        <br>
        +
        <br>
      </blockquote>
      <br>
      <br>
      <blockquote type="cite">+    var isValidFile = function() {
        <br>
        +        var fileName = $(localFileBox).val();
        <br>
        +        return fileName &amp;&amp;
        <br>
        +            /[Ii][Ss][Oo]/g.test(fileName.split('.').pop());
        <br>
        +    };
        <br>
      </blockquote>
      <br>
      The user can download and upload any type of file.
      <br>
      So you just need to check it a file was selected.
      <br>
    </blockquote>
    ACK.<br>
    <blockquote cite="mid:5413121C.1050804@linux.vnet.ibm.com"
      type="cite">
      <br>
      <blockquote type="cite">+
        <br>
        +    $(typeRadios).change(function(event) {
        <br>
        +        $('.volume-input').prop('disabled', true);
        <br>
        +        $('.volume-input.' + this.value).prop('disabled',
        false);
        <br>
        +        type = this.value;
        <br>
        +        if(type == 'download') {
        <br>
        +            $(addButton).prop('disabled', !isValidURL());
        <br>
        +        }
        <br>
        +        else {
        <br>
        +            $(addButton).prop('disabled', !isValidFile());
        <br>
        +        }
        <br>
        +    });
        <br>
        +
        <br>
        +    $(remoteURLBox).on('input propertychange', function(event)
        {
        <br>
        +        $(addButton).prop('disabled', !isValidURL());
        <br>
        +    });
        <br>
        +
        <br>
        +    $(localFileBox).on('change', function(event) {
        <br>
        +        $(addButton).prop('disabled', !isValidFile());
        <br>
        +    });
        <br>
        +
        <br>
      </blockquote>
      <br>
      <br>
      <blockquote type="cite">+    if(!kimchi.volumeTransferTracker) {
        <br>
        +        kimchi.volumeTransferTracker = (function() {
        <br>
        +            var tasks = {},
        <br>
        +                sps = {};
        <br>
        +            var addTask = function(task) {
        <br>
        +                var taskID = task['id'];
        <br>
        +                tasks[taskID] = task;
        <br>
        +                var sp = task['sp'];
        <br>
        +                if(sps[sp] === undefined) {
        <br>
        +                    sps[sp] = 1;
        <br>
        +                }
        <br>
        +                else {
        <br>
        +                    sps[sp]++;
        <br>
        +                }
        <br>
        +            };
        <br>
        +            var getTask = function(taskID) {
        <br>
        +                return tasks[taskID];
        <br>
        +            };
        <br>
        +            var removeTask = function(task) {
        <br>
        +                var taskID = task['id'];
        <br>
        +                var sp = tasks[taskID]['sp'];
        <br>
        +                delete tasks[taskID];
        <br>
        +                if(--sps[sp] === 0) {
        <br>
        +                    delete sps[sp];
        <br>
        +                   
        kimchi.topic('kimchi/allVolumeTasksFinished').publish({
        <br>
        +                        sp: sp
        <br>
        +                    });
        <br>
        +                }
        <br>
        +            };
        <br>
        +            return {
        <br>
        +                add: addTask,
        <br>
        +                get: getTask,
        <br>
        +                remove: removeTask
        <br>
        +            };
        <br>
        +        })();
        <br>
        +    }
        <br>
        +    var taskTracker = kimchi.volumeTransferTracker;
        <br>
      </blockquote>
      <br>
      Why the above code is needed?
      <br>
    </blockquote>
    This code is used for:<br>
    1) Setup relationship between storage pool and task, and make it
    possible to retrieve the task ID for a volume and the storage pool
    name for a task. In current GET /tasks response, there is no storage
    pool name information so it's inconvenient when we want to update
    the progress information for a volume because we need to find which
    storage pool is this volume is located (volumes with same name can
    be located in different storage pools).<br>
    2) There may be several in-progress volumes at the same time within
    a same storage pool. So we need do a whole refresh of the storage
    pool when all volume transferring is done. We need a counter for
    each storage pools.<br>
    3) Performance. Keeping the mapping of storage pool name and task in
    memory can save Ajax requests to improve performance.<br>
    <br>
    <blockquote cite="mid:5413121C.1050804@linux.vnet.ibm.com"
      type="cite">
      <br>
      The flow should be:
      <br>
      <br>
      1) User selects a file to upload or download and click on "Add"
      button
      <br>
      2) Add handler will only start the process
      <br>
          POST /storagepools/&lt;pool&gt;/storagevolumes/&lt;volume&gt;
      {&lt;data&gt;}
      <br>
      <br>
      While listing the storage volumes in a storage pool you need to:
      <br>
      <br>
      GET /storagepools/&lt;pool&gt;/storagevolumes/
      <br>
      <br>
      *AND* get the storage volumes in progress by calling:
      <br>
      <br>
      GET
/tasks?status=running&amp;target_uri=^/storagepools/&lt;pool&gt;/storagevolumes/*<br>
      <br>
      So when listing the volumes you will know which ones are in
      progress.
      <br>
      <br>
      volumes = GET
      /storagepools/&lt;pool&gt;/storagevolumes/&lt;volume&gt;
      <br>
      pendingVolumes = GET
/tasks?status=running&amp;target_uri=^/storagepools/&lt;pool&gt;/storagevolumes/*<br>
      <br>
      for volumes in volumes:
      <br>
          #create volume box html
      <br>
      <br>
          # check the volume is in progress
      <br>
          if volume in pendingVolumes:
      <br>
              # add progress bar according to task
      <br>
              # add taskTrack for this task id
      <br>
      <br>
      <br>
      By now the Task resource can only return target_uri and message,
      so there is no way to differ download from upload.
      <br>
      What we know is only if a storage volume is pending.
      <br>
      So I suggest to change the progress bar message to a generic one,
      example, "In progress"
      <br>
    </blockquote>
    ACK.<br>
    Already save the download/upload information when making the Ajax
    callback in <b>makeCallback()</b> function. Though if the user
    refreshes the browser, the information will be lost. Then we'll lose
    the transfer type information and degrade it to "In progress".<br>
    <blockquote cite="mid:5413121C.1050804@linux.vnet.ibm.com"
      type="cite">
      <br>
      <br>
      <br>
      <blockquote type="cite">+
        <br>
        +    var makeCallback = function(trackType, sp, transType,
        callback) {
        <br>
        +        return function(resp) {
        <br>
        +            var taskID = resp['id'];
        <br>
        +            var volumeName =
        resp['target_uri'].split('/').pop();
        <br>
        +            if(trackType === 'add') {
        <br>
        +                taskTracker.add({
        <br>
        +                    id: taskID,
        <br>
        +                    sp: sp,
        <br>
        +                    volume: volumeName
        <br>
        +                });
        <br>
        +            }
        <br>
        +            callback(transType, resp);
        <br>
        +       };
        <br>
        +    };
        <br>
        +
        <br>
        +    var extractProgressData = function(data) {
        <br>
        +        var sizeArray = /(\d+)\/(\d+)/g.exec(data) || [0, 0,
        0];
        <br>
        +        var downloaded = sizeArray[1];
        <br>
        +        var percent = 0;
        <br>
        +        if(downloaded) {
        <br>
        +            var total = sizeArray[2];
        <br>
        +            if(!isNaN(total)) {
        <br>
        +                percent = downloaded / total * 100;
        <br>
        +            }
        <br>
        +        }
        <br>
        +        var formatted = kimchi.formatMeasurement(downloaded);
        <br>
        +        var size = (1.0 * formatted['v']).toFixed(1) +
        formatted['s'];
        <br>
        +        return {
        <br>
        +            size: size,
        <br>
        +            percent: percent
        <br>
        +        };
        <br>
        +    };
        <br>
        +
        <br>
        +    var onFinished = function(type, result) {
        <br>
        +        var progress = extractProgressData(resp['message']);
        <br>
        +        var task = taskTracker.get([result['id']]);
        <br>
        +       
        kimchi.topic('kimchi/volumeTransferFinished').publish($.extend(progress,
        {
        <br>
        +            sp: task['sp'],
        <br>
        +            type: type,
        <br>
        +            volume: task['volume']
        <br>
        +        }));
        <br>
        +        taskTracker.remove({
        <br>
        +            id: result['id']
        <br>
        +        });
        <br>
        +    };
        <br>
        +
        <br>
        +    var onProgress = function(type, resp) {
        <br>
        +        var progress = extractProgressData(resp['message']);
        <br>
        +        var task = taskTracker.get([resp['id']]);
        <br>
        +       
        kimchi.topic('kimchi/volumeTransferProgress').publish($.extend(progress,
        {
        <br>
        +            sp: task['sp'],
        <br>
        +            type: type,
        <br>
        +            volume: task['volume']
        <br>
        +        }));
        <br>
        +    };
        <br>
        +
        <br>
        +    var onTransferError = function(type, result) {
        <br>
        +        if(!result) {
        <br>
        +            return;
        <br>
        +        }
        <br>
        +        var msg = result &amp;&amp; (result['message'] || (
        <br>
        +            result['responseJSON'] &amp;&amp;
        result['responseJSON']['reason'])
        <br>
        +        );
        <br>
        +        kimchi.message.error(msg);
        <br>
        +
        <br>
        +        if(!result['target_uri']) {
        <br>
        +            return;
        <br>
        +        }
        <br>
        +        var task = taskTracker.get(result['id']);
        <br>
        +        kimchi.topic('kimchi/volumeTransferError').publish({
        <br>
        +            sp: task['sp'],
        <br>
        +            type: type,
        <br>
        +            volume: task['volume']
        <br>
        +        });
        <br>
        +        taskTracker.remove({
        <br>
        +            id: result['id']
        <br>
        +        });
        <br>
        +    };
        <br>
        +
        <br>
        +    var onAccepted = function(type, resp) {
        <br>
        +        var taskID = resp['id'];
        <br>
        +        var task = taskTracker.get(taskID);
        <br>
        +        kimchi.window.close();
        <br>
        +        kimchi.topic('kimchi/volumeTransferStarted').publish({
        <br>
        +            sp: task['sp'],
        <br>
        +            type: type,
        <br>
        +            volume: task['volume']
        <br>
        +        });
        <br>
        +
        <br>
        +        kimchi.trackTask(taskID, function(resp) {
        <br>
        +            onFinished(type, resp);
        <br>
        +        }, function(resp) {
        <br>
        +            onTransferError(type, resp);
        <br>
        +        }, function(resp) {
        <br>
        +            onProgress(type, resp);
        <br>
        +        });
        <br>
        +    };
        <br>
        +
        <br>
        +    var onError = function(result) {
        <br>
        +        $(this).prop('disabled', false);
        <br>
        +        $(typeRadios).prop('disabled', false);
        <br>
        +        if(!result) {
        <br>
        +            return;
        <br>
        +        }
        <br>
        +        var msg = result['message'] || (
        <br>
        +            result['responseJSON'] &amp;&amp;
        result['responseJSON']['reason']
        <br>
        +        );
        <br>
        +        kimchi.message.error(msg);
        <br>
        +    };
        <br>
        +
        <br>
        +    var fetchRemoteFile = function() {
        <br>
        +        var volumeURL = remoteURLBox.val();
        <br>
        +        var volumeName = volumeURL.split(/(\\|\/)/g).pop();
        <br>
        +        kimchi.downloadVolumeToSP({
        <br>
        +            sp: kimchi.selectedSP,
        <br>
        +            name: volumeName,
        <br>
        +            url: volumeURL
        <br>
        +        }, makeCallback('add', kimchi.selectedSP, 'download',
        onAccepted),
        <br>
        +            onError
        <br>
        +        );
        <br>
        +    };
        <br>
        +
        <br>
      </blockquote>
      <br>
      <br>
      <blockquote type="cite">+    var uploadFile = function() {
        <br>
        +        var blobFile = $(localFileBox)[0].files[0];
        <br>
        +        var fileName = blobFile.name;
        <br>
        +        var fd = new FormData();
        <br>
        +        fd.append('name', fileName);
        <br>
        +        fd.append('file', blobFile);
        <br>
        +        kimchi.uploadVolumeToSP({
        <br>
        +            sp: kimchi.selectedSP,
        <br>
        +            formData: fd
        <br>
        +        }, makeCallback('add', kimchi.selectedSP, 'upload',
        onAccepted),
        <br>
        +            onError
        <br>
        +        );
        <br>
        +    };
        <br>
        +
        <br>
      </blockquote>
      <br>
      When selecting a file to upload the UI will need to read the
      selected file to build  the request.
      <br>
      If the file is large it takes some time to complete and the UI
      freezes.
      <br>
      <br>
      I have to suggestions:
      <br>
      <br>
      OPTION 1:
      <br>
      <br>
      1) Use select a file to upload
      <br>
      2) Close the add volume window and display the volume in progress
      with the progress bar label "Verifying file"
      <br>
      3) Once the request is built, send it and update the volume box
      accordingly
      <br>
      <br>
      OPTION 2:
      <br>
      <br>
      1) Use select a file to upload
      <br>
      2) Disable "Add" button and rename it to: "Verifying file"
      <br>
      3) Once the request is built, send it and close the add volume
      window.
      <br>
      4) Update volume list
      <br>
    </blockquote>
    ACK. Thanks for the testing for large files which I didn't. Option 1
    sounds good.<br>
    <blockquote cite="mid:5413121C.1050804@linux.vnet.ibm.com"
      type="cite">
      <br>
      <blockquote type="cite">+    $(addButton).on('click',
        function(event) {
        <br>
        +        $(this).prop('disabled', true);
        <br>
        +        $(typeRadios).prop('disabled', true);
        <br>
        +        if(type === 'download') {
        <br>
        +            fetchRemoteFile();
        <br>
        +        }
        <br>
        +        else {
        <br>
        +            uploadFile();
        <br>
        +        }
        <br>
        +        event.preventDefault();
        <br>
        +    });
        <br>
        +};
        <br>
        diff --git a/ui/pages/storagepool-add-volume.html.tmpl
        b/ui/pages/storagepool-add-volume.html.tmpl
        <br>
        new file mode 100644
        <br>
        index 0000000..b01c942
        <br>
        --- /dev/null
        <br>
        +++ b/ui/pages/storagepool-add-volume.html.tmpl
        <br>
        @@ -0,0 +1,80 @@
        <br>
        +#*
        <br>
        + * Project Kimchi
        <br>
        + *
        <br>
        + * Copyright IBM, Corp. 2014
        <br>
        + *
        <br>
        + * Authors:
        <br>
        + *  Hongliang Wang <a class="moz-txt-link-rfc2396E" href="mailto:hlwang@linux.vnet.ibm.com">&lt;hlwang@linux.vnet.ibm.com&gt;</a>
        <br>
        + *
        <br>
        + * Licensed under the Apache License, Version 2.0 (the
        "License");
        <br>
        + * you may not use this file except in compliance with the
        License.
        <br>
        + * You may obtain a copy of the License at
        <br>
        + *
        <br>
        + *     <a class="moz-txt-link-freetext" href="http://www.apache.org/licenses/LICENSE-2.0">http://www.apache.org/licenses/LICENSE-2.0</a>
        <br>
        + *
        <br>
        + * Unless required by applicable law or agreed to in writing,
        software
        <br>
        + * distributed under the License is distributed on an "AS IS"
        BASIS,
        <br>
        + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
        or implied.
        <br>
        + * See the License for the specific language governing
        permissions and
        <br>
        + * limitations under the License.
        <br>
        + *#
        <br>
        +#unicode UTF-8
        <br>
        +#import gettext
        <br>
        +#from kimchi.cachebust import href
        <br>
        +#silent t = gettext.translation($lang.domain, $lang.localedir,
        languages=$lang.lang)
        <br>
        +#silent _ = t.gettext
        <br>
        +#silent _t = t.gettext
        <br>
        +&lt;div id="sp-add-volume-window" class="window"&gt;
        <br>
        +    &lt;form id="form-sp-add-volume"&gt;
        <br>
        +        &lt;header class="window-header"&gt;
        <br>
        +            &lt;h1 class="title"&gt;$_("Add a Volume to Storage
        Pool")&lt;/h1&gt;
        <br>
        +            &lt;div class="close"&gt;X&lt;/div&gt;
        <br>
        +        &lt;/header&gt;
        <br>
        +        &lt;section&gt;
        <br>
        +            &lt;div class="content"&gt;
        <br>
        +                &lt;div class="form-section"&gt;
        <br>
        +                    &lt;h2&gt;
        <br>
        +                        &lt;input type="radio"
        id="volume-type-download" class="volume-type" name="volumeType"
        value="download" checked="checked" /&gt;
        <br>
        +                        &lt;label
        for="volume-type-download"&gt;
        <br>
        +                            $_("Fetch from remote URL")
        <br>
        +                        &lt;/label&gt;
        <br>
        +                    &lt;/h2&gt;
        <br>
        +                    &lt;div class="field"&gt;
        <br>
        +                        &lt;p class="text-help"&gt;
        <br>
        +                            $_("Enter the remote URL here.")
        <br>
        +                        &lt;/p&gt;
        <br>
        +                        &lt;div class="textbox-wrapper"&gt;
        <br>
        +                            &lt;input type="text"
        id="volume-remote-url" class="text volume-input download"
        name="volumeRemoteURL" /&gt;
        <br>
        +                        &lt;/div&gt;
        <br>
        +                    &lt;/div&gt;
        <br>
        +                &lt;/div&gt;
        <br>
        +                &lt;div class="form-section"&gt;
        <br>
        +                    &lt;h2&gt;
        <br>
        +                        &lt;input type="radio"
        id="volume-type-upload" class="volume-type" name="volumeType"
        value="upload"/&gt;
        <br>
        +                        &lt;label for="volume-type-upload"&gt;
        <br>
        +                        $_("Upload an file")
        <br>
        +                        &lt;/label&gt;
        <br>
        +                    &lt;/h2&gt;
        <br>
        +                    &lt;div class="field"&gt;
        <br>
        +                        &lt;p class="text-help"&gt;
        <br>
        +                            $_("Choose the ISO file (with .iso
        suffix) you want to upload.")
        <br>
        +                        &lt;/p&gt;
        <br>
        +                        &lt;div class="textbox-wrapper"&gt;
        <br>
        +                            &lt;input type="file"
        class="volume-input upload" id="volume-input-file"
        name="volumeLocalFile" disabled="disabled" /&gt;
        <br>
        +                        &lt;/div&gt;
        <br>
        +                    &lt;/div&gt;
        <br>
        +                &lt;/div&gt;
        <br>
        +            &lt;/div&gt;
        <br>
        +        &lt;/section&gt;
        <br>
        +        &lt;footer&gt;
        <br>
        +            &lt;div class="btn-group"&gt;
        <br>
        +                &lt;button type="submit"
        id="sp-add-volume-button" class="btn-normal"
        disabled="disabled"&gt;
        <br>
        +                    &lt;span
        class="text"&gt;$_("Add")&lt;/span&gt;
        <br>
        +                &lt;/button&gt;
        <br>
        +            &lt;/div&gt;
        <br>
        +        &lt;/footer&gt;
        <br>
        +    &lt;/form&gt;
        <br>
        +&lt;/div&gt;
        <br>
        +&lt;script type="text/javascript"&gt;
        <br>
        +    kimchi.sp_add_volume_main();
        <br>
        +&lt;/script&gt;
        <br>
      </blockquote>
      <br>
    </blockquote>
    <br>
  </body>
</html>