text: "use strict";
  
  var Zmodem = module.exports;
  
  //TODO: Make this usable without require.js or what not.
  window.Zmodem = Zmodem;
  
  Object.assign(
      Zmodem,
      require("./zmodem")
  );
  
  function _check_aborted(session) {
      if (session.aborted()) {
          throw new Zmodem.Error("aborted");
      }
  }
  
  /** Browser-specific tools
   *
   * @exports Browser
   */
  Zmodem.Browser = {
  
      /**
       * Send a batch of files in sequence. The session is left open
       * afterward, which allows for more files to be sent if desired.
       *
       * @param {Zmodem.Session} session - The send session
       *
       * @param {FileList|Array} files - A list of File objects
       *
       * @param {Object} [options]
       * @param {Function} [options.on_offer_response] - Called when an
       * offer response arrives. Arguments are:
       *
       * - (File) - The File object that corresponds to the offer.
       * - (Transfer|undefined) - If the receiver accepts the offer, then
       * this is a Transfer object; otherwise it’s undefined.
       *
       * @param {Function} [options.on_progress] - Called immediately
       * after a chunk of a file is sent. Arguments are:
       *
       * - (File) - The File object that corresponds to the file.
       * - (Transfer) - The Transfer object for the current transfer.
       * - (Uint8Array) - The chunk of data that was just loaded from disk
       * and sent to the receiver.
       *
       * @param {Function} [options.on_file_complete] - Called immediately
       * after the last file packet is sent. Arguments are:
       *
       * - (File) - The File object that corresponds to the file.
       * - (Transfer) - The Transfer object for the now-completed transfer.
       *
       * @return {Promise} A Promise that fulfills when the batch is done.
       *      Note that skipped files are not considered an error condition.
       */
      send_files: function send_files(session, files, options) {
          if (!options) options = {};
  
          //Populate the batch in reverse order to simplify sending
          //the remaining files/bytes components.
          var batch = [];
          var total_size = 0;
          for (var f=files.length - 1; f>=0; f--) {
              var fobj = files[f];
              total_size += fobj.size;
              batch[f] = {
                  obj: fobj,
                  name: fobj.name,
                  size: fobj.size,
                  mtime: new Date(fobj.lastModified),
                  files_remaining: files.length - f,
                  bytes_remaining: total_size,
              };
          }
  
          var file_idx = 0;
          function promise_callback() {
              var cur_b = batch[file_idx];
  
              if (!cur_b) {
                  return Promise.resolve(); //batch done!
              }
  
              file_idx++;
  
              return session.send_offer(cur_b).then( function after_send_offer(xfer) {
                  if (options.on_offer_response) {
                      options.on_offer_response(cur_b.obj, xfer);
                  }
  
                  if (xfer === undefined) {
                      return promise_callback();   //skipped
                  }
  
                  return new Promise( function(res) {
                      var reader = new FileReader();
                      reader.readAsArrayBuffer(cur_b.obj);
  
                      //This really shouldn’t happen … so let’s
                      //blow up if it does.
                      reader.onerror = function reader_onerror(e) {
                          console.error("file read error", e);
                          throw("File read error: " + e);
                      };
  
                      var piece;
                      reader.onprogress = function reader_onprogress(e) {
                          //Some browsers (e.g., Chrome) give partial returns,
                          //while others (e.g., Firefox) don’t.
                          // 只有文件读取完毕才能取到result值，所以此处无效
                          if (e.target.result) {
                              piece = new Uint8Array(e.target.result, xfer.get_offset())
                              _check_aborted(session);
  
                              xfer.send(piece);
  
                              if (options.on_progress) {
                                options.on_progress(cur_b.obj, xfer, piece);
                              }
                          }
                      };
  
                      reader.onload = function reader_onload(e) {
                          piece = new Uint8Array(e.target.result, xfer, piece)
  
                          _check_aborted(session);
  
                          xfer.end(piece).then( function() {
                              if (options.on_progress && piece.length) {
                                  options.on_progress(cur_b.obj, xfer, piece);
                              }
  
                              if (options.on_file_complete) {
                                  options.on_file_complete(cur_b.obj, xfer);
                              }
  
                              //Resolve the current file-send promise with
                              //another promise. That promise resolves immediately
                              //if we’re done, or with another file-send promise
                              //if there’s more to send.
                              res( promise_callback() );
                          } );
                      };
                  } );
              } );
          }
  
          return promise_callback();
      },
  
      /**
       * Prompt a user to save the given packets as a file by injecting an
       * `<a>` element (with `display: none` styling) into the page and
       * calling the element’s `click()`
       * method. The element is removed immediately after.
       *
       * @param {Array} packets - Same as the first argument to [Blob’s constructor](https://developer.mozilla.org/en-US/docs/Web/API/Blob).
       * @param {string} name - The name to give the file.
       */
      save_to_disk: function save_to_disk(packets, name) {
          var blob = new Blob(packets);
          var url = URL.createObjectURL(blob);
  
          var el = document.createElement("a");
          el.style.display = "none";
          el.href = url;
          el.download = name;
          document.body.appendChild(el);
  
          //It seems like a security problem that this actually works;
          //I’d think there would need to be some confirmation before
          //a browser could save arbitrarily many bytes onto the disk.
          //But, hey.
          el.click();
  
          document.body.removeChild(el);
      },
  };
  