src/zmodem_browser.js
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();

                    //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.
                        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() );
                        } );
                    };

                    reader.readAsArrayBuffer(cur_b.obj);
                } );
            } );
        }

        return promise_callback();
    }
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();

                    //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.
                        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() );
                        } );
                    };

                    reader.readAsArrayBuffer(cur_b.obj);
                } );
            } );
        }

        return promise_callback();
    }
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 ( ) ; //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. 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 ( ) ) ; } ) ; } ; reader . readAsArrayBuffer ( cur_b . obj ) ; } ) ; } ) ; } return promise_callback ( ) ; }
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 a batch of files in sequence . The session is left open afterward which allows for more files to be sent if desired .