You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							1610 lines
						
					
					
						
							44 KiB
						
					
					
				
			
		
		
	
	
							1610 lines
						
					
					
						
							44 KiB
						
					
					
				/*! | 
						|
 * Uploader - Uploader library implements html5 file upload and provides multiple simultaneous, stable, fault tolerant and resumable uploads | 
						|
 * @version v0.5.6 | 
						|
 * @author dolymood <dolymood@gmail.com> | 
						|
 * @link https://github.com/simple-uploader/Uploader | 
						|
 * @license MIT | 
						|
 */ | 
						|
!function(e){if("object"==typeof exports)module.exports=e();else if("function"==typeof define&&define.amd)define(e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.Uploader=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){ | 
						|
var utils = _dereq_('./utils') | 
						|
 | 
						|
function Chunk (uploader, file, offset) { | 
						|
  utils.defineNonEnumerable(this, 'uploader', uploader) | 
						|
  utils.defineNonEnumerable(this, 'file', file) | 
						|
  utils.defineNonEnumerable(this, 'bytes', null) | 
						|
  this.offset = offset | 
						|
  this.tested = false | 
						|
  this.retries = 0 | 
						|
  this.pendingRetry = false | 
						|
  this.preprocessState = 0 | 
						|
  this.readState = 0 | 
						|
  this.loaded = 0 | 
						|
  this.total = 0 | 
						|
  this.chunkSize = this.uploader.opts.chunkSize | 
						|
  this.startByte = this.offset * this.chunkSize | 
						|
  this.endByte = this.computeEndByte() | 
						|
  this.xhr = null | 
						|
} | 
						|
 | 
						|
var STATUS = Chunk.STATUS = { | 
						|
  PENDING: 'pending', | 
						|
  UPLOADING: 'uploading', | 
						|
  READING: 'reading', | 
						|
  SUCCESS: 'success', | 
						|
  ERROR: 'error', | 
						|
  COMPLETE: 'complete', | 
						|
  PROGRESS: 'progress', | 
						|
  RETRY: 'retry' | 
						|
} | 
						|
 | 
						|
utils.extend(Chunk.prototype, { | 
						|
 | 
						|
  _event: function (evt, args) { | 
						|
    args = utils.toArray(arguments) | 
						|
    args.unshift(this) | 
						|
    this.file._chunkEvent.apply(this.file, args) | 
						|
  }, | 
						|
 | 
						|
  computeEndByte: function () { | 
						|
    var endByte = Math.min(this.file.size, (this.offset + 1) * this.chunkSize) | 
						|
    if (this.file.size - endByte < this.chunkSize && !this.uploader.opts.forceChunkSize) { | 
						|
      // The last chunk will be bigger than the chunk size, | 
						|
      // but less than 2 * this.chunkSize | 
						|
      endByte = this.file.size | 
						|
    } | 
						|
    return endByte | 
						|
  }, | 
						|
 | 
						|
  getParams: function () { | 
						|
    return { | 
						|
      chunkNumber: this.offset + 1, | 
						|
      chunkSize: this.uploader.opts.chunkSize, | 
						|
      currentChunkSize: this.endByte - this.startByte, | 
						|
      totalSize: this.file.size, | 
						|
      identifier: this.file.uniqueIdentifier, | 
						|
      filename: this.file.name, | 
						|
      relativePath: this.file.relativePath, | 
						|
      totalChunks: this.file.chunks.length | 
						|
    } | 
						|
  }, | 
						|
 | 
						|
  getTarget: function (target, params) { | 
						|
    if (!params.length) { | 
						|
      return target | 
						|
    } | 
						|
    if (target.indexOf('?') < 0) { | 
						|
      target += '?' | 
						|
    } else { | 
						|
      target += '&' | 
						|
    } | 
						|
    return target + params.join('&') | 
						|
  }, | 
						|
 | 
						|
  test: function () { | 
						|
    this.xhr = new XMLHttpRequest() | 
						|
    this.xhr.addEventListener('load', testHandler, false) | 
						|
    this.xhr.addEventListener('error', testHandler, false) | 
						|
    var testMethod = utils.evalOpts(this.uploader.opts.testMethod, this.file, this) | 
						|
    var data = this.prepareXhrRequest(testMethod, true) | 
						|
    this.xhr.send(data) | 
						|
 | 
						|
    var $ = this | 
						|
    function testHandler (event) { | 
						|
      var status = $.status(true) | 
						|
      if (status === STATUS.ERROR) { | 
						|
        $._event(status, $.message()) | 
						|
        $.uploader.uploadNextChunk() | 
						|
      } else if (status === STATUS.SUCCESS) { | 
						|
        $._event(status, $.message()) | 
						|
        $.tested = true | 
						|
      } else if (!$.file.paused) { | 
						|
        // Error might be caused by file pause method | 
						|
        // Chunks does not exist on the server side | 
						|
        $.tested = true | 
						|
        $.send() | 
						|
      } | 
						|
    } | 
						|
  }, | 
						|
 | 
						|
  preprocessFinished: function () { | 
						|
    // Compute the endByte after the preprocess function to allow an | 
						|
    // implementer of preprocess to set the fileObj size | 
						|
    this.endByte = this.computeEndByte() | 
						|
    this.preprocessState = 2 | 
						|
    this.send() | 
						|
  }, | 
						|
 | 
						|
  readFinished: function (bytes) { | 
						|
    this.readState = 2 | 
						|
    this.bytes = bytes | 
						|
    this.send() | 
						|
  }, | 
						|
 | 
						|
  send: function () { | 
						|
    var preprocess = this.uploader.opts.preprocess | 
						|
    var read = this.uploader.opts.readFileFn | 
						|
    if (utils.isFunction(preprocess)) { | 
						|
      switch (this.preprocessState) { | 
						|
        case 0: | 
						|
          this.preprocessState = 1 | 
						|
          preprocess(this) | 
						|
          return | 
						|
        case 1: | 
						|
          return | 
						|
      } | 
						|
    } | 
						|
    switch (this.readState) { | 
						|
      case 0: | 
						|
        this.readState = 1 | 
						|
        read(this.file, this.file.fileType, this.startByte, this.endByte, this) | 
						|
        return | 
						|
      case 1: | 
						|
        return | 
						|
    } | 
						|
    if (this.uploader.opts.testChunks && !this.tested) { | 
						|
      this.test() | 
						|
      return | 
						|
    } | 
						|
 | 
						|
    this.loaded = 0 | 
						|
    this.total = 0 | 
						|
    this.pendingRetry = false | 
						|
 | 
						|
    // Set up request and listen for event | 
						|
    this.xhr = new XMLHttpRequest() | 
						|
    this.xhr.upload.addEventListener('progress', progressHandler, false) | 
						|
    this.xhr.addEventListener('load', doneHandler, false) | 
						|
    this.xhr.addEventListener('error', doneHandler, false) | 
						|
 | 
						|
    var uploadMethod = utils.evalOpts(this.uploader.opts.uploadMethod, this.file, this) | 
						|
    var data = this.prepareXhrRequest(uploadMethod, false, this.uploader.opts.method, this.bytes) | 
						|
    this.xhr.send(data) | 
						|
 | 
						|
    var $ = this | 
						|
    function progressHandler (event) { | 
						|
      if (event.lengthComputable) { | 
						|
        $.loaded = event.loaded | 
						|
        $.total = event.total | 
						|
      } | 
						|
      $._event(STATUS.PROGRESS, event) | 
						|
    } | 
						|
 | 
						|
    function doneHandler (event) { | 
						|
      var msg = $.message() | 
						|
      $.processingResponse = true | 
						|
      $.uploader.opts.processResponse(msg, function (err, res) { | 
						|
        $.processingResponse = false | 
						|
        if (!$.xhr) { | 
						|
          return | 
						|
        } | 
						|
        $.processedState = { | 
						|
          err: err, | 
						|
          res: res | 
						|
        } | 
						|
        var status = $.status() | 
						|
        if (status === STATUS.SUCCESS || status === STATUS.ERROR) { | 
						|
          // delete this.data | 
						|
          $._event(status, res) | 
						|
          status === STATUS.ERROR && $.uploader.uploadNextChunk() | 
						|
        } else { | 
						|
          $._event(STATUS.RETRY, res) | 
						|
          $.pendingRetry = true | 
						|
          $.abort() | 
						|
          $.retries++ | 
						|
          var retryInterval = $.uploader.opts.chunkRetryInterval | 
						|
          if (retryInterval !== null) { | 
						|
            setTimeout(function () { | 
						|
              $.send() | 
						|
            }, retryInterval) | 
						|
          } else { | 
						|
            $.send() | 
						|
          } | 
						|
        } | 
						|
      }, $.file, $) | 
						|
    } | 
						|
  }, | 
						|
 | 
						|
  abort: function () { | 
						|
    var xhr = this.xhr | 
						|
    this.xhr = null | 
						|
    this.processingResponse = false | 
						|
    this.processedState = null | 
						|
    if (xhr) { | 
						|
      xhr.abort() | 
						|
    } | 
						|
  }, | 
						|
 | 
						|
  status: function (isTest) { | 
						|
    if (this.readState === 1) { | 
						|
      return STATUS.READING | 
						|
    } else if (this.pendingRetry || this.preprocessState === 1) { | 
						|
      // if pending retry then that's effectively the same as actively uploading, | 
						|
      // there might just be a slight delay before the retry starts | 
						|
      return STATUS.UPLOADING | 
						|
    } else if (!this.xhr) { | 
						|
      return STATUS.PENDING | 
						|
    } else if (this.xhr.readyState < 4 || this.processingResponse) { | 
						|
      // Status is really 'OPENED', 'HEADERS_RECEIVED' | 
						|
      // or 'LOADING' - meaning that stuff is happening | 
						|
      return STATUS.UPLOADING | 
						|
    } else { | 
						|
      var _status | 
						|
      if (this.uploader.opts.successStatuses.indexOf(this.xhr.status) > -1) { | 
						|
        // HTTP 200, perfect | 
						|
        // HTTP 202 Accepted - The request has been accepted for processing, but the processing has not been completed. | 
						|
        _status = STATUS.SUCCESS | 
						|
      } else if (this.uploader.opts.permanentErrors.indexOf(this.xhr.status) > -1 || | 
						|
          !isTest && this.retries >= this.uploader.opts.maxChunkRetries) { | 
						|
        // HTTP 415/500/501, permanent error | 
						|
        _status = STATUS.ERROR | 
						|
      } else { | 
						|
        // this should never happen, but we'll reset and queue a retry | 
						|
        // a likely case for this would be 503 service unavailable | 
						|
        this.abort() | 
						|
        _status = STATUS.PENDING | 
						|
      } | 
						|
      var processedState = this.processedState | 
						|
      if (processedState && processedState.err) { | 
						|
        _status = STATUS.ERROR | 
						|
      } | 
						|
      return _status | 
						|
    } | 
						|
  }, | 
						|
 | 
						|
  message: function () { | 
						|
    return this.xhr ? this.xhr.responseText : '' | 
						|
  }, | 
						|
 | 
						|
  progress: function () { | 
						|
    if (this.pendingRetry) { | 
						|
      return 0 | 
						|
    } | 
						|
    var s = this.status() | 
						|
    if (s === STATUS.SUCCESS || s === STATUS.ERROR) { | 
						|
      return 1 | 
						|
    } else if (s === STATUS.PENDING) { | 
						|
      return 0 | 
						|
    } else { | 
						|
      return this.total > 0 ? this.loaded / this.total : 0 | 
						|
    } | 
						|
  }, | 
						|
 | 
						|
  sizeUploaded: function () { | 
						|
    var size = this.endByte - this.startByte | 
						|
    // can't return only chunk.loaded value, because it is bigger than chunk size | 
						|
    if (this.status() !== STATUS.SUCCESS) { | 
						|
      size = this.progress() * size | 
						|
    } | 
						|
    return size | 
						|
  }, | 
						|
 | 
						|
  prepareXhrRequest: function (method, isTest, paramsMethod, blob) { | 
						|
    // Add data from the query options | 
						|
    var query = utils.evalOpts(this.uploader.opts.query, this.file, this, isTest) | 
						|
    query = utils.extend(this.getParams(), query) | 
						|
 | 
						|
    // processParams | 
						|
    query = this.uploader.opts.processParams(query, this.file, this, isTest) | 
						|
 | 
						|
    var target = utils.evalOpts(this.uploader.opts.target, this.file, this, isTest) | 
						|
    var data = null | 
						|
    if (method === 'GET' || paramsMethod === 'octet') { | 
						|
      // Add data from the query options | 
						|
      var params = [] | 
						|
      utils.each(query, function (v, k) { | 
						|
        params.push([encodeURIComponent(k), encodeURIComponent(v)].join('=')) | 
						|
      }) | 
						|
      target = this.getTarget(target, params) | 
						|
      data = blob || null | 
						|
    } else { | 
						|
      // Add data from the query options | 
						|
      data = new FormData() | 
						|
      utils.each(query, function (v, k) { | 
						|
        data.append(k, v) | 
						|
      }) | 
						|
      if (typeof blob !== 'undefined') { | 
						|
        data.append(this.uploader.opts.fileParameterName, blob, this.file.name) | 
						|
      } | 
						|
    } | 
						|
 | 
						|
    this.xhr.open(method, target, true) | 
						|
    this.xhr.withCredentials = this.uploader.opts.withCredentials | 
						|
 | 
						|
    // Add data from header options | 
						|
    utils.each(utils.evalOpts(this.uploader.opts.headers, this.file, this, isTest), function (v, k) { | 
						|
      this.xhr.setRequestHeader(k, v) | 
						|
    }, this) | 
						|
 | 
						|
    return data | 
						|
  } | 
						|
 | 
						|
}) | 
						|
 | 
						|
module.exports = Chunk | 
						|
 | 
						|
},{"./utils":5}],2:[function(_dereq_,module,exports){ | 
						|
var each = _dereq_('./utils').each | 
						|
 | 
						|
var event = { | 
						|
 | 
						|
  _eventData: null, | 
						|
 | 
						|
  on: function (name, func) { | 
						|
    if (!this._eventData) this._eventData = {} | 
						|
    if (!this._eventData[name]) this._eventData[name] = [] | 
						|
    var listened = false | 
						|
    each(this._eventData[name], function (fuc) { | 
						|
      if (fuc === func) { | 
						|
        listened = true | 
						|
        return false | 
						|
      } | 
						|
    }) | 
						|
    if (!listened) { | 
						|
      this._eventData[name].push(func) | 
						|
    } | 
						|
  }, | 
						|
 | 
						|
  off: function (name, func) { | 
						|
    if (!this._eventData) this._eventData = {} | 
						|
    if (!this._eventData[name] || !this._eventData[name].length) return | 
						|
    if (func) { | 
						|
      each(this._eventData[name], function (fuc, i) { | 
						|
        if (fuc === func) { | 
						|
          this._eventData[name].splice(i, 1) | 
						|
          return false | 
						|
        } | 
						|
      }, this) | 
						|
    } else { | 
						|
      this._eventData[name] = [] | 
						|
    } | 
						|
  }, | 
						|
 | 
						|
  trigger: function (name) { | 
						|
    if (!this._eventData) this._eventData = {} | 
						|
    if (!this._eventData[name]) return true | 
						|
    var args = this._eventData[name].slice.call(arguments, 1) | 
						|
    var preventDefault = false | 
						|
    each(this._eventData[name], function (fuc) { | 
						|
      preventDefault = fuc.apply(this, args) === false || preventDefault | 
						|
    }, this) | 
						|
    return !preventDefault | 
						|
  } | 
						|
} | 
						|
 | 
						|
module.exports = event | 
						|
 | 
						|
},{"./utils":5}],3:[function(_dereq_,module,exports){ | 
						|
var utils = _dereq_('./utils') | 
						|
var event = _dereq_('./event') | 
						|
var File = _dereq_('./file') | 
						|
var Chunk = _dereq_('./chunk') | 
						|
 | 
						|
var version = '0.5.6' | 
						|
 | 
						|
var isServer = typeof window === 'undefined' | 
						|
 | 
						|
// ie10+ | 
						|
var ie10plus = isServer ? false : window.navigator.msPointerEnabled | 
						|
var support = (function () { | 
						|
  if (isServer) { | 
						|
    return false | 
						|
  } | 
						|
  var sliceName = 'slice' | 
						|
  var _support = utils.isDefined(window.File) && utils.isDefined(window.Blob) && | 
						|
                utils.isDefined(window.FileList) | 
						|
  var bproto = null | 
						|
  if (_support) { | 
						|
    bproto = window.Blob.prototype | 
						|
    utils.each(['slice', 'webkitSlice', 'mozSlice'], function (n) { | 
						|
      if (bproto[n]) { | 
						|
        sliceName = n | 
						|
        return false | 
						|
      } | 
						|
    }) | 
						|
    _support = !!bproto[sliceName] | 
						|
  } | 
						|
  if (_support) Uploader.sliceName = sliceName | 
						|
  bproto = null | 
						|
  return _support | 
						|
})() | 
						|
 | 
						|
var supportDirectory = (function () { | 
						|
  if (isServer) { | 
						|
    return false | 
						|
  } | 
						|
  var input = window.document.createElement('input') | 
						|
  input.type = 'file' | 
						|
  var sd = 'webkitdirectory' in input || 'directory' in input | 
						|
  input = null | 
						|
  return sd | 
						|
})() | 
						|
 | 
						|
function Uploader (opts) { | 
						|
  this.support = support | 
						|
  /* istanbul ignore if */ | 
						|
  if (!this.support) { | 
						|
    return | 
						|
  } | 
						|
  this.supportDirectory = supportDirectory | 
						|
  utils.defineNonEnumerable(this, 'filePaths', {}) | 
						|
  this.opts = utils.extend({}, Uploader.defaults, opts || {}) | 
						|
 | 
						|
  this.preventEvent = utils.bind(this._preventEvent, this) | 
						|
 | 
						|
  File.call(this, this) | 
						|
} | 
						|
 | 
						|
/** | 
						|
 * Default read function using the webAPI | 
						|
 * | 
						|
 * @function webAPIFileRead(fileObj, fileType, startByte, endByte, chunk) | 
						|
 * | 
						|
 */ | 
						|
var webAPIFileRead = function (fileObj, fileType, startByte, endByte, chunk) { | 
						|
  chunk.readFinished(fileObj.file[Uploader.sliceName](startByte, endByte, fileType)) | 
						|
} | 
						|
 | 
						|
Uploader.version = version | 
						|
 | 
						|
Uploader.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, 415, 500, 501], | 
						|
  successStatuses: [200, 201, 202], | 
						|
  onDropStopPropagation: false, | 
						|
  initFileFn: null, | 
						|
  readFileFn: webAPIFileRead, | 
						|
  checkChunkUploadedByResponse: null, | 
						|
  initialPaused: false, | 
						|
  processResponse: function (response, cb) { | 
						|
    cb(null, response) | 
						|
  }, | 
						|
  processParams: function (params) { | 
						|
    return params | 
						|
  } | 
						|
} | 
						|
 | 
						|
Uploader.utils = utils | 
						|
Uploader.event = event | 
						|
Uploader.File = File | 
						|
Uploader.Chunk = Chunk | 
						|
 | 
						|
// inherit file | 
						|
Uploader.prototype = utils.extend({}, File.prototype) | 
						|
// inherit event | 
						|
utils.extend(Uploader.prototype, event) | 
						|
utils.extend(Uploader.prototype, { | 
						|
 | 
						|
  constructor: Uploader, | 
						|
 | 
						|
  _trigger: function (name) { | 
						|
    var args = utils.toArray(arguments) | 
						|
    var preventDefault = !this.trigger.apply(this, arguments) | 
						|
    if (name !== 'catchAll') { | 
						|
      args.unshift('catchAll') | 
						|
      preventDefault = !this.trigger.apply(this, args) || preventDefault | 
						|
    } | 
						|
    return !preventDefault | 
						|
  }, | 
						|
 | 
						|
  _triggerAsync: function () { | 
						|
    var args = arguments | 
						|
    utils.nextTick(function () { | 
						|
      this._trigger.apply(this, args) | 
						|
    }, this) | 
						|
  }, | 
						|
 | 
						|
  addFiles: function (files, evt) { | 
						|
    var _files = [] | 
						|
    var oldFileListLen = this.fileList.length | 
						|
    utils.each(files, function (file) { | 
						|
      // Uploading empty file IE10/IE11 hangs indefinitely | 
						|
      // Directories have size `0` and name `.` | 
						|
      // Ignore already added files if opts.allowDuplicateUploads is set to false | 
						|
      if ((!ie10plus || ie10plus && file.size > 0) && !(file.size % 4096 === 0 && (file.name === '.' || file.fileName === '.'))) { | 
						|
        var uniqueIdentifier = this.generateUniqueIdentifier(file) | 
						|
        if (this.opts.allowDuplicateUploads || !this.getFromUniqueIdentifier(uniqueIdentifier)) { | 
						|
          var _file = new File(this, file, this) | 
						|
          _file.uniqueIdentifier = uniqueIdentifier | 
						|
          if (this._trigger('fileAdded', _file, evt)) { | 
						|
            _files.push(_file) | 
						|
          } else { | 
						|
            File.prototype.removeFile.call(this, _file) | 
						|
          } | 
						|
        } | 
						|
      } | 
						|
    }, this) | 
						|
    // get new fileList | 
						|
    var newFileList = this.fileList.slice(oldFileListLen) | 
						|
    if (this._trigger('filesAdded', _files, newFileList, evt)) { | 
						|
      utils.each(_files, function (file) { | 
						|
        if (this.opts.singleFile && this.files.length > 0) { | 
						|
          this.removeFile(this.files[0]) | 
						|
        } | 
						|
        this.files.push(file) | 
						|
      }, this) | 
						|
      this._trigger('filesSubmitted', _files, newFileList, evt) | 
						|
    } else { | 
						|
      utils.each(newFileList, function (file) { | 
						|
        File.prototype.removeFile.call(this, file) | 
						|
      }, this) | 
						|
    } | 
						|
  }, | 
						|
 | 
						|
  addFile: function (file, evt) { | 
						|
    this.addFiles([file], evt) | 
						|
  }, | 
						|
 | 
						|
  cancel: function () { | 
						|
    for (var i = this.fileList.length - 1; i >= 0; i--) { | 
						|
      this.fileList[i].cancel() | 
						|
    } | 
						|
  }, | 
						|
 | 
						|
  removeFile: function (file) { | 
						|
    File.prototype.removeFile.call(this, file) | 
						|
    this._trigger('fileRemoved', file) | 
						|
  }, | 
						|
 | 
						|
  generateUniqueIdentifier: function (file) { | 
						|
    var custom = this.opts.generateUniqueIdentifier | 
						|
    if (utils.isFunction(custom)) { | 
						|
      return custom(file) | 
						|
    } | 
						|
    /* istanbul ignore next */ | 
						|
    // Some confusion in different versions of Firefox | 
						|
    var relativePath = file.relativePath || file.webkitRelativePath || file.fileName || file.name | 
						|
    /* istanbul ignore next */ | 
						|
    return file.size + '-' + relativePath.replace(/[^0-9a-zA-Z_-]/img, '') | 
						|
  }, | 
						|
 | 
						|
  getFromUniqueIdentifier: function (uniqueIdentifier) { | 
						|
    var ret = false | 
						|
    utils.each(this.files, function (file) { | 
						|
      if (file.uniqueIdentifier === uniqueIdentifier) { | 
						|
        ret = file | 
						|
        return false | 
						|
      } | 
						|
    }) | 
						|
    return ret | 
						|
  }, | 
						|
 | 
						|
  uploadNextChunk: function (preventEvents) { | 
						|
    var found = false | 
						|
    var pendingStatus = Chunk.STATUS.PENDING | 
						|
    var checkChunkUploaded = this.uploader.opts.checkChunkUploadedByResponse | 
						|
    if (this.opts.prioritizeFirstAndLastChunk) { | 
						|
      utils.each(this.files, function (file) { | 
						|
        if (file.paused) { | 
						|
          return | 
						|
        } | 
						|
        if (checkChunkUploaded && !file._firstResponse && file.isUploading()) { | 
						|
          // waiting for current file's first chunk response | 
						|
          return | 
						|
        } | 
						|
        if (file.chunks.length && file.chunks[0].status() === pendingStatus) { | 
						|
          file.chunks[0].send() | 
						|
          found = true | 
						|
          return false | 
						|
        } | 
						|
        if (file.chunks.length > 1 && file.chunks[file.chunks.length - 1].status() === pendingStatus) { | 
						|
          file.chunks[file.chunks.length - 1].send() | 
						|
          found = true | 
						|
          return false | 
						|
        } | 
						|
      }) | 
						|
      if (found) { | 
						|
        return found | 
						|
      } | 
						|
    } | 
						|
 | 
						|
    // Now, simply look for the next, best thing to upload | 
						|
    utils.each(this.files, function (file) { | 
						|
      if (!file.paused) { | 
						|
        if (checkChunkUploaded && !file._firstResponse && file.isUploading()) { | 
						|
          // waiting for current file's first chunk response | 
						|
          return | 
						|
        } | 
						|
        utils.each(file.chunks, function (chunk) { | 
						|
          if (chunk.status() === pendingStatus) { | 
						|
            chunk.send() | 
						|
            found = true | 
						|
            return false | 
						|
          } | 
						|
        }) | 
						|
      } | 
						|
      if (found) { | 
						|
        return false | 
						|
      } | 
						|
    }) | 
						|
    if (found) { | 
						|
      return true | 
						|
    } | 
						|
 | 
						|
    // The are no more outstanding chunks to upload, check is everything is done | 
						|
    var outstanding = false | 
						|
    utils.each(this.files, function (file) { | 
						|
      if (!file.isComplete()) { | 
						|
        outstanding = true | 
						|
        return false | 
						|
      } | 
						|
    }) | 
						|
    // should check files now | 
						|
    // if now files in list | 
						|
    // should not trigger complete event | 
						|
    if (!outstanding && !preventEvents && this.files.length) { | 
						|
      // All chunks have been uploaded, complete | 
						|
      this._triggerAsync('complete') | 
						|
    } | 
						|
    return outstanding | 
						|
  }, | 
						|
 | 
						|
  upload: function (preventEvents) { | 
						|
    // Make sure we don't start too many uploads at once | 
						|
    var ret = this._shouldUploadNext() | 
						|
    if (ret === false) { | 
						|
      return | 
						|
    } | 
						|
    !preventEvents && this._trigger('uploadStart') | 
						|
    var started = false | 
						|
    for (var num = 1; num <= this.opts.simultaneousUploads - ret; num++) { | 
						|
      started = this.uploadNextChunk(!preventEvents) || started | 
						|
      if (!started && preventEvents) { | 
						|
        // completed | 
						|
        break | 
						|
      } | 
						|
    } | 
						|
    if (!started && !preventEvents) { | 
						|
      this._triggerAsync('complete') | 
						|
    } | 
						|
  }, | 
						|
 | 
						|
  /** | 
						|
   * should upload next chunk | 
						|
   * @function | 
						|
   * @returns {Boolean|Number} | 
						|
   */ | 
						|
  _shouldUploadNext: function () { | 
						|
    var num = 0 | 
						|
    var should = true | 
						|
    var simultaneousUploads = this.opts.simultaneousUploads | 
						|
    var uploadingStatus = Chunk.STATUS.UPLOADING | 
						|
    utils.each(this.files, function (file) { | 
						|
      utils.each(file.chunks, function (chunk) { | 
						|
        if (chunk.status() === uploadingStatus) { | 
						|
          num++ | 
						|
          if (num >= simultaneousUploads) { | 
						|
            should = false | 
						|
            return false | 
						|
          } | 
						|
        } | 
						|
      }) | 
						|
      return should | 
						|
    }) | 
						|
    // if should is true then return uploading chunks's length | 
						|
    return should && num | 
						|
  }, | 
						|
 | 
						|
  /** | 
						|
   * Assign a browse action to one or more DOM nodes. | 
						|
   * @function | 
						|
   * @param {Element|Array.<Element>} domNodes | 
						|
   * @param {boolean} isDirectory Pass in true to allow directories to | 
						|
   * @param {boolean} singleFile prevent multi file upload | 
						|
   * @param {Object} attributes set custom attributes: | 
						|
   *  http://www.w3.org/TR/html-markup/input.file.html#input.file-attributes | 
						|
   *  eg: accept: 'image/*' | 
						|
   * be selected (Chrome only). | 
						|
   */ | 
						|
  assignBrowse: function (domNodes, isDirectory, singleFile, attributes) { | 
						|
    if (typeof domNodes.length === 'undefined') { | 
						|
      domNodes = [domNodes] | 
						|
    } | 
						|
 | 
						|
    utils.each(domNodes, function (domNode) { | 
						|
      var input | 
						|
      if (domNode.tagName === 'INPUT' && domNode.type === 'file') { | 
						|
        input = domNode | 
						|
      } else { | 
						|
        input = document.createElement('input') | 
						|
        input.setAttribute('type', 'file') | 
						|
        // display:none - not working in opera 12 | 
						|
        utils.extend(input.style, { | 
						|
          visibility: 'hidden', | 
						|
          position: 'absolute', | 
						|
          width: '1px', | 
						|
          height: '1px' | 
						|
        }) | 
						|
        // for opera 12 browser, input must be assigned to a document | 
						|
        domNode.appendChild(input) | 
						|
        // https://developer.mozilla.org/en/using_files_from_web_applications) | 
						|
        // event listener is executed two times | 
						|
        // first one - original mouse click event | 
						|
        // second - input.click(), input is inside domNode | 
						|
        domNode.addEventListener('click', function (e) { | 
						|
          if (domNode.tagName.toLowerCase() === 'label') { | 
						|
            return | 
						|
          } | 
						|
          input.click() | 
						|
        }, false) | 
						|
      } | 
						|
      if (!this.opts.singleFile && !singleFile) { | 
						|
        input.setAttribute('multiple', 'multiple') | 
						|
      } | 
						|
      if (isDirectory) { | 
						|
        input.setAttribute('webkitdirectory', 'webkitdirectory') | 
						|
      } | 
						|
      attributes && utils.each(attributes, function (value, key) { | 
						|
        input.setAttribute(key, value) | 
						|
      }) | 
						|
      // When new files are added, simply append them to the overall list | 
						|
      var that = this | 
						|
      input.addEventListener('change', function (e) { | 
						|
        that._trigger(e.type, e) | 
						|
        if (e.target.value) { | 
						|
          that.addFiles(e.target.files, e) | 
						|
          e.target.value = '' | 
						|
        } | 
						|
      }, false) | 
						|
    }, this) | 
						|
  }, | 
						|
 | 
						|
  onDrop: function (evt) { | 
						|
    this._trigger(evt.type, evt) | 
						|
    if (this.opts.onDropStopPropagation) { | 
						|
      evt.stopPropagation() | 
						|
    } | 
						|
    evt.preventDefault() | 
						|
    this._parseDataTransfer(evt.dataTransfer, evt) | 
						|
  }, | 
						|
 | 
						|
  _parseDataTransfer: function (dataTransfer, evt) { | 
						|
    if (dataTransfer.items && dataTransfer.items[0] && | 
						|
      dataTransfer.items[0].webkitGetAsEntry) { | 
						|
      this.webkitReadDataTransfer(dataTransfer, evt) | 
						|
    } else { | 
						|
      this.addFiles(dataTransfer.files, evt) | 
						|
    } | 
						|
  }, | 
						|
 | 
						|
  webkitReadDataTransfer: function (dataTransfer, evt) { | 
						|
    var self = this | 
						|
    var queue = dataTransfer.items.length | 
						|
    var files = [] | 
						|
    utils.each(dataTransfer.items, function (item) { | 
						|
      var entry = item.webkitGetAsEntry() | 
						|
      if (!entry) { | 
						|
        decrement() | 
						|
        return | 
						|
      } | 
						|
      if (entry.isFile) { | 
						|
        // due to a bug in Chrome's File System API impl - #149735 | 
						|
        fileReadSuccess(item.getAsFile(), entry.fullPath) | 
						|
      } else { | 
						|
        readDirectory(entry.createReader()) | 
						|
      } | 
						|
    }) | 
						|
    function readDirectory (reader) { | 
						|
      reader.readEntries(function (entries) { | 
						|
        if (entries.length) { | 
						|
          queue += entries.length | 
						|
          utils.each(entries, function (entry) { | 
						|
            if (entry.isFile) { | 
						|
              var fullPath = entry.fullPath | 
						|
              entry.file(function (file) { | 
						|
                fileReadSuccess(file, fullPath) | 
						|
              }, readError) | 
						|
            } else if (entry.isDirectory) { | 
						|
              readDirectory(entry.createReader()) | 
						|
            } | 
						|
          }) | 
						|
          readDirectory(reader) | 
						|
        } else { | 
						|
          decrement() | 
						|
        } | 
						|
      }, readError) | 
						|
    } | 
						|
    function fileReadSuccess (file, fullPath) { | 
						|
      // relative path should not start with "/" | 
						|
      file.relativePath = fullPath.substring(1) | 
						|
      files.push(file) | 
						|
      decrement() | 
						|
    } | 
						|
    function readError (fileError) { | 
						|
      throw fileError | 
						|
    } | 
						|
    function decrement () { | 
						|
      if (--queue === 0) { | 
						|
        self.addFiles(files, evt) | 
						|
      } | 
						|
    } | 
						|
  }, | 
						|
 | 
						|
  _assignHelper: function (domNodes, handles, remove) { | 
						|
    if (typeof domNodes.length === 'undefined') { | 
						|
      domNodes = [domNodes] | 
						|
    } | 
						|
    var evtMethod = remove ? 'removeEventListener' : 'addEventListener' | 
						|
    utils.each(domNodes, function (domNode) { | 
						|
      utils.each(handles, function (handler, name) { | 
						|
        domNode[evtMethod](name, handler, false) | 
						|
      }, this) | 
						|
    }, this) | 
						|
  }, | 
						|
 | 
						|
  _preventEvent: function (e) { | 
						|
    utils.preventEvent(e) | 
						|
    this._trigger(e.type, e) | 
						|
  }, | 
						|
 | 
						|
  /** | 
						|
   * Assign one or more DOM nodes as a drop target. | 
						|
   * @function | 
						|
   * @param {Element|Array.<Element>} domNodes | 
						|
   */ | 
						|
  assignDrop: function (domNodes) { | 
						|
    this._onDrop = utils.bind(this.onDrop, this) | 
						|
    this._assignHelper(domNodes, { | 
						|
      dragover: this.preventEvent, | 
						|
      dragenter: this.preventEvent, | 
						|
      dragleave: this.preventEvent, | 
						|
      drop: this._onDrop | 
						|
    }) | 
						|
  }, | 
						|
 | 
						|
  /** | 
						|
   * Un-assign drop event from DOM nodes | 
						|
   * @function | 
						|
   * @param domNodes | 
						|
   */ | 
						|
  unAssignDrop: function (domNodes) { | 
						|
    this._assignHelper(domNodes, { | 
						|
      dragover: this.preventEvent, | 
						|
      dragenter: this.preventEvent, | 
						|
      dragleave: this.preventEvent, | 
						|
      drop: this._onDrop | 
						|
    }, true) | 
						|
    this._onDrop = null | 
						|
  } | 
						|
}) | 
						|
 | 
						|
module.exports = Uploader | 
						|
 | 
						|
},{"./chunk":1,"./event":2,"./file":4,"./utils":5}],4:[function(_dereq_,module,exports){ | 
						|
var utils = _dereq_('./utils') | 
						|
var Chunk = _dereq_('./chunk') | 
						|
 | 
						|
function File (uploader, file, parent) { | 
						|
  utils.defineNonEnumerable(this, 'uploader', uploader) | 
						|
  this.isRoot = this.isFolder = uploader === this | 
						|
  utils.defineNonEnumerable(this, 'parent', parent || null) | 
						|
  utils.defineNonEnumerable(this, 'files', []) | 
						|
  utils.defineNonEnumerable(this, 'fileList', []) | 
						|
  utils.defineNonEnumerable(this, 'chunks', []) | 
						|
  utils.defineNonEnumerable(this, '_errorFiles', []) | 
						|
  utils.defineNonEnumerable(this, 'file', null) | 
						|
  this.id = utils.uid() | 
						|
 | 
						|
  if (this.isRoot || !file) { | 
						|
    this.file = null | 
						|
  } else { | 
						|
    if (utils.isString(file)) { | 
						|
      // folder | 
						|
      this.isFolder = true | 
						|
      this.file = null | 
						|
      this.path = file | 
						|
      if (this.parent.path) { | 
						|
        file = file.substr(this.parent.path.length) | 
						|
      } | 
						|
      this.name = file.charAt(file.length - 1) === '/' ? file.substr(0, file.length - 1) : file | 
						|
    } else { | 
						|
      this.file = file | 
						|
      this.fileType = this.file.type | 
						|
      this.name = file.fileName || file.name | 
						|
      this.size = file.size | 
						|
      this.relativePath = file.relativePath || file.webkitRelativePath || this.name | 
						|
      this._parseFile() | 
						|
    } | 
						|
  } | 
						|
 | 
						|
  this.paused = uploader.opts.initialPaused | 
						|
  this.error = false | 
						|
  this.allError = false | 
						|
  this.aborted = false | 
						|
  this.completed = false | 
						|
  this.averageSpeed = 0 | 
						|
  this.currentSpeed = 0 | 
						|
  this._lastProgressCallback = Date.now() | 
						|
  this._prevUploadedSize = 0 | 
						|
  this._prevProgress = 0 | 
						|
 | 
						|
  this.bootstrap() | 
						|
} | 
						|
 | 
						|
utils.extend(File.prototype, { | 
						|
 | 
						|
  _parseFile: function () { | 
						|
    var ppaths = parsePaths(this.relativePath) | 
						|
    if (ppaths.length) { | 
						|
      var filePaths = this.uploader.filePaths | 
						|
      utils.each(ppaths, function (path, i) { | 
						|
        var folderFile = filePaths[path] | 
						|
        if (!folderFile) { | 
						|
          folderFile = new File(this.uploader, path, this.parent) | 
						|
          filePaths[path] = folderFile | 
						|
          this._updateParentFileList(folderFile) | 
						|
        } | 
						|
        this.parent = folderFile | 
						|
        folderFile.files.push(this) | 
						|
        if (!ppaths[i + 1]) { | 
						|
          folderFile.fileList.push(this) | 
						|
        } | 
						|
      }, this) | 
						|
    } else { | 
						|
      this._updateParentFileList() | 
						|
    } | 
						|
  }, | 
						|
 | 
						|
  _updateParentFileList: function (file) { | 
						|
    if (!file) { | 
						|
      file = this | 
						|
    } | 
						|
    var p = this.parent | 
						|
    if (p) { | 
						|
      p.fileList.push(file) | 
						|
    } | 
						|
  }, | 
						|
 | 
						|
  _eachAccess: function (eachFn, fileFn) { | 
						|
    if (this.isFolder) { | 
						|
      utils.each(this.files, function (f, i) { | 
						|
        return eachFn.call(this, f, i) | 
						|
      }, this) | 
						|
      return | 
						|
    } | 
						|
    fileFn.call(this, this) | 
						|
  }, | 
						|
 | 
						|
  bootstrap: function () { | 
						|
    if (this.isFolder) return | 
						|
    var opts = this.uploader.opts | 
						|
    if (utils.isFunction(opts.initFileFn)) { | 
						|
      opts.initFileFn.call(this, this) | 
						|
    } | 
						|
 | 
						|
    this.abort(true) | 
						|
    this._resetError() | 
						|
    // Rebuild stack of chunks from file | 
						|
    this._prevProgress = 0 | 
						|
    var round = opts.forceChunkSize ? Math.ceil : Math.floor | 
						|
    var chunks = Math.max(round(this.size / opts.chunkSize), 1) | 
						|
    for (var offset = 0; offset < chunks; offset++) { | 
						|
      this.chunks.push(new Chunk(this.uploader, this, offset)) | 
						|
    } | 
						|
  }, | 
						|
 | 
						|
  _measureSpeed: function () { | 
						|
    var smoothingFactor = this.uploader.opts.speedSmoothingFactor | 
						|
    var timeSpan = Date.now() - this._lastProgressCallback | 
						|
    if (!timeSpan) { | 
						|
      return | 
						|
    } | 
						|
    var uploaded = this.sizeUploaded() | 
						|
    // Prevent negative upload speed after file upload resume | 
						|
    this.currentSpeed = Math.max((uploaded - this._prevUploadedSize) / timeSpan * 1000, 0) | 
						|
    this.averageSpeed = smoothingFactor * this.currentSpeed + (1 - smoothingFactor) * this.averageSpeed | 
						|
    this._prevUploadedSize = uploaded | 
						|
    if (this.parent && this.parent._checkProgress()) { | 
						|
      this.parent._measureSpeed() | 
						|
    } | 
						|
  }, | 
						|
 | 
						|
  _checkProgress: function (file) { | 
						|
    return Date.now() - this._lastProgressCallback >= this.uploader.opts.progressCallbacksInterval | 
						|
  }, | 
						|
 | 
						|
  _chunkEvent: function (chunk, evt, message) { | 
						|
    var uploader = this.uploader | 
						|
    var STATUS = Chunk.STATUS | 
						|
    var that = this | 
						|
    var rootFile = this.getRoot() | 
						|
    var triggerProgress = function () { | 
						|
      that._measureSpeed() | 
						|
      uploader._trigger('fileProgress', rootFile, that, chunk) | 
						|
      that._lastProgressCallback = Date.now() | 
						|
    } | 
						|
    switch (evt) { | 
						|
      case STATUS.PROGRESS: | 
						|
        if (this._checkProgress()) { | 
						|
          triggerProgress() | 
						|
        } | 
						|
        break | 
						|
      case STATUS.ERROR: | 
						|
        this._error() | 
						|
        this.abort(true) | 
						|
        uploader._trigger('fileError', rootFile, this, message, chunk) | 
						|
        break | 
						|
      case STATUS.SUCCESS: | 
						|
        this._updateUploadedChunks(message, chunk) | 
						|
        if (this.error) { | 
						|
          return | 
						|
        } | 
						|
        clearTimeout(this._progeressId) | 
						|
        this._progeressId = 0 | 
						|
        var timeDiff = Date.now() - this._lastProgressCallback | 
						|
        if (timeDiff < uploader.opts.progressCallbacksInterval) { | 
						|
          this._progeressId = setTimeout(triggerProgress, uploader.opts.progressCallbacksInterval - timeDiff) | 
						|
        } | 
						|
        if (this.isComplete()) { | 
						|
          clearTimeout(this._progeressId) | 
						|
          triggerProgress() | 
						|
          this.currentSpeed = 0 | 
						|
          this.averageSpeed = 0 | 
						|
          uploader._trigger('fileSuccess', rootFile, this, message, chunk) | 
						|
          if (rootFile.isComplete()) { | 
						|
            uploader._trigger('fileComplete', rootFile, this) | 
						|
          } | 
						|
        } else if (!this._progeressId) { | 
						|
          triggerProgress() | 
						|
        } | 
						|
        break | 
						|
      case STATUS.RETRY: | 
						|
        uploader._trigger('fileRetry', rootFile, this, chunk) | 
						|
        break | 
						|
    } | 
						|
  }, | 
						|
 | 
						|
  _updateUploadedChunks: function (message, chunk) { | 
						|
    var checkChunkUploaded = this.uploader.opts.checkChunkUploadedByResponse | 
						|
    if (checkChunkUploaded) { | 
						|
      var xhr = chunk.xhr | 
						|
      utils.each(this.chunks, function (_chunk) { | 
						|
        if (!_chunk.tested) { | 
						|
          var uploaded = checkChunkUploaded.call(this, _chunk, message) | 
						|
          if (_chunk === chunk && !uploaded) { | 
						|
            // fix the first chunk xhr status | 
						|
            // treated as success but checkChunkUploaded is false | 
						|
            // so the current chunk should be uploaded again | 
						|
            _chunk.xhr = null | 
						|
          } | 
						|
          if (uploaded) { | 
						|
            // first success and other chunks are uploaded | 
						|
            // then set xhr, so the uploaded chunks | 
						|
            // will be treated as success too | 
						|
            _chunk.xhr = xhr | 
						|
          } | 
						|
          _chunk.tested = true | 
						|
        } | 
						|
      }, this) | 
						|
      if (!this._firstResponse) { | 
						|
        this._firstResponse = true | 
						|
        this.uploader.upload(true) | 
						|
      } else { | 
						|
        this.uploader.uploadNextChunk() | 
						|
      } | 
						|
    } else { | 
						|
      this.uploader.uploadNextChunk() | 
						|
    } | 
						|
  }, | 
						|
 | 
						|
  _error: function () { | 
						|
    this.error = this.allError = true | 
						|
    var parent = this.parent | 
						|
    while (parent && parent !== this.uploader) { | 
						|
      parent._errorFiles.push(this) | 
						|
      parent.error = true | 
						|
      if (parent._errorFiles.length === parent.files.length) { | 
						|
        parent.allError = true | 
						|
      } | 
						|
      parent = parent.parent | 
						|
    } | 
						|
  }, | 
						|
 | 
						|
  _resetError: function () { | 
						|
    this.error = this.allError = false | 
						|
    var parent = this.parent | 
						|
    var index = -1 | 
						|
    while (parent && parent !== this.uploader) { | 
						|
      index = parent._errorFiles.indexOf(this) | 
						|
      parent._errorFiles.splice(index, 1) | 
						|
      parent.allError = false | 
						|
      if (!parent._errorFiles.length) { | 
						|
        parent.error = false | 
						|
      } | 
						|
      parent = parent.parent | 
						|
    } | 
						|
  }, | 
						|
 | 
						|
  isComplete: function () { | 
						|
    if (!this.completed) { | 
						|
      var outstanding = false | 
						|
      this._eachAccess(function (file) { | 
						|
        if (!file.isComplete()) { | 
						|
          outstanding = true | 
						|
          return false | 
						|
        } | 
						|
      }, function () { | 
						|
        if (this.error) { | 
						|
          outstanding = true | 
						|
        } else { | 
						|
          var STATUS = Chunk.STATUS | 
						|
          utils.each(this.chunks, function (chunk) { | 
						|
            var status = chunk.status() | 
						|
            if (status === STATUS.ERROR || status === STATUS.PENDING || status === STATUS.UPLOADING || status === STATUS.READING || chunk.preprocessState === 1 || chunk.readState === 1) { | 
						|
              outstanding = true | 
						|
              return false | 
						|
            } | 
						|
          }) | 
						|
        } | 
						|
      }) | 
						|
      this.completed = !outstanding | 
						|
    } | 
						|
    return this.completed | 
						|
  }, | 
						|
 | 
						|
  isUploading: function () { | 
						|
    var uploading = false | 
						|
    this._eachAccess(function (file) { | 
						|
      if (file.isUploading()) { | 
						|
        uploading = true | 
						|
        return false | 
						|
      } | 
						|
    }, function () { | 
						|
      var uploadingStatus = Chunk.STATUS.UPLOADING | 
						|
      utils.each(this.chunks, function (chunk) { | 
						|
        if (chunk.status() === uploadingStatus) { | 
						|
          uploading = true | 
						|
          return false | 
						|
        } | 
						|
      }) | 
						|
    }) | 
						|
    return uploading | 
						|
  }, | 
						|
 | 
						|
  resume: function () { | 
						|
    this._eachAccess(function (f) { | 
						|
      f.resume() | 
						|
    }, function () { | 
						|
      this.paused = false | 
						|
      this.aborted = false | 
						|
      this.uploader.upload() | 
						|
    }) | 
						|
    this.paused = false | 
						|
    this.aborted = false | 
						|
  }, | 
						|
 | 
						|
  pause: function () { | 
						|
    this._eachAccess(function (f) { | 
						|
      f.pause() | 
						|
    }, function () { | 
						|
      this.paused = true | 
						|
      this.abort() | 
						|
    }) | 
						|
    this.paused = true | 
						|
  }, | 
						|
 | 
						|
  cancel: function () { | 
						|
    this.uploader.removeFile(this) | 
						|
  }, | 
						|
 | 
						|
  retry: function (file) { | 
						|
    var fileRetry = function (file) { | 
						|
      if (file.error) { | 
						|
        file.bootstrap() | 
						|
      } | 
						|
    } | 
						|
    if (file) { | 
						|
      file.bootstrap() | 
						|
    } else { | 
						|
      this._eachAccess(fileRetry, function () { | 
						|
        this.bootstrap() | 
						|
      }) | 
						|
    } | 
						|
    this.uploader.upload() | 
						|
  }, | 
						|
 | 
						|
  abort: function (reset) { | 
						|
    if (this.aborted) { | 
						|
      return | 
						|
    } | 
						|
    this.currentSpeed = 0 | 
						|
    this.averageSpeed = 0 | 
						|
    this.aborted = !reset | 
						|
    var chunks = this.chunks | 
						|
    if (reset) { | 
						|
      this.chunks = [] | 
						|
    } | 
						|
    var uploadingStatus = Chunk.STATUS.UPLOADING | 
						|
    utils.each(chunks, function (c) { | 
						|
      if (c.status() === uploadingStatus) { | 
						|
        c.abort() | 
						|
        this.uploader.uploadNextChunk() | 
						|
      } | 
						|
    }, this) | 
						|
  }, | 
						|
 | 
						|
  progress: function () { | 
						|
    var totalDone = 0 | 
						|
    var totalSize = 0 | 
						|
    var ret = 0 | 
						|
    this._eachAccess(function (file, index) { | 
						|
      totalDone += file.progress() * file.size | 
						|
      totalSize += file.size | 
						|
      if (index === this.files.length - 1) { | 
						|
        ret = totalSize > 0 ? totalDone / totalSize : this.isComplete() ? 1 : 0 | 
						|
      } | 
						|
    }, function () { | 
						|
      if (this.error) { | 
						|
        ret = 1 | 
						|
        return | 
						|
      } | 
						|
      if (this.chunks.length === 1) { | 
						|
        this._prevProgress = Math.max(this._prevProgress, this.chunks[0].progress()) | 
						|
        ret = this._prevProgress | 
						|
        return | 
						|
      } | 
						|
      // Sum up progress across everything | 
						|
      var bytesLoaded = 0 | 
						|
      utils.each(this.chunks, function (c) { | 
						|
        // get chunk progress relative to entire file | 
						|
        bytesLoaded += c.progress() * (c.endByte - c.startByte) | 
						|
      }) | 
						|
      var percent = bytesLoaded / this.size | 
						|
      // We don't want to lose percentages when an upload is paused | 
						|
      this._prevProgress = Math.max(this._prevProgress, percent > 0.9999 ? 1 : percent) | 
						|
      ret = this._prevProgress | 
						|
    }) | 
						|
    return ret | 
						|
  }, | 
						|
 | 
						|
  getSize: function () { | 
						|
    var size = 0 | 
						|
    this._eachAccess(function (file) { | 
						|
      size += file.size | 
						|
    }, function () { | 
						|
      size += this.size | 
						|
    }) | 
						|
    return size | 
						|
  }, | 
						|
 | 
						|
  getFormatSize: function () { | 
						|
    var size = this.getSize() | 
						|
    return utils.formatSize(size) | 
						|
  }, | 
						|
 | 
						|
  getRoot: function () { | 
						|
    if (this.isRoot) { | 
						|
      return this | 
						|
    } | 
						|
    var parent = this.parent | 
						|
    while (parent) { | 
						|
      if (parent.parent === this.uploader) { | 
						|
        // find it | 
						|
        return parent | 
						|
      } | 
						|
      parent = parent.parent | 
						|
    } | 
						|
    return this | 
						|
  }, | 
						|
 | 
						|
  sizeUploaded: function () { | 
						|
    var size = 0 | 
						|
    this._eachAccess(function (file) { | 
						|
      size += file.sizeUploaded() | 
						|
    }, function () { | 
						|
      utils.each(this.chunks, function (chunk) { | 
						|
        size += chunk.sizeUploaded() | 
						|
      }) | 
						|
    }) | 
						|
    return size | 
						|
  }, | 
						|
 | 
						|
  timeRemaining: function () { | 
						|
    var ret = 0 | 
						|
    var sizeDelta = 0 | 
						|
    var averageSpeed = 0 | 
						|
    this._eachAccess(function (file, i) { | 
						|
      if (!file.paused && !file.error) { | 
						|
        sizeDelta += file.size - file.sizeUploaded() | 
						|
        averageSpeed += file.averageSpeed | 
						|
      } | 
						|
      if (i === this.files.length - 1) { | 
						|
        ret = calRet(sizeDelta, averageSpeed) | 
						|
      } | 
						|
    }, function () { | 
						|
      if (this.paused || this.error) { | 
						|
        ret = 0 | 
						|
        return | 
						|
      } | 
						|
      var delta = this.size - this.sizeUploaded() | 
						|
      ret = calRet(delta, this.averageSpeed) | 
						|
    }) | 
						|
    return ret | 
						|
    function calRet (delta, averageSpeed) { | 
						|
      if (delta && !averageSpeed) { | 
						|
        return Number.POSITIVE_INFINITY | 
						|
      } | 
						|
      if (!delta && !averageSpeed) { | 
						|
        return 0 | 
						|
      } | 
						|
      return Math.floor(delta / averageSpeed) | 
						|
    } | 
						|
  }, | 
						|
 | 
						|
  removeFile: function (file) { | 
						|
    if (file.isFolder) { | 
						|
      while (file.files.length) { | 
						|
        var f = file.files[file.files.length - 1] | 
						|
        this._removeFile(f) | 
						|
      } | 
						|
    } | 
						|
    this._removeFile(file) | 
						|
  }, | 
						|
 | 
						|
  _delFilePath: function (file) { | 
						|
    if (file.path && this.filePaths) { | 
						|
      delete this.filePaths[file.path] | 
						|
    } | 
						|
    utils.each(file.fileList, function (file) { | 
						|
      this._delFilePath(file) | 
						|
    }, this) | 
						|
  }, | 
						|
 | 
						|
  _removeFile: function (file) { | 
						|
    if (!file.isFolder) { | 
						|
      utils.each(this.files, function (f, i) { | 
						|
        if (f === file) { | 
						|
          this.files.splice(i, 1) | 
						|
          return false | 
						|
        } | 
						|
      }, this) | 
						|
      file.abort() | 
						|
      var parent = file.parent | 
						|
      var newParent | 
						|
      while (parent && parent !== this) { | 
						|
        newParent = parent.parent | 
						|
        parent._removeFile(file) | 
						|
        parent = newParent | 
						|
      } | 
						|
    } | 
						|
    file.parent === this && utils.each(this.fileList, function (f, i) { | 
						|
      if (f === file) { | 
						|
        this.fileList.splice(i, 1) | 
						|
        return false | 
						|
      } | 
						|
    }, this) | 
						|
    if (!this.isRoot && this.isFolder && !this.files.length) { | 
						|
      this.parent._removeFile(this) | 
						|
      this.uploader._delFilePath(this) | 
						|
    } | 
						|
    file.parent = null | 
						|
  }, | 
						|
 | 
						|
  getType: function () { | 
						|
    if (this.isFolder) { | 
						|
      return 'folder' | 
						|
    } | 
						|
    return this.file.type && this.file.type.split('/')[1] | 
						|
  }, | 
						|
 | 
						|
  getExtension: function () { | 
						|
    if (this.isFolder) { | 
						|
      return '' | 
						|
    } | 
						|
    return this.name.substr((~-this.name.lastIndexOf('.') >>> 0) + 2).toLowerCase() | 
						|
  } | 
						|
 | 
						|
}) | 
						|
 | 
						|
module.exports = File | 
						|
 | 
						|
function parsePaths (path) { | 
						|
  var ret = [] | 
						|
  var paths = path.split('/') | 
						|
  var len = paths.length | 
						|
  var i = 1 | 
						|
  paths.splice(len - 1, 1) | 
						|
  len-- | 
						|
  if (paths.length) { | 
						|
    while (i <= len) { | 
						|
      ret.push(paths.slice(0, i++).join('/') + '/') | 
						|
    } | 
						|
  } | 
						|
  return ret | 
						|
} | 
						|
 | 
						|
},{"./chunk":1,"./utils":5}],5:[function(_dereq_,module,exports){ | 
						|
var oproto = Object.prototype | 
						|
var aproto = Array.prototype | 
						|
var serialize = oproto.toString | 
						|
 | 
						|
var isFunction = function (fn) { | 
						|
  return serialize.call(fn) === '[object Function]' | 
						|
} | 
						|
 | 
						|
var isArray = Array.isArray || /* istanbul ignore next */ function (ary) { | 
						|
  return serialize.call(ary) === '[object Array]' | 
						|
} | 
						|
 | 
						|
var isPlainObject = function (obj) { | 
						|
  return serialize.call(obj) === '[object Object]' && Object.getPrototypeOf(obj) === oproto | 
						|
} | 
						|
 | 
						|
var i = 0 | 
						|
var utils = { | 
						|
  uid: function () { | 
						|
    return ++i | 
						|
  }, | 
						|
  noop: function () {}, | 
						|
  bind: function (fn, context) { | 
						|
    return function () { | 
						|
      return fn.apply(context, arguments) | 
						|
    } | 
						|
  }, | 
						|
  preventEvent: function (evt) { | 
						|
    evt.preventDefault() | 
						|
  }, | 
						|
  stop: function (evt) { | 
						|
    evt.preventDefault() | 
						|
    evt.stopPropagation() | 
						|
  }, | 
						|
  nextTick: function (fn, context) { | 
						|
    setTimeout(utils.bind(fn, context), 0) | 
						|
  }, | 
						|
  toArray: function (ary, start, end) { | 
						|
    if (start === undefined) start = 0 | 
						|
    if (end === undefined) end = ary.length | 
						|
    return aproto.slice.call(ary, start, end) | 
						|
  }, | 
						|
 | 
						|
  isPlainObject: isPlainObject, | 
						|
  isFunction: isFunction, | 
						|
  isArray: isArray, | 
						|
  isObject: function (obj) { | 
						|
    return Object(obj) === obj | 
						|
  }, | 
						|
  isString: function (s) { | 
						|
    return typeof s === 'string' | 
						|
  }, | 
						|
  isUndefined: function (a) { | 
						|
    return typeof a === 'undefined' | 
						|
  }, | 
						|
  isDefined: function (a) { | 
						|
    return typeof a !== 'undefined' | 
						|
  }, | 
						|
 | 
						|
  each: function (ary, func, context) { | 
						|
    if (utils.isDefined(ary.length)) { | 
						|
      for (var i = 0, len = ary.length; i < len; i++) { | 
						|
        if (func.call(context, ary[i], i, ary) === false) { | 
						|
          break | 
						|
        } | 
						|
      } | 
						|
    } else { | 
						|
      for (var k in ary) { | 
						|
        if (func.call(context, ary[k], k, ary) === false) { | 
						|
          break | 
						|
        } | 
						|
      } | 
						|
    } | 
						|
  }, | 
						|
 | 
						|
  /** | 
						|
   * If option is a function, evaluate it with given params | 
						|
   * @param {*} data | 
						|
   * @param {...} args arguments of a callback | 
						|
   * @returns {*} | 
						|
   */ | 
						|
  evalOpts: function (data, args) { | 
						|
    if (utils.isFunction(data)) { | 
						|
      // `arguments` is an object, not array, in FF, so: | 
						|
      args = utils.toArray(arguments) | 
						|
      data = data.apply(null, args.slice(1)) | 
						|
    } | 
						|
    return data | 
						|
  }, | 
						|
 | 
						|
  extend: function () { | 
						|
    var options | 
						|
    var name | 
						|
    var src | 
						|
    var copy | 
						|
    var copyIsArray | 
						|
    var clone | 
						|
    var target = arguments[0] || {} | 
						|
    var i = 1 | 
						|
    var length = arguments.length | 
						|
    var force = false | 
						|
 | 
						|
    // 如果第一个参数为布尔,判定是否深拷贝 | 
						|
    if (typeof target === 'boolean') { | 
						|
      force = target | 
						|
      target = arguments[1] || {} | 
						|
      i++ | 
						|
    } | 
						|
 | 
						|
    // 确保接受方为一个复杂的数据类型 | 
						|
    if (typeof target !== 'object' && !isFunction(target)) { | 
						|
      target = {} | 
						|
    } | 
						|
 | 
						|
    // 如果只有一个参数,那么新成员添加于 extend 所在的对象上 | 
						|
    if (i === length) { | 
						|
      target = this | 
						|
      i-- | 
						|
    } | 
						|
 | 
						|
    for (; i < length; i++) { | 
						|
      // 只处理非空参数 | 
						|
      if ((options = arguments[i]) != null) { | 
						|
        for (name in options) { | 
						|
          src = target[name] | 
						|
          copy = options[name] | 
						|
 | 
						|
          // 防止环引用 | 
						|
          if (target === copy) { | 
						|
            continue | 
						|
          } | 
						|
          if (force && copy && (isPlainObject(copy) || (copyIsArray = isArray(copy)))) { | 
						|
            if (copyIsArray) { | 
						|
              copyIsArray = false | 
						|
              clone = src && isArray(src) ? src : [] | 
						|
            } else { | 
						|
              clone = src && isPlainObject(src) ? src : {} | 
						|
            } | 
						|
            target[name] = utils.extend(force, clone, copy) | 
						|
          } else if (copy !== undefined) { | 
						|
            target[name] = copy | 
						|
          } | 
						|
        } | 
						|
      } | 
						|
    } | 
						|
    return target | 
						|
  }, | 
						|
 | 
						|
  formatSize: function (size) { | 
						|
    if (size < 1024) { | 
						|
      return size.toFixed(0) + ' bytes' | 
						|
    } else if (size < 1024 * 1024) { | 
						|
      return (size / 1024.0).toFixed(0) + ' KB' | 
						|
    } else if (size < 1024 * 1024 * 1024) { | 
						|
      return (size / 1024.0 / 1024.0).toFixed(1) + ' MB' | 
						|
    } else { | 
						|
      return (size / 1024.0 / 1024.0 / 1024.0).toFixed(1) + ' GB' | 
						|
    } | 
						|
  }, | 
						|
 | 
						|
  defineNonEnumerable: function (target, key, value) { | 
						|
    Object.defineProperty(target, key, { | 
						|
      enumerable: false, | 
						|
      configurable: true, | 
						|
      writable: true, | 
						|
      value: value | 
						|
    }) | 
						|
  } | 
						|
} | 
						|
 | 
						|
module.exports = utils | 
						|
 | 
						|
},{}]},{},[3]) | 
						|
(3) | 
						|
}); |