text: ;(function() {
  
  "use strict";
  
  var isNodeEnviroment = typeof module !== 'undefined' && typeof module.exports !== 'undefined';
  
  var Tar = isNodeEnviroment ? require('./tar') : window.Tar;
  var download = isNodeEnviroment ? require('./download') : window.download;
  var GIF = isNodeEnviroment ? require('./gif').GIF : window.GIF;
  var WebMWriter = isNodeEnviroment ? require('./webm-writer-0.2.0') : window.WebMWriter;
  
  var objectTypes = {
  'function': true,
  'object': true
  };
  
  function checkGlobal(value) {
      return (value && value.Object === Object) ? value : null;
    }
  
  /** Built-in method references without a dependency on `root`. */
  var freeParseFloat = parseFloat,
    freeParseInt = parseInt;
  
  /** Detect free variable `exports`. */
  var freeExports = (objectTypes[typeof exports] && exports && !exports.nodeType)
  ? exports
  : undefined;
  
  /** Detect free variable `module`. */
  var freeModule = (objectTypes[typeof module] && module && !module.nodeType)
  ? module
  : undefined;
  
  /** Detect the popular CommonJS extension `module.exports`. */
  var moduleExports = (freeModule && freeModule.exports === freeExports)
  ? freeExports
  : undefined;
  
  /** Detect free variable `global` from Node.js. */
  var freeGlobal = checkGlobal(freeExports && freeModule && typeof global == 'object' && global);
  
  /** Detect free variable `self`. */
  var freeSelf = checkGlobal(objectTypes[typeof self] && self);
  
  /** Detect free variable `window`. */
  var freeWindow = checkGlobal(objectTypes[typeof window] && window);
  
  /** Detect `this` as the global object. */
  var thisGlobal = checkGlobal(objectTypes[typeof this] && this);
  
  /**
  * Used as a reference to the global object.
  *
  * The `this` value is used if it's the global object to avoid Greasemonkey's
  * restricted `window` object, otherwise the `window` object is used.
  */
  var root = freeGlobal ||
  ((freeWindow !== (thisGlobal && thisGlobal.window)) && freeWindow) ||
    freeSelf || thisGlobal || Function('return this')();
  
  if( !('gc' in window ) ) {
  	window.gc = function(){}
  }
  
  if (!HTMLCanvasElement.prototype.toBlob) {
   Object.defineProperty(HTMLCanvasElement.prototype, 'toBlob', {
    value: function (callback, type, quality) {
  
      var binStr = atob( this.toDataURL(type, quality).split(',')[1] ),
          len = binStr.length,
          arr = new Uint8Array(len);
  
      for (var i=0; i<len; i++ ) {
       arr[i] = binStr.charCodeAt(i);
      }
  
      callback( new Blob( [arr], {type: type || 'image/png'} ) );
    }
   });
  }
  
  // @license http://opensource.org/licenses/MIT
  // copyright Paul Irish 2015
  
  
  // Date.now() is supported everywhere except IE8. For IE8 we use the Date.now polyfill
  //   github.com/Financial-Times/polyfill-service/blob/master/polyfills/Date.now/polyfill.js
  // as Safari 6 doesn't have support for NavigationTiming, we use a Date.now() timestamp for relative values
  
  // if you want values similar to what you'd get with real perf.now, place this towards the head of the page
  // but in reality, you're just getting the delta between now() calls, so it's not terribly important where it's placed
  
  
  (function(){
  
    if ("performance" in window == false) {
        window.performance = {};
    }
  
    Date.now = (Date.now || function () {  // thanks IE8
  	  return new Date().getTime();
    });
  
    if ("now" in window.performance == false){
  
      var nowOffset = Date.now();
  
      if (performance.timing && performance.timing.navigationStart){
        nowOffset = performance.timing.navigationStart
      }
  
      window.performance.now = function now(){
        return Date.now() - nowOffset;
      }
    }
  
  })();
  
  
  function pad( n ) {
  	return String("0000000" + n).slice(-7);
  }
  // https://developer.mozilla.org/en-US/Add-ons/Code_snippets/Timers
  
  var g_startTime = window.Date.now();
  
  function guid() {
  	function s4() {
  		return Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);
  	}
  	return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
  }
  
  function CCFrameEncoder( settings ) {
  
  	var _handlers = {};
  
  	this.settings = settings;
  
  	this.on = function(event, handler) {
  
  		_handlers[event] = handler;
  
  	};
  
  	this.emit = function(event) {
  
  		var handler = _handlers[event];
  		if (handler) {
  
  			handler.apply(null, Array.prototype.slice.call(arguments, 1));
  
  		}
  
  	};
  
  	this.filename = settings.name || guid();
  	this.extension = '';
  	this.mimeType = '';
  
  }
  
  CCFrameEncoder.prototype.start = function(){};
  CCFrameEncoder.prototype.stop = function(){};
  CCFrameEncoder.prototype.add = function(){};
  CCFrameEncoder.prototype.save = function(){};
  CCFrameEncoder.prototype.dispose = function(){};
  CCFrameEncoder.prototype.safeToProceed = function(){ return true; };
  CCFrameEncoder.prototype.step = function() { console.log( 'Step not set!' ) }
  
  function CCTarEncoder( settings ) {
  
    CCFrameEncoder.call( this, settings );
  
    this.extension = '.tar'
    this.mimeType = 'application/x-tar'
    this.fileExtension = '';
    this.baseFilename = this.filename;
  
    this.tape = null
    this.count = 0;
    this.part = 1;
    this.frames = 0;
  
  }
  
  CCTarEncoder.prototype = Object.create( CCFrameEncoder.prototype );
  
  CCTarEncoder.prototype.start = function(){
  
    this.dispose();
  
  };
  
  CCTarEncoder.prototype.add = function( blob ) {
  
    var fileReader = new FileReader();
    fileReader.onload = function() {
      this.tape.append( pad( this.count ) + this.fileExtension, new Uint8Array( fileReader.result ) );
  
      if( this.settings.autoSaveTime > 0 && ( this.frames / this.settings.framerate ) >= this.settings.autoSaveTime ) {
        this.save( function( blob ) {
          this.filename = this.baseFilename + '-part-' + pad( this.part );
          download( blob, this.filename + this.extension, this.mimeType );
          var count = this.count;
          this.dispose();
          this.count = count+1;
          this.part++;
          this.filename = this.baseFilename + '-part-' + pad( this.part );
          this.frames = 0;
          this.step();
        }.bind( this ) )
      } else {
        this.count++;
        this.frames++;
        this.step();
      }
  
    }.bind( this );
    fileReader.readAsArrayBuffer(blob);
  
  }
  
  CCTarEncoder.prototype.save = function( callback ) {
  
    callback( this.tape.save() );
  
  }
  
  CCTarEncoder.prototype.dispose = function() {
  
    this.tape = new Tar();
    this.count = 0;
  
  }
  
  function CCPNGEncoder( settings ) {
  
  	CCTarEncoder.call( this, settings );
  
  	this.type = 'image/png';
  	this.fileExtension = '.png';
  
  }
  
  CCPNGEncoder.prototype = Object.create( CCTarEncoder.prototype );
  
  CCPNGEncoder.prototype.add = function( canvas ) {
  
  	canvas.toBlob( function( blob ) {
  		CCTarEncoder.prototype.add.call( this, blob );
  	}.bind( this ), this.type )
  
  }
  
  function CCJPEGEncoder( settings ) {
  
  	CCTarEncoder.call( this, settings );
  
  	this.type = 'image/jpeg';
  	this.fileExtension = '.jpg';
  	this.quality = ( settings.quality / 100 ) || .8;
  
  }
  
  CCJPEGEncoder.prototype = Object.create( CCTarEncoder.prototype );
  
  CCJPEGEncoder.prototype.add = function( canvas ) {
  
  	canvas.toBlob( function( blob ) {
  		CCTarEncoder.prototype.add.call( this, blob );
  	}.bind( this ), this.type, this.quality )
  
  }
  
  /*
  
  	WebM Encoder
  
  */
  
  function CCWebMEncoder( settings ) {
  
  	var canvas = document.createElement( 'canvas' );
  	if( canvas.toDataURL( 'image/webp' ).substr(5,10) !== 'image/webp' ){
  		console.log( "WebP not supported - try another export format" )
  	}
  
  	CCFrameEncoder.call( this, settings );
  
  	this.quality = ( settings.quality / 100 ) || .8;
  
  	this.extension = '.webm'
  	this.mimeType = 'video/webm'
  	this.baseFilename = this.filename;
    this.framerate = settings.framerate;
  
  	this.frames = 0;
  	this.part = 1;
  
    this.videoWriter = new WebMWriter({
      quality: this.quality,
      fileWriter: null,
      fd: null,
      frameRate: this.framerate
    });
  
  }
  
  CCWebMEncoder.prototype = Object.create( CCFrameEncoder.prototype );
  
  CCWebMEncoder.prototype.start = function( canvas ) {
  
  	this.dispose();
  
  }
  
  CCWebMEncoder.prototype.add = function( canvas ) {
  
    this.videoWriter.addFrame(canvas);
  
  	if( this.settings.autoSaveTime > 0 && ( this.frames / this.settings.framerate ) >= this.settings.autoSaveTime ) {
  		this.save( function( blob ) {
  			this.filename = this.baseFilename + '-part-' + pad( this.part );
  			download( blob, this.filename + this.extension, this.mimeType );
  			this.dispose();
  			this.part++;
  			this.filename = this.baseFilename + '-part-' + pad( this.part );
  			this.step();
  		}.bind( this ) )
  	} else {
      this.frames++;
  		this.step();
  	}
  
  }
  
  CCWebMEncoder.prototype.save = function( callback ) {
  
    this.videoWriter.complete().then(callback);
  
  }
  
  CCWebMEncoder.prototype.dispose = function( canvas ) {
  
  	this.frames = 0;
    this.videoWriter = new WebMWriter({
      quality: this.quality,
      fileWriter: null,
      fd: null,
      frameRate: this.framerate
    });
  
  }
  
  function CCFFMpegServerEncoder( settings ) {
  
  	CCFrameEncoder.call( this, settings );
  
  	settings.quality = ( settings.quality / 100 ) || .8;
  
  	this.encoder = new FFMpegServer.Video( settings );
      this.encoder.on( 'process', function() {
          this.emit( 'process' )
      }.bind( this ) );
      this.encoder.on('finished', function( url, size ) {
          var cb = this.callback;
          if ( cb ) {
              this.callback = undefined;
              cb( url, size );
          }
      }.bind( this ) );
      this.encoder.on( 'progress', function( progress ) {
          if ( this.settings.onProgress ) {
              this.settings.onProgress( progress )
          }
      }.bind( this ) );
      this.encoder.on( 'error', function( data ) {
          alert(JSON.stringify(data, null, 2));
      }.bind( this ) );
  
  }
  
  CCFFMpegServerEncoder.prototype = Object.create( CCFrameEncoder.prototype );
  
  CCFFMpegServerEncoder.prototype.start = function() {
  
  	this.encoder.start( this.settings );
  
  };
  
  CCFFMpegServerEncoder.prototype.add = function( canvas ) {
  
  	this.encoder.add( canvas );
  
  }
  
  CCFFMpegServerEncoder.prototype.save = function( callback ) {
  
      this.callback = callback;
      this.encoder.end();
  
  }
  
  CCFFMpegServerEncoder.prototype.safeToProceed = function() {
      return this.encoder.safeToProceed();
  };
  
  /*
  	HTMLCanvasElement.captureStream()
  */
  
  function CCStreamEncoder( settings ) {
  
  	CCFrameEncoder.call( this, settings );
  
  	this.framerate = this.settings.framerate;
  	this.type = 'video/webm';
  	this.extension = '.webm';
  	this.stream = null;
  	this.mediaRecorder = null;
  	this.chunks = [];
  
  }
  
  CCStreamEncoder.prototype = Object.create( CCFrameEncoder.prototype );
  
  CCStreamEncoder.prototype.add = function( canvas ) {
  
  	if( !this.stream ) {
  		this.stream = canvas.captureStream( this.framerate );
  		this.mediaRecorder = new MediaRecorder( this.stream );
  		this.mediaRecorder.start();
  
  		this.mediaRecorder.ondataavailable = function(e) {
  			this.chunks.push(e.data);
  		}.bind( this );
  
  	}
  	this.step();
  
  }
  
  CCStreamEncoder.prototype.save = function( callback ) {
  
  	this.mediaRecorder.onstop = function( e ) {
  		var blob = new Blob( this.chunks, { 'type' : 'video/webm' });
  		this.chunks = [];
  		callback( blob );
  
  	}.bind( this );
  
  	this.mediaRecorder.stop();
  
  }
  
  /*function CCGIFEncoder( settings ) {
  
  	CCFrameEncoder.call( this );
  
  	settings.quality = settings.quality || 6;
  	this.settings = settings;
  
  	this.encoder = new GIFEncoder();
  	this.encoder.setRepeat( 1 );
    	this.encoder.setDelay( settings.step );
    	this.encoder.setQuality( 6 );
    	this.encoder.setTransparent( null );
    	this.encoder.setSize( 150, 150 );
  
    	this.canvas = document.createElement( 'canvas' );
    	this.ctx = this.canvas.getContext( '2d' );
  
  }
  
  CCGIFEncoder.prototype = Object.create( CCFrameEncoder );
  
  CCGIFEncoder.prototype.start = function() {
  
  	this.encoder.start();
  
  }
  
  CCGIFEncoder.prototype.add = function( canvas ) {
  
  	this.canvas.width = canvas.width;
  	this.canvas.height = canvas.height;
  	this.ctx.drawImage( canvas, 0, 0 );
  	this.encoder.addFrame( this.ctx );
  
  	this.encoder.setSize( canvas.width, canvas.height );
  	var readBuffer = new Uint8Array(canvas.width * canvas.height * 4);
  	var context = canvas.getContext( 'webgl' );
  	context.readPixels(0, 0, canvas.width, canvas.height, context.RGBA, context.UNSIGNED_BYTE, readBuffer);
  	this.encoder.addFrame( readBuffer, true );
  
  }
  
  CCGIFEncoder.prototype.stop = function() {
  
  	this.encoder.finish();
  
  }
  
  CCGIFEncoder.prototype.save = function( callback ) {
  
  	var binary_gif = this.encoder.stream().getData();
  
  	var data_url = 'data:image/gif;base64,'+encode64(binary_gif);
  	window.location = data_url;
  	return;
  
  	var blob = new Blob( [ binary_gif ], { type: "octet/stream" } );
  	var url = window.URL.createObjectURL( blob );
  	callback( url );
  
  }*/
  
  function CCGIFEncoder( settings ) {
  
  	CCFrameEncoder.call( this, settings );
  
  	settings.quality = 31 - ( ( settings.quality * 30 / 100 ) || 10 );
  	settings.workers = settings.workers || 4;
  
  	this.extension = '.gif'
  	this.mimeType = 'image/gif'
  
    	this.canvas = document.createElement( 'canvas' );
    	this.ctx = this.canvas.getContext( '2d' );
    	this.sizeSet = false;
  
    	this.encoder = new GIF({
  		workers: settings.workers,
  		quality: settings.quality,
  		workerScript: settings.workersPath + 'gif.worker.js'
  	} );
  
      this.encoder.on( 'progress', function( progress ) {
          if ( this.settings.onProgress ) {
              this.settings.onProgress( progress )
          }
      }.bind( this ) );
  
      this.encoder.on('finished', function( blob ) {
          var cb = this.callback;
          if ( cb ) {
              this.callback = undefined;
              cb( blob );
          }
      }.bind( this ) );
  
  }
  
  CCGIFEncoder.prototype = Object.create( CCFrameEncoder.prototype );
  
  CCGIFEncoder.prototype.add = function( canvas ) {
  
  	if( !this.sizeSet ) {
  		this.encoder.setOption( 'width',canvas.width );
  		this.encoder.setOption( 'height',canvas.height );
  		this.sizeSet = true;
  	}
  
  	this.canvas.width = canvas.width;
  	this.canvas.height = canvas.height;
  	this.ctx.drawImage( canvas, 0, 0 );
  
  	this.encoder.addFrame( this.ctx, { copy: true, delay: this.settings.step } );
  	this.step();
  
  	/*this.encoder.setSize( canvas.width, canvas.height );
  	var readBuffer = new Uint8Array(canvas.width * canvas.height * 4);
  	var context = canvas.getContext( 'webgl' );
  	context.readPixels(0, 0, canvas.width, canvas.height, context.RGBA, context.UNSIGNED_BYTE, readBuffer);
  	this.encoder.addFrame( readBuffer, true );*/
  
  }
  
  CCGIFEncoder.prototype.save = function( callback ) {
  
      this.callback = callback;
  
  	this.encoder.render();
  
  }
  
  function CCapture( settings ) {
  
  	var _settings = settings || {},
  		_date = new Date(),
  		_verbose,
  		_display,
  		_time,
  		_startTime,
  		_performanceTime,
  		_performanceStartTime,
  		_step,
          _encoder,
  		_timeouts = [],
  		_intervals = [],
  		_frameCount = 0,
  		_intermediateFrameCount = 0,
  		_lastFrame = null,
  		_requestAnimationFrameCallbacks = [],
  		_capturing = false,
          _handlers = {};
  
  	_settings.framerate = _settings.framerate || 60;
  	_settings.motionBlurFrames = 2 * ( _settings.motionBlurFrames || 1 );
  	_verbose = _settings.verbose || false;
  	_display = _settings.display || false;
  	_settings.step = 1000.0 / _settings.framerate ;
  	_settings.timeLimit = _settings.timeLimit || 0;
  	_settings.frameLimit = _settings.frameLimit || 0;
  	_settings.startTime = _settings.startTime || 0;
  
  	var _timeDisplay = document.createElement( 'div' );
  	_timeDisplay.style.position = 'absolute';
  	_timeDisplay.style.left = _timeDisplay.style.top = 0
  	_timeDisplay.style.backgroundColor = 'black';
  	_timeDisplay.style.fontFamily = 'monospace'
  	_timeDisplay.style.fontSize = '11px'
  	_timeDisplay.style.padding = '5px'
  	_timeDisplay.style.color = 'red';
  	_timeDisplay.style.zIndex = 100000
  	if( _settings.display ) document.body.appendChild( _timeDisplay );
  
  	var canvasMotionBlur = document.createElement( 'canvas' );
  	var ctxMotionBlur = canvasMotionBlur.getContext( '2d' );
  	var bufferMotionBlur;
  	var imageData;
  
  	_log( 'Step is set to ' + _settings.step + 'ms' );
  
      var _encoders = {
  		gif: CCGIFEncoder,
  		webm: CCWebMEncoder,
  		ffmpegserver: CCFFMpegServerEncoder,
  		png: CCPNGEncoder,
  		jpg: CCJPEGEncoder,
  		'webm-mediarecorder': CCStreamEncoder
      };
  
      var ctor = _encoders[ _settings.format ];
      if ( !ctor ) {
  		throw "Error: Incorrect or missing format: Valid formats are " + Object.keys(_encoders).join(", ");
      }
      _encoder = new ctor( _settings );
      _encoder.step = _step
  
  	_encoder.on('process', _process);
      _encoder.on('progress', _progress);
  
      if ("performance" in window == false) {
      	window.performance = {};
      }
  
  	Date.now = (Date.now || function () {  // thanks IE8
  		return new Date().getTime();
  	});
  
  	if ("now" in window.performance == false){
  
  		var nowOffset = Date.now();
  
  		if (performance.timing && performance.timing.navigationStart){
  			nowOffset = performance.timing.navigationStart
  		}
  
  		window.performance.now = function now(){
  			return Date.now() - nowOffset;
  		}
  	}
  
  	var _oldSetTimeout = window.setTimeout,
  		_oldSetInterval = window.setInterval,
  	    	_oldClearInterval = window.clearInterval,
  		_oldClearTimeout = window.clearTimeout,
  		_oldRequestAnimationFrame = window.requestAnimationFrame,
  		_oldNow = window.Date.now,
  		_oldPerformanceNow = window.performance.now,
  		_oldGetTime = window.Date.prototype.getTime;
  	// Date.prototype._oldGetTime = Date.prototype.getTime;
  
  	var media = [];
  
  	function _init() {
  
  		_log( 'Capturer start' );
  
  		_startTime = window.Date.now();
  		_time = _startTime + _settings.startTime;
  		_performanceStartTime = window.performance.now();
  		_performanceTime = _performanceStartTime + _settings.startTime;
  
  		window.Date.prototype.getTime = function(){
  			return _time;
  		};
  		window.Date.now = function() {
  			return _time;
  		};
  
  		window.setTimeout = function( callback, time ) {
  			var t = {
  				callback: callback,
  				time: time,
  				triggerTime: _time + time
  			};
  			_timeouts.push( t );
  			_log( 'Timeout set to ' + t.time );
              return t;
  		};
  		window.clearTimeout = function( id ) {
  			for( var j = 0; j < _timeouts.length; j++ ) {
  				if( _timeouts[ j ] == id ) {
  					_timeouts.splice( j, 1 );
  					_log( 'Timeout cleared' );
  					continue;
  				}
  			}
  		};
  		window.setInterval = function( callback, time ) {
  			var t = {
  				callback: callback,
  				time: time,
  				triggerTime: _time + time
  			};
  			_intervals.push( t );
  			_log( 'Interval set to ' + t.time );
  			return t;
  		};
  		window.clearInterval = function( id ) {
  			_log( 'clear Interval' );
  			return null;
  		};
  		window.requestAnimationFrame = function( callback ) {
  			_requestAnimationFrameCallbacks.push( callback );
  		};
  		window.performance.now = function(){
  			return _performanceTime;
  		};
  
  		function hookCurrentTime() { 
  			if( !this._hooked ) {
  				this._hooked = true;
  				this._hookedTime = this.currentTime || 0;
  				this.pause();
  				media.push( this );
  			}
  			return this._hookedTime + _settings.startTime;
  		};
  
  		try {
  			Object.defineProperty( HTMLVideoElement.prototype, 'currentTime', { get: hookCurrentTime } )
  			Object.defineProperty( HTMLAudioElement.prototype, 'currentTime', { get: hookCurrentTime } )
  		} catch (err) {
  			_log(err);
  		}
  
  	}
  
  	function _start() {
  		_init();
  		_encoder.start();
  		_capturing = true;
  	}
  
  	function _stop() {
  		_capturing = false;
  		_encoder.stop();
  		_destroy();
  	}
  
  	function _call( fn, p ) {
  		_oldSetTimeout( fn, 0, p );
  	}
  
  	function _step() {
  		//_oldRequestAnimationFrame( _process );
  		_call( _process );
  	}
  
  	function _destroy() {
  		_log( 'Capturer stop' );
  		window.setTimeout = _oldSetTimeout;
  		window.setInterval = _oldSetInterval;
  		window.clearInterval = _oldClearInterval;
  		window.clearTimeout = _oldClearTimeout;
  		window.requestAnimationFrame = _oldRequestAnimationFrame;
  		window.Date.prototype.getTime = _oldGetTime;
  		window.Date.now = _oldNow;
  		window.performance.now = _oldPerformanceNow;
  	}
  
  	function _updateTime() {
  		var seconds = _frameCount / _settings.framerate;
  		if( ( _settings.frameLimit && _frameCount >= _settings.frameLimit ) || ( _settings.timeLimit && seconds >= _settings.timeLimit ) ) {
  			_stop();
  			_save();
  		}
  		var d = new Date( null );
  		d.setSeconds( seconds );
  		if( _settings.motionBlurFrames > 2 ) {
  			_timeDisplay.textContent = 'CCapture ' + _settings.format + ' | ' + _frameCount + ' frames (' + _intermediateFrameCount + ' inter) | ' +  d.toISOString().substr( 11, 8 );
  		} else {
  			_timeDisplay.textContent = 'CCapture ' + _settings.format + ' | ' + _frameCount + ' frames | ' +  d.toISOString().substr( 11, 8 );
  		}
  	}
  
  	function _checkFrame( canvas ) {
  
  		if( canvasMotionBlur.width !== canvas.width || canvasMotionBlur.height !== canvas.height ) {
  			canvasMotionBlur.width = canvas.width;
  			canvasMotionBlur.height = canvas.height;
  			bufferMotionBlur = new Uint16Array( canvasMotionBlur.height * canvasMotionBlur.width * 4 );
  			ctxMotionBlur.fillStyle = '#0'
  			ctxMotionBlur.fillRect( 0, 0, canvasMotionBlur.width, canvasMotionBlur.height );
  		}
  
  	}
  
  	function _blendFrame( canvas ) {
  
  		//_log( 'Intermediate Frame: ' + _intermediateFrameCount );
  
  		ctxMotionBlur.drawImage( canvas, 0, 0 );
  		imageData = ctxMotionBlur.getImageData( 0, 0, canvasMotionBlur.width, canvasMotionBlur.height );
  		for( var j = 0; j < bufferMotionBlur.length; j+= 4 ) {
  			bufferMotionBlur[ j ] += imageData.data[ j ];
  			bufferMotionBlur[ j + 1 ] += imageData.data[ j + 1 ];
  			bufferMotionBlur[ j + 2 ] += imageData.data[ j + 2 ];
  		}
  		_intermediateFrameCount++;
  
  	}
  
  	function _saveFrame(){
  
  		var data = imageData.data;
  		for( var j = 0; j < bufferMotionBlur.length; j+= 4 ) {
  			data[ j ] = bufferMotionBlur[ j ] * 2 / _settings.motionBlurFrames;
  			data[ j + 1 ] = bufferMotionBlur[ j + 1 ] * 2 / _settings.motionBlurFrames;
  			data[ j + 2 ] = bufferMotionBlur[ j + 2 ] * 2 / _settings.motionBlurFrames;
  		}
  		ctxMotionBlur.putImageData( imageData, 0, 0 );
  		_encoder.add( canvasMotionBlur );
  		_frameCount++;
  		_intermediateFrameCount = 0;
  		_log( 'Full MB Frame! ' + _frameCount + ' ' +  _time );
  		for( var j = 0; j < bufferMotionBlur.length; j+= 4 ) {
  			bufferMotionBlur[ j ] = 0;
  			bufferMotionBlur[ j + 1 ] = 0;
  			bufferMotionBlur[ j + 2 ] = 0;
  		}
  		gc();
  
  	}
  
  	function _capture( canvas ) {
  
  		if( _capturing ) {
  
  			if( _settings.motionBlurFrames > 2 ) {
  
  				_checkFrame( canvas );
  				_blendFrame( canvas );
  
  				if( _intermediateFrameCount >= .5 * _settings.motionBlurFrames ) {
  					_saveFrame();
  				} else {
  					_step();
  				}
  
  			} else {
  				_encoder.add( canvas );
  				_frameCount++;
  				_log( 'Full Frame! ' + _frameCount );
  			}
  
  		}
  
  	}
  
  	function _process() {
  
  		var step = 1000 / _settings.framerate;
  		var dt = ( _frameCount + _intermediateFrameCount / _settings.motionBlurFrames ) * step;
  
  		_time = _startTime + dt;
  		_performanceTime = _performanceStartTime + dt;
  
  		media.forEach( function( v ) {
  			v._hookedTime = dt / 1000;
  		} );
  
  		_updateTime();
  		_log( 'Frame: ' + _frameCount + ' ' + _intermediateFrameCount );
  
  		for( var j = 0; j < _timeouts.length; j++ ) {
  			if( _time >= _timeouts[ j ].triggerTime ) {
  				_call( _timeouts[ j ].callback )
  				//console.log( 'timeout!' );
  				_timeouts.splice( j, 1 );
  				continue;
  			}
  		}
  
  		for( var j = 0; j < _intervals.length; j++ ) {
  			if( _time >= _intervals[ j ].triggerTime ) {
  				_call( _intervals[ j ].callback );
  				_intervals[ j ].triggerTime += _intervals[ j ].time;
  				//console.log( 'interval!' );
  				continue;
  			}
  		}
  
  		_requestAnimationFrameCallbacks.forEach( function( cb ) {
       		_call( cb, _time - g_startTime );
          } );
          _requestAnimationFrameCallbacks = [];
  
  	}
  
  	function _save( callback ) {
  
  		if( !callback ) {
  			callback = function( blob ) {
  				download( blob, _encoder.filename + _encoder.extension, _encoder.mimeType );
  				return false;
  			}
  		}
  		_encoder.save( callback );
  
  	}
  
  	function _log( message ) {
  		if( _verbose ) console.log( message );
  	}
  
      function _on( event, handler ) {
  
          _handlers[event] = handler;
  
      }
  
      function _emit( event ) {
  
          var handler = _handlers[event];
          if ( handler ) {
  
              handler.apply( null, Array.prototype.slice.call( arguments, 1 ) );
  
          }
  
      }
  
      function _progress( progress ) {
  
          _emit( 'progress', progress );
  
      }
  
  	return {
  		start: _start,
  		capture: _capture,
  		stop: _stop,
  		save: _save,
          on: _on
  	}
  }
  
  (freeWindow || freeSelf || {}).CCapture = CCapture;
  
    // Some AMD build optimizers like r.js check for condition patterns like the following:
    if (typeof define == 'function' && typeof define.amd == 'object' && define.amd) {
      // Define as an anonymous module so, through path mapping, it can be
      // referenced as the "underscore" module.
      define(function() {
      	return CCapture;
      });
  }
    // Check for `exports` after `define` in case a build optimizer adds an `exports` object.
    else if (freeExports && freeModule) {
      // Export for Node.js.
      if (moduleExports) {
      	(freeModule.exports = CCapture).CCapture = CCapture;
      }
      // Export for CommonJS support.
      freeExports.CCapture = CCapture;
  }
  else {
      // Export to the global object.
      root.CCapture = CCapture;
  }
  
  }());
  