ccapture.js
Version:
A library to capture canvas-based animations at a fixed framerate
992 lines (731 loc) • 25.6 kB
JavaScript
;(function() {
"use strict";
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;
}
}());