function Flow(opts) {
    /**
     * Supported by browser?
     * @type {boolean}
     */
    this.support = (
        typeof File !== 'undefined' &&
        typeof Blob !== 'undefined' &&
        typeof FileList !== 'undefined' &&
        (
          !!Blob.prototype.slice || !!Blob.prototype.webkitSlice || !!Blob.prototype.mozSlice ||
          false
        ) // slicing files support
    );

    if (!this.support) {
      return ;
    }

    /**
     * Check if directory upload is supported
     * @type {boolean}
     */
    this.supportDirectory = (
        /Chrome/.test(window.navigator.userAgent) ||
        /Firefox/.test(window.navigator.userAgent) ||
        /Edge/.test(window.navigator.userAgent)
    );

    /**
     * List of FlowFile objects
     * @type {Array.<FlowFile>}
     */
    this.files = [];

    /**
     * Default options for flow.js
     * @type {Object}
     */
    this.defaults = {
      chunkSize: 1024 * 1024,
      forceChunkSize: false,
      simultaneousUploads: 3,
      singleFile: false,
      fileParameterName: 'file',
      progressCallbacksInterval: 500,
      speedSmoothingFactor: 0.1,
      query: {},
      headers: {},
      withCredentials: false,
      preprocess: null,
      method: 'multipart',
      testMethod: 'GET',
      uploadMethod: 'POST',
      prioritizeFirstAndLastChunk: false,
      allowDuplicateUploads: false,
      target: '/',
      testChunks: true,
      generateUniqueIdentifier: null,
      maxChunkRetries: 0,
      chunkRetryInterval: null,
      permanentErrors: [404, 413, 415, 500, 501],
      successStatuses: [200, 201, 202],
      onDropStopPropagation: false,
      initFileFn: null,
      readFileFn: webAPIFileRead
    };

    /**
     * Current options
     * @type {Object}
     */
    this.opts = {};

    /**
     * List of events:
     *  key stands for event name
     *  value array list of callbacks
     * @type {}
     */
    this.events = {};

    var $ = this;

    /**
     * On drop event
     * @function
     * @param {MouseEvent} event
     */
    this.onDrop = function (event) {
      if ($.opts.onDropStopPropagation) {
        event.stopPropagation();
      }
      event.preventDefault();
      var dataTransfer = event.dataTransfer;
      if (dataTransfer.items && dataTransfer.items[0] &&
        dataTransfer.items[0].webkitGetAsEntry) {
        $.webkitReadDataTransfer(event);
      } else {
        $.addFiles(dataTransfer.files, event);
      }
    };

    /**
     * Prevent default
     * @function
     * @param {MouseEvent} event
     */
    this.preventEvent = function (event) {
      event.preventDefault();
    };


    /**
     * Current options
     * @type {Object}
     */
    this.opts = Flow.extend({}, this.defaults, opts || {});

  }
function Flow(opts) {
    /**
     * Supported by browser?
     * @type {boolean}
     */
    this.support = (
        typeof File !== 'undefined' &&
        typeof Blob !== 'undefined' &&
        typeof FileList !== 'undefined' &&
        (
          !!Blob.prototype.slice || !!Blob.prototype.webkitSlice || !!Blob.prototype.mozSlice ||
          false
        ) // slicing files support
    );

    if (!this.support) {
      return ;
    }

    /**
     * Check if directory upload is supported
     * @type {boolean}
     */
    this.supportDirectory = (
        /Chrome/.test(window.navigator.userAgent) ||
        /Firefox/.test(window.navigator.userAgent) ||
        /Edge/.test(window.navigator.userAgent)
    );

    /**
     * List of FlowFile objects
     * @type {Array.<FlowFile>}
     */
    this.files = [];

    /**
     * Default options for flow.js
     * @type {Object}
     */
    this.defaults = {
      chunkSize: 1024 * 1024,
      forceChunkSize: false,
      simultaneousUploads: 3,
      singleFile: false,
      fileParameterName: 'file',
      progressCallbacksInterval: 500,
      speedSmoothingFactor: 0.1,
      query: {},
      headers: {},
      withCredentials: false,
      preprocess: null,
      method: 'multipart',
      testMethod: 'GET',
      uploadMethod: 'POST',
      prioritizeFirstAndLastChunk: false,
      allowDuplicateUploads: false,
      target: '/',
      testChunks: true,
      generateUniqueIdentifier: null,
      maxChunkRetries: 0,
      chunkRetryInterval: null,
      permanentErrors: [404, 413, 415, 500, 501],
      successStatuses: [200, 201, 202],
      onDropStopPropagation: false,
      initFileFn: null,
      readFileFn: webAPIFileRead
    };

    /**
     * Current options
     * @type {Object}
     */
    this.opts = {};

    /**
     * List of events:
     *  key stands for event name
     *  value array list of callbacks
     * @type {}
     */
    this.events = {};

    var $ = this;

    /**
     * On drop event
     * @function
     * @param {MouseEvent} event
     */
    this.onDrop = function (event) {
      if ($.opts.onDropStopPropagation) {
        event.stopPropagation();
      }
      event.preventDefault();
      var dataTransfer = event.dataTransfer;
      if (dataTransfer.items && dataTransfer.items[0] &&
        dataTransfer.items[0].webkitGetAsEntry) {
        $.webkitReadDataTransfer(event);
      } else {
        $.addFiles(dataTransfer.files, event);
      }
    };

    /**
     * Prevent default
     * @function
     * @param {MouseEvent} event
     */
    this.preventEvent = function (event) {
      event.preventDefault();
    };


    /**
     * Current options
     * @type {Object}
     */
    this.opts = Flow.extend({}, this.defaults, opts || {});

  }
function Flow ( opts ) { /**
     * Supported by browser?
     * @type {boolean}
     */ this . support = ( typeof File !== 'undefined' && typeof Blob !== 'undefined' && typeof FileList !== 'undefined' && ( ! ! Blob . prototype . slice || ! ! Blob . prototype . webkitSlice || ! ! Blob . prototype . mozSlice || false ) // slicing files support ) ; if ( ! this . support ) { return ; } /**
     * Check if directory upload is supported
     * @type {boolean}
     */ this . supportDirectory = ( / Chrome / . test ( window . navigator . userAgent ) || / Firefox / . test ( window . navigator . userAgent ) || / Edge / . test ( window . navigator . userAgent ) ) ; /**
     * List of FlowFile objects
     * @type {Array.<FlowFile>}
     */ this . files = [ ] ; /**
     * Default options for flow.js
     * @type {Object}
     */ this . defaults = { chunkSize : 1024 * 1024 , forceChunkSize : false , simultaneousUploads : 3 , singleFile : false , fileParameterName : 'file' , progressCallbacksInterval : 500 , speedSmoothingFactor : 0.1 , query : { } , headers : { } , withCredentials : false , preprocess : null , method : 'multipart' , testMethod : 'GET' , uploadMethod : 'POST' , prioritizeFirstAndLastChunk : false , allowDuplicateUploads : false , target : '/' , testChunks : true , generateUniqueIdentifier : null , maxChunkRetries : 0 , chunkRetryInterval : null , permanentErrors : [ 404 , 413 , 415 , 500 , 501 ] , successStatuses : [ 200 , 201 , 202 ] , onDropStopPropagation : false , initFileFn : null , readFileFn : webAPIFileRead } ; /**
     * Current options
     * @type {Object}
     */ this . opts = { } ; /**
     * List of events:
     *  key stands for event name
     *  value array list of callbacks
     * @type {}
     */ this . events = { } ; var $ = this ; /**
     * On drop event
     * @function
     * @param {MouseEvent} event
     */ this . onDrop = function ( event ) { if ( $ . opts . onDropStopPropagation ) { event . stopPropagation ( ) ; } event . preventDefault ( ) ; var dataTransfer = event . dataTransfer ; if ( dataTransfer . items && dataTransfer . items [ 0 ] && dataTransfer . items [ 0 ] . webkitGetAsEntry ) { $ . webkitReadDataTransfer ( event ) ; } else { $ . addFiles ( dataTransfer . files , event ) ; } } ; /**
     * Prevent default
     * @function
     * @param {MouseEvent} event
     */ this . preventEvent = function ( event ) { event . preventDefault ( ) ; } ; /**
     * Current options
     * @type {Object}
     */ this . opts = Flow . extend ( { } , this . defaults , opts || { } ) ; }
Flow.js is a library providing multiple simultaneous, stable and
resumable uploads via the HTML5 File API.
@param [opts]
@param {number} [opts.chunkSize]
@param {bool} [opts.forceChunkSize]
@param {number} [opts.simultaneousUploads]
@param {bool} [opts.singleFile]
@param {string} [opts.fileParameterName]
@param {number} [opts.progressCallbacksInterval]
@param {number} [opts.speedSmoothingFactor]
@param {Object|Function} [opts.query]
@param {Object|Function} [opts.headers]
@param {bool} [opts.withCredentials]
@param {Function} [opts.preprocess]
@param {string} [opts.method]
@param {string|Function} [opts.testMethod]
@param {string|Function} [opts.uploadMethod]
@param {bool} [opts.prioritizeFirstAndLastChunk]
@param {bool} [opts.allowDuplicateUploads]
@param {string|Function} [opts.target]
@param {number} [opts.maxChunkRetries]
@param {number} [opts.chunkRetryInterval]
@param {Array.<number>} [opts.permanentErrors]
@param {Array.<number>} [opts.successStatuses]
@param {Function} [opts.initFileFn]
@param {Function} [opts.readFileFn]
@param {Function} [opts.generateUniqueIdentifier]
@constructor
Flow . js is a library providing multiple simultaneous stable and resumable uploads via the HTML5 File API .