text: function Downloader() {
  	this.isActive = false;
  	this.realtime = false;
  	this.chunkStart = 0;
  	this.chunkSize = 0;
  	this.totalLength = 0;
  	this.chunkTimeout = 1000;
  	this.url = null;
  	this.callback = null;
  	this.eof = false;
  	this.setDownloadTimeoutCallback = null;
  }
  
  Downloader.prototype.setDownloadTimeoutCallback = function(callback) {
  	this.setDownloadTimeoutCallback = callback;
  	return this;
  }
  
  Downloader.prototype.reset = function() {
  	this.chunkStart = 0;
  	this.totalLength = 0;
  	this.eof = false;
  	return this;
  }
  
  Downloader.prototype.setRealTime = function(_realtime) {
  	this.realtime = _realtime;
  	return this;
  }
  
  Downloader.prototype.setChunkSize = function(_size) {
  	this.chunkSize = _size;
  	return this;
  }
  
  Downloader.prototype.setChunkStart = function(_start) {
  	this.chunkStart = _start;
  	this.eof = false;
  	return this;
  }
  
  Downloader.prototype.setInterval = function(_timeout) {
  	this.chunkTimeout = _timeout;
  	return this;
  }
  
  Downloader.prototype.setUrl = function(_url) {
  	this.url = _url;
  	return this;
  }
  
  Downloader.prototype.setCallback = function(_callback) {
  	this.callback = _callback;
  	return this;
  }
  
  Downloader.prototype.isStopped = function () {
  	return !this.isActive;
  }
  
  Downloader.prototype.getFileLength = function () {
  	return this.totalLength;
  }
  
  Downloader.prototype.getFile = function() {
  	var dl = this;
  	if (dl.totalLength && this.chunkStart>= dl.totalLength) {
  		dl.eof = true;
  	}
  	if (dl.eof === true) {
  		Log.info("Downloader", "File download done.");
  		this.callback(null, true);
  		return;
  	}
  	var xhr = new XMLHttpRequest();
  	xhr.open("GET", this.url, true);
  	xhr.responseType = "arraybuffer";
  	var range = null;
  	xhr.start = this.chunkStart;
  	var maxRange;
  	if (this.chunkStart+this.chunkSize < Infinity) {
  		range = 'bytes=' + this.chunkStart + '-';
  		maxRange = this.chunkStart+this.chunkSize-1;
  		/* if the file length is known we limit the max range to that length */
  		/*if (dl.totalLength !== 0) {
  			maxRange = Math.min(maxRange, dl.totalLength);
  		}*/
  		range += maxRange;
  		xhr.setRequestHeader('Range', range);
  	}
  	xhr.onerror = function(e) {
  		dl.callback(null, false, true);
  	}
  	xhr.onreadystatechange = function (e) { 
  		if (xhr.status == 404) {
  			dl.callback(null, false, true);
  		}	
  		if ((xhr.status == 200 || xhr.status == 206 || xhr.status == 304 || xhr.status == 416) && xhr.readyState == this.DONE) {
  			var rangeReceived = xhr.getResponseHeader("Content-Range");
  			Log.info("Downloader", "Received data range: "+rangeReceived);
  			/* if the length of the file is not known, we get it from the response header */
  			if (!dl.totalLength && rangeReceived) {
  				var sizeIndex;
  				sizeIndex = rangeReceived.indexOf("/");
  				if (sizeIndex > -1) {
  					dl.totalLength = +rangeReceived.slice(sizeIndex+1);
  				}
  			}
  			dl.eof = (xhr.response.byteLength !== dl.chunkSize) || (xhr.response.byteLength === dl.totalLength);
  			var buffer = xhr.response;
  			buffer.fileStart = xhr.start;
  			if (!buffer.fileStart) {
  				// IE does not support adding properties to an ArrayBuffer generated by XHR
  				buffer = buffer.slice(0);
  				buffer.fileStart = xhr.start;
  			}
  			dl.callback(buffer, dl.eof); 
  			if (dl.isActive === true && dl.eof === false) {
  				var timeoutDuration = 0;
  				if (!dl.realtime) {
  					timeoutDuration = dl.chunkTimeout;
  				} else {
  					timeoutDuration = computeWaitingTimeFromBuffer(video);
  				}
  				if (dl.setDownloadTimeoutCallback) dl.setDownloadTimeoutCallback(timeoutDuration);
  				Log.info("Downloader", "Next download scheduled in "+Math.floor(timeoutDuration)+ ' ms.');
  				dl.timeoutID = window.setTimeout(dl.getFile.bind(dl), timeoutDuration);
  			} else {
  				/* end of file */
  				dl.isActive = false;
  			}
  		}
  	};
  	if (dl.isActive) {
  		Log.info("Downloader", "Fetching "+this.url+(range ? (" with range: "+range): ""));
  		xhr.send();
  	}
  }
  
  Downloader.prototype.start = function() {
  	Log.info("Downloader", "Starting file download");
  	this.chunkStart = 0;
  	this.resume();
  	return this;
  }
  
  Downloader.prototype.resume = function() {
  	Log.info("Downloader", "Resuming file download");
  	this.isActive = true;
  	if (this.chunkSize === 0) {
  		this.chunkSize = Infinity;
  	}
  	this.getFile();
  	return this;
  }
  
  Downloader.prototype.stop = function() {
  	Log.info("Downloader", "Stopping file download");
  	this.isActive = false;
  	if (this.timeoutID) {
  		window.clearTimeout(this.timeoutID);
  		delete this.timeoutID;
  	}
  	return this;
  }
  