<commit_before>/**
* @author mrdoob / http://mrdoob.com/
*/

THREE.WebGLState = function ( gl, extensions, paramThreeToGL ) {

	var _this = this;

	var color = new THREE.Vector4();

	var newAttributes = new Uint8Array( 16 );
	var enabledAttributes = new Uint8Array( 16 );
	var attributeDivisors = new Uint8Array( 16 );

	var capabilities = {};

	var compressedTextureFormats = null;

	var currentBlending = null;
	var currentBlendEquation = null;
	var currentBlendSrc = null;
	var currentBlendDst = null;
	var currentBlendEquationAlpha = null;
	var currentBlendSrcAlpha = null;
	var currentBlendDstAlpha = null;

	var currentDepthFunc = null;
	var currentDepthWrite = null;

	var currentColorWrite = null;

	var currentStencilWrite = null;
	var currentStencilFunc = null;
	var currentStencilRef = null;
	var currentStencilMask = null;
	var currentStencilFail  = null;
	var currentStencilZFail = null;
	var currentStencilZPass = null;

	var currentFlipSided = null;

	var currentLineWidth = null;

	var currentPolygonOffsetFactor = null;
	var currentPolygonOffsetUnits = null;

	var currentScissorTest = null;

	var maxTextures = gl.getParameter( gl.MAX_TEXTURE_IMAGE_UNITS );

	var currentTextureSlot = undefined;
	var currentBoundTextures = {};

	var currentClearColor = new THREE.Vector4();
	var currentClearDepth = null;
	var currentClearStencil = null;

	var currentScissor = new THREE.Vector4();
	var currentViewport = new THREE.Vector4();

	var emptyTexture = gl.createTexture();
	gl.bindTexture(gl.TEXTURE_2D, emptyTexture);
	gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
	gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, 1, 1, 0, gl.RGB, gl.UNSIGNED_BYTE, new Uint8Array([
		0, 0, 0
	]));

	this.init = function () {

		this.clearColor( 0, 0, 0, 1 );
		this.clearDepth( 1 );
		this.clearStencil( 0 );

		this.enable( gl.DEPTH_TEST );
		gl.depthFunc( gl.LEQUAL );

		gl.frontFace( gl.CCW );
		gl.cullFace( gl.BACK );
		this.enable( gl.CULL_FACE );

		this.enable( gl.BLEND );
		gl.blendEquation( gl.FUNC_ADD );
		gl.blendFunc( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA );

	};

	this.initAttributes = function () {

		for ( var i = 0, l = newAttributes.length; i < l; i ++ ) {

			newAttributes[ i ] = 0;

		}

	};

	this.enableAttribute = function ( attribute ) {

		newAttributes[ attribute ] = 1;

		if ( enabledAttributes[ attribute ] === 0 ) {

			gl.enableVertexAttribArray( attribute );
			enabledAttributes[ attribute ] = 1;

		}

		if ( attributeDivisors[ attribute ] !== 0 ) {

			var extension = extensions.get( 'ANGLE_instanced_arrays' );

			extension.vertexAttribDivisorANGLE( attribute, 0 );
			attributeDivisors[ attribute ] = 0;

		}

	};

	this.enableAttributeAndDivisor = function ( attribute, meshPerAttribute, extension ) {

		newAttributes[ attribute ] = 1;

		if ( enabledAttributes[ attribute ] === 0 ) {

			gl.enableVertexAttribArray( attribute );
			enabledAttributes[ attribute ] = 1;

		}

		if ( attributeDivisors[ attribute ] !== meshPerAttribute ) {

			extension.vertexAttribDivisorANGLE( attribute, meshPerAttribute );
			attributeDivisors[ attribute ] = meshPerAttribute;

		}

	};

	this.disableUnusedAttributes = function () {

		for ( var i = 0, l = enabledAttributes.length; i < l; i ++ ) {

			if ( enabledAttributes[ i ] !== newAttributes[ i ] ) {

				gl.disableVertexAttribArray( i );
				enabledAttributes[ i ] = 0;

			}

		}

	};

	this.enable = function ( id ) {

		if ( capabilities[ id ] !== true ) {

			gl.enable( id );
			capabilities[ id ] = true;

		}

	};

	this.disable = function ( id ) {

		if ( capabilities[ id ] !== false ) {

			gl.disable( id );
			capabilities[ id ] = false;

		}

	};

	this.getCompressedTextureFormats = function () {

		if ( compressedTextureFormats === null ) {

			compressedTextureFormats = [];

			if ( extensions.get( 'WEBGL_compressed_texture_pvrtc' ) ||
			     extensions.get( 'WEBGL_compressed_texture_s3tc' ) ||
			     extensions.get( 'WEBGL_compressed_texture_etc1' )) {

				var formats = gl.getParameter( gl.COMPRESSED_TEXTURE_FORMATS );

				for ( var i = 0; i < formats.length; i ++ ) {

					compressedTextureFormats.push( formats[ i ] );

				}

			}

		}

		return compressedTextureFormats;

	};

	this.setBlending = function ( blending, blendEquation, blendSrc, blendDst, blendEquationAlpha, blendSrcAlpha, blendDstAlpha ) {

		if ( blending === THREE.NoBlending ) {

			this.disable( gl.BLEND );

		} else {

			this.enable( gl.BLEND );

		}

		if ( blending !== currentBlending ) {

			if ( blending === THREE.AdditiveBlending ) {

				gl.blendEquation( gl.FUNC_ADD );
				gl.blendFunc( gl.SRC_ALPHA, gl.ONE );

			} else if ( blending === THREE.SubtractiveBlending ) {

				// TODO: Find blendFuncSeparate() combination

				gl.blendEquation( gl.FUNC_ADD );
				gl.blendFunc( gl.ZERO, gl.ONE_MINUS_SRC_COLOR );

			} else if ( blending === THREE.MultiplyBlending ) {

				// TODO: Find blendFuncSeparate() combination

				gl.blendEquation( gl.FUNC_ADD );
				gl.blendFunc( gl.ZERO, gl.SRC_COLOR );

			} else {

				gl.blendEquationSeparate( gl.FUNC_ADD, gl.FUNC_ADD );
				gl.blendFuncSeparate( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA );

			}

			currentBlending = blending;

		}

		if ( blending === THREE.CustomBlending ) {

			blendEquationAlpha = blendEquationAlpha || blendEquation;
			blendSrcAlpha = blendSrcAlpha || blendSrc;
			blendDstAlpha = blendDstAlpha || blendDst;

			if ( blendEquation !== currentBlendEquation || blendEquationAlpha !== currentBlendEquationAlpha ) {

				gl.blendEquationSeparate( paramThreeToGL( blendEquation ), paramThreeToGL( blendEquationAlpha ) );

				currentBlendEquation = blendEquation;
				currentBlendEquationAlpha = blendEquationAlpha;

			}

			if ( blendSrc !== currentBlendSrc || blendDst !== currentBlendDst || blendSrcAlpha !== currentBlendSrcAlpha || blendDstAlpha !== currentBlendDstAlpha ) {

				gl.blendFuncSeparate( paramThreeToGL( blendSrc ), paramThreeToGL( blendDst ), paramThreeToGL( blendSrcAlpha ), paramThreeToGL( blendDstAlpha ) );

				currentBlendSrc = blendSrc;
				currentBlendDst = blendDst;
				currentBlendSrcAlpha = blendSrcAlpha;
				currentBlendDstAlpha = blendDstAlpha;

			}

		} else {

			currentBlendEquation = null;
			currentBlendSrc = null;
			currentBlendDst = null;
			currentBlendEquationAlpha = null;
			currentBlendSrcAlpha = null;
			currentBlendDstAlpha = null;

		}

	};

	this.setDepthFunc = function ( depthFunc ) {

		if ( currentDepthFunc !== depthFunc ) {

			if ( depthFunc ) {

				switch ( depthFunc ) {

					case THREE.NeverDepth:

						gl.depthFunc( gl.NEVER );
						break;

					case THREE.AlwaysDepth:

						gl.depthFunc( gl.ALWAYS );
						break;

					case THREE.LessDepth:

						gl.depthFunc( gl.LESS );
						break;

					case THREE.LessEqualDepth:

						gl.depthFunc( gl.LEQUAL );
						break;

					case THREE.EqualDepth:

						gl.depthFunc( gl.EQUAL );
						break;

					case THREE.GreaterEqualDepth:

						gl.depthFunc( gl.GEQUAL );
						break;

					case THREE.GreaterDepth:

						gl.depthFunc( gl.GREATER );
						break;

					case THREE.NotEqualDepth:

						gl.depthFunc( gl.NOTEQUAL );
						break;

					default:

						gl.depthFunc( gl.LEQUAL );

				}

			} else {

				gl.depthFunc( gl.LEQUAL );

			}

			currentDepthFunc = depthFunc;

		}

	};

	this.setDepthTest = function ( depthTest ) {

		if ( depthTest ) {

			this.enable( gl.DEPTH_TEST );

		} else {

			this.disable( gl.DEPTH_TEST );

		}

	};

	this.setDepthWrite = function ( depthWrite ) {

		// TODO: Rename to setDepthMask

		if ( currentDepthWrite !== depthWrite ) {

			gl.depthMask( depthWrite );
			currentDepthWrite = depthWrite;

		}

	};

	this.setColorWrite = function ( colorWrite ) {

		// TODO: Rename to setColorMask

		if ( currentColorWrite !== colorWrite ) {

			gl.colorMask( colorWrite, colorWrite, colorWrite, colorWrite );
			currentColorWrite = colorWrite;

		}

	};

	this.setStencilFunc = function ( stencilFunc, stencilRef, stencilMask ) {

		if ( currentStencilFunc !== stencilFunc ||
				 currentStencilRef 	!== stencilRef 	||
				 currentStencilMask !== stencilMask ) {

			gl.stencilFunc( stencilFunc,  stencilRef, stencilMask );

			currentStencilFunc = stencilFunc;
			currentStencilRef  = stencilRef;
			currentStencilMask = stencilMask;

		}

	};

	this.setStencilOp = function ( stencilFail, stencilZFail, stencilZPass ) {

		if ( currentStencilFail	 !== stencilFail 	||
				 currentStencilZFail !== stencilZFail ||
				 currentStencilZPass !== stencilZPass ) {

			gl.stencilOp( stencilFail,  stencilZFail, stencilZPass );

			currentStencilFail  = stencilFail;
			currentStencilZFail = stencilZFail;
			currentStencilZPass = stencilZPass;

		}

	};

	this.setStencilTest = function ( stencilTest ) {

		if ( stencilTest ) {

			this.enable( gl.STENCIL_TEST );

		} else {

			this.disable( gl.STENCIL_TEST );

		}

	};

	this.setStencilWrite = function ( stencilWrite ) {

		// TODO: Rename to setStencilMask

		if ( currentStencilWrite !== stencilWrite ) {

			gl.stencilMask( stencilWrite );
			currentStencilWrite = stencilWrite;

		}

	};

	this.setFlipSided = function ( flipSided ) {

		if ( currentFlipSided !== flipSided ) {

			if ( flipSided ) {

				gl.frontFace( gl.CW );

			} else {

				gl.frontFace( gl.CCW );

			}

			currentFlipSided = flipSided;

		}

	};

	this.setLineWidth = function ( width ) {

		if ( width !== currentLineWidth ) {

			gl.lineWidth( width );

			currentLineWidth = width;

		}

	};

	this.setPolygonOffset = function ( polygonOffset, factor, units ) {

		if ( polygonOffset ) {

			this.enable( gl.POLYGON_OFFSET_FILL );

		} else {

			this.disable( gl.POLYGON_OFFSET_FILL );

		}

		if ( polygonOffset && ( currentPolygonOffsetFactor !== factor || currentPolygonOffsetUnits !== units ) ) {

			gl.polygonOffset( factor, units );

			currentPolygonOffsetFactor = factor;
			currentPolygonOffsetUnits = units;

		}

	};

	this.getScissorTest = function () {

		return currentScissorTest;

	};

	this.setScissorTest = function ( scissorTest ) {

		currentScissorTest = scissorTest;

		if ( scissorTest ) {

			this.enable( gl.SCISSOR_TEST );

		} else {

			this.disable( gl.SCISSOR_TEST );

		}

	};

	// texture

	this.activeTexture = function ( webglSlot ) {

		if ( webglSlot === undefined ) webglSlot = gl.TEXTURE0 + maxTextures - 1;

		if ( currentTextureSlot !== webglSlot ) {

			gl.activeTexture( webglSlot );
			currentTextureSlot = webglSlot;

		}

	};

	this.bindTexture = function ( webglType, webglTexture ) {

		if ( currentTextureSlot === undefined ) {

			_this.activeTexture();

		}

		var boundTexture = currentBoundTextures[ currentTextureSlot ];

		if ( boundTexture === undefined ) {

			boundTexture = { type: undefined, texture: undefined };
			currentBoundTextures[ currentTextureSlot ] = boundTexture;

		}

		if ( boundTexture.type !== webglType || boundTexture.texture !== webglTexture ) {

			gl.bindTexture( webglType, webglTexture || emptyTexture );

			boundTexture.type = webglType;
			boundTexture.texture = webglTexture;

		}

	};

	this.compressedTexImage2D = function () {

		try {

			gl.compressedTexImage2D.apply( gl, arguments );

		} catch ( error ) {

			console.error( error );

		}

	};

	this.texImage2D = function () {

		try {

			gl.texImage2D.apply( gl, arguments );

		} catch ( error ) {

			console.error( error );

		}

	};

	// clear values

	this.clearColor = function ( r, g, b, a ) {

		color.set( r, g, b, a );

		if ( currentClearColor.equals( color ) === false ) {

			gl.clearColor( r, g, b, a );
			currentClearColor.copy( color );

		}

	};

	this.clearDepth = function ( depth ) {

		if ( currentClearDepth !== depth ) {

			gl.clearDepth( depth );
			currentClearDepth = depth;

		}

	};

	this.clearStencil = function ( stencil ) {

		if ( currentClearStencil !== stencil ) {

			gl.clearStencil( stencil );
			currentClearStencil = stencil;

		}

	};

	//

	this.scissor = function ( scissor ) {

		if ( currentScissor.equals( scissor ) === false ) {

			gl.scissor( scissor.x, scissor.y, scissor.z, scissor.w );
			currentScissor.copy( scissor );

		}

	};

	this.viewport = function ( viewport ) {

		if ( currentViewport.equals( viewport ) === false ) {

			gl.viewport( viewport.x, viewport.y, viewport.z, viewport.w );
			currentViewport.copy( viewport );

		}

	};

	//

	this.reset = function () {

		for ( var i = 0; i < enabledAttributes.length; i ++ ) {

			if ( enabledAttributes[ i ] === 1 ) {

				gl.disableVertexAttribArray( i );
				enabledAttributes[ i ] = 0;

			}

		}

		capabilities = {};

		compressedTextureFormats = null;

		currentBlending = null;

		currentColorWrite = null;
		currentDepthWrite = null;
		currentStencilWrite = null;

		currentFlipSided = null;

	};

};
<commit_msg>WebGLState: Clean up.<commit_after>/**
* @author mrdoob / http://mrdoob.com/
*/

THREE.WebGLState = function ( gl, extensions, paramThreeToGL ) {

	var _this = this;

	var color = new THREE.Vector4();

	var newAttributes = new Uint8Array( 16 );
	var enabledAttributes = new Uint8Array( 16 );
	var attributeDivisors = new Uint8Array( 16 );

	var capabilities = {};

	var compressedTextureFormats = null;

	var currentBlending = null;
	var currentBlendEquation = null;
	var currentBlendSrc = null;
	var currentBlendDst = null;
	var currentBlendEquationAlpha = null;
	var currentBlendSrcAlpha = null;
	var currentBlendDstAlpha = null;

	var currentDepthFunc = null;
	var currentDepthWrite = null;

	var currentColorWrite = null;

	var currentStencilWrite = null;
	var currentStencilFunc = null;
	var currentStencilRef = null;
	var currentStencilMask = null;
	var currentStencilFail  = null;
	var currentStencilZFail = null;
	var currentStencilZPass = null;

	var currentFlipSided = null;

	var currentLineWidth = null;

	var currentPolygonOffsetFactor = null;
	var currentPolygonOffsetUnits = null;

	var currentScissorTest = null;

	var maxTextures = gl.getParameter( gl.MAX_TEXTURE_IMAGE_UNITS );

	var currentTextureSlot = undefined;
	var currentBoundTextures = {};

	var currentClearColor = new THREE.Vector4();
	var currentClearDepth = null;
	var currentClearStencil = null;

	var currentScissor = new THREE.Vector4();
	var currentViewport = new THREE.Vector4();

	var emptyTexture = gl.createTexture();
	gl.bindTexture( gl.TEXTURE_2D, emptyTexture );
	gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR );
	gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGB, 1, 1, 0, gl.RGB, gl.UNSIGNED_BYTE, new Uint8Array( 3 ) );

	this.init = function () {

		this.clearColor( 0, 0, 0, 1 );
		this.clearDepth( 1 );
		this.clearStencil( 0 );

		this.enable( gl.DEPTH_TEST );
		gl.depthFunc( gl.LEQUAL );

		gl.frontFace( gl.CCW );
		gl.cullFace( gl.BACK );
		this.enable( gl.CULL_FACE );

		this.enable( gl.BLEND );
		gl.blendEquation( gl.FUNC_ADD );
		gl.blendFunc( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA );

	};

	this.initAttributes = function () {

		for ( var i = 0, l = newAttributes.length; i < l; i ++ ) {

			newAttributes[ i ] = 0;

		}

	};

	this.enableAttribute = function ( attribute ) {

		newAttributes[ attribute ] = 1;

		if ( enabledAttributes[ attribute ] === 0 ) {

			gl.enableVertexAttribArray( attribute );
			enabledAttributes[ attribute ] = 1;

		}

		if ( attributeDivisors[ attribute ] !== 0 ) {

			var extension = extensions.get( 'ANGLE_instanced_arrays' );

			extension.vertexAttribDivisorANGLE( attribute, 0 );
			attributeDivisors[ attribute ] = 0;

		}

	};

	this.enableAttributeAndDivisor = function ( attribute, meshPerAttribute, extension ) {

		newAttributes[ attribute ] = 1;

		if ( enabledAttributes[ attribute ] === 0 ) {

			gl.enableVertexAttribArray( attribute );
			enabledAttributes[ attribute ] = 1;

		}

		if ( attributeDivisors[ attribute ] !== meshPerAttribute ) {

			extension.vertexAttribDivisorANGLE( attribute, meshPerAttribute );
			attributeDivisors[ attribute ] = meshPerAttribute;

		}

	};

	this.disableUnusedAttributes = function () {

		for ( var i = 0, l = enabledAttributes.length; i < l; i ++ ) {

			if ( enabledAttributes[ i ] !== newAttributes[ i ] ) {

				gl.disableVertexAttribArray( i );
				enabledAttributes[ i ] = 0;

			}

		}

	};

	this.enable = function ( id ) {

		if ( capabilities[ id ] !== true ) {

			gl.enable( id );
			capabilities[ id ] = true;

		}

	};

	this.disable = function ( id ) {

		if ( capabilities[ id ] !== false ) {

			gl.disable( id );
			capabilities[ id ] = false;

		}

	};

	this.getCompressedTextureFormats = function () {

		if ( compressedTextureFormats === null ) {

			compressedTextureFormats = [];

			if ( extensions.get( 'WEBGL_compressed_texture_pvrtc' ) ||
			     extensions.get( 'WEBGL_compressed_texture_s3tc' ) ||
			     extensions.get( 'WEBGL_compressed_texture_etc1' ) ) {

				var formats = gl.getParameter( gl.COMPRESSED_TEXTURE_FORMATS );

				for ( var i = 0; i < formats.length; i ++ ) {

					compressedTextureFormats.push( formats[ i ] );

				}

			}

		}

		return compressedTextureFormats;

	};

	this.setBlending = function ( blending, blendEquation, blendSrc, blendDst, blendEquationAlpha, blendSrcAlpha, blendDstAlpha ) {

		if ( blending === THREE.NoBlending ) {

			this.disable( gl.BLEND );

		} else {

			this.enable( gl.BLEND );

		}

		if ( blending !== currentBlending ) {

			if ( blending === THREE.AdditiveBlending ) {

				gl.blendEquation( gl.FUNC_ADD );
				gl.blendFunc( gl.SRC_ALPHA, gl.ONE );

			} else if ( blending === THREE.SubtractiveBlending ) {

				// TODO: Find blendFuncSeparate() combination

				gl.blendEquation( gl.FUNC_ADD );
				gl.blendFunc( gl.ZERO, gl.ONE_MINUS_SRC_COLOR );

			} else if ( blending === THREE.MultiplyBlending ) {

				// TODO: Find blendFuncSeparate() combination

				gl.blendEquation( gl.FUNC_ADD );
				gl.blendFunc( gl.ZERO, gl.SRC_COLOR );

			} else {

				gl.blendEquationSeparate( gl.FUNC_ADD, gl.FUNC_ADD );
				gl.blendFuncSeparate( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA );

			}

			currentBlending = blending;

		}

		if ( blending === THREE.CustomBlending ) {

			blendEquationAlpha = blendEquationAlpha || blendEquation;
			blendSrcAlpha = blendSrcAlpha || blendSrc;
			blendDstAlpha = blendDstAlpha || blendDst;

			if ( blendEquation !== currentBlendEquation || blendEquationAlpha !== currentBlendEquationAlpha ) {

				gl.blendEquationSeparate( paramThreeToGL( blendEquation ), paramThreeToGL( blendEquationAlpha ) );

				currentBlendEquation = blendEquation;
				currentBlendEquationAlpha = blendEquationAlpha;

			}

			if ( blendSrc !== currentBlendSrc || blendDst !== currentBlendDst || blendSrcAlpha !== currentBlendSrcAlpha || blendDstAlpha !== currentBlendDstAlpha ) {

				gl.blendFuncSeparate( paramThreeToGL( blendSrc ), paramThreeToGL( blendDst ), paramThreeToGL( blendSrcAlpha ), paramThreeToGL( blendDstAlpha ) );

				currentBlendSrc = blendSrc;
				currentBlendDst = blendDst;
				currentBlendSrcAlpha = blendSrcAlpha;
				currentBlendDstAlpha = blendDstAlpha;

			}

		} else {

			currentBlendEquation = null;
			currentBlendSrc = null;
			currentBlendDst = null;
			currentBlendEquationAlpha = null;
			currentBlendSrcAlpha = null;
			currentBlendDstAlpha = null;

		}

	};

	this.setDepthFunc = function ( depthFunc ) {

		if ( currentDepthFunc !== depthFunc ) {

			if ( depthFunc ) {

				switch ( depthFunc ) {

					case THREE.NeverDepth:

						gl.depthFunc( gl.NEVER );
						break;

					case THREE.AlwaysDepth:

						gl.depthFunc( gl.ALWAYS );
						break;

					case THREE.LessDepth:

						gl.depthFunc( gl.LESS );
						break;

					case THREE.LessEqualDepth:

						gl.depthFunc( gl.LEQUAL );
						break;

					case THREE.EqualDepth:

						gl.depthFunc( gl.EQUAL );
						break;

					case THREE.GreaterEqualDepth:

						gl.depthFunc( gl.GEQUAL );
						break;

					case THREE.GreaterDepth:

						gl.depthFunc( gl.GREATER );
						break;

					case THREE.NotEqualDepth:

						gl.depthFunc( gl.NOTEQUAL );
						break;

					default:

						gl.depthFunc( gl.LEQUAL );

				}

			} else {

				gl.depthFunc( gl.LEQUAL );

			}

			currentDepthFunc = depthFunc;

		}

	};

	this.setDepthTest = function ( depthTest ) {

		if ( depthTest ) {

			this.enable( gl.DEPTH_TEST );

		} else {

			this.disable( gl.DEPTH_TEST );

		}

	};

	this.setDepthWrite = function ( depthWrite ) {

		// TODO: Rename to setDepthMask

		if ( currentDepthWrite !== depthWrite ) {

			gl.depthMask( depthWrite );
			currentDepthWrite = depthWrite;

		}

	};

	this.setColorWrite = function ( colorWrite ) {

		// TODO: Rename to setColorMask

		if ( currentColorWrite !== colorWrite ) {

			gl.colorMask( colorWrite, colorWrite, colorWrite, colorWrite );
			currentColorWrite = colorWrite;

		}

	};

	this.setStencilFunc = function ( stencilFunc, stencilRef, stencilMask ) {

		if ( currentStencilFunc !== stencilFunc ||
				 currentStencilRef 	!== stencilRef 	||
				 currentStencilMask !== stencilMask ) {

			gl.stencilFunc( stencilFunc,  stencilRef, stencilMask );

			currentStencilFunc = stencilFunc;
			currentStencilRef  = stencilRef;
			currentStencilMask = stencilMask;

		}

	};

	this.setStencilOp = function ( stencilFail, stencilZFail, stencilZPass ) {

		if ( currentStencilFail	 !== stencilFail 	||
				 currentStencilZFail !== stencilZFail ||
				 currentStencilZPass !== stencilZPass ) {

			gl.stencilOp( stencilFail,  stencilZFail, stencilZPass );

			currentStencilFail  = stencilFail;
			currentStencilZFail = stencilZFail;
			currentStencilZPass = stencilZPass;

		}

	};

	this.setStencilTest = function ( stencilTest ) {

		if ( stencilTest ) {

			this.enable( gl.STENCIL_TEST );

		} else {

			this.disable( gl.STENCIL_TEST );

		}

	};

	this.setStencilWrite = function ( stencilWrite ) {

		// TODO: Rename to setStencilMask

		if ( currentStencilWrite !== stencilWrite ) {

			gl.stencilMask( stencilWrite );
			currentStencilWrite = stencilWrite;

		}

	};

	this.setFlipSided = function ( flipSided ) {

		if ( currentFlipSided !== flipSided ) {

			if ( flipSided ) {

				gl.frontFace( gl.CW );

			} else {

				gl.frontFace( gl.CCW );

			}

			currentFlipSided = flipSided;

		}

	};

	this.setLineWidth = function ( width ) {

		if ( width !== currentLineWidth ) {

			gl.lineWidth( width );

			currentLineWidth = width;

		}

	};

	this.setPolygonOffset = function ( polygonOffset, factor, units ) {

		if ( polygonOffset ) {

			this.enable( gl.POLYGON_OFFSET_FILL );

		} else {

			this.disable( gl.POLYGON_OFFSET_FILL );

		}

		if ( polygonOffset && ( currentPolygonOffsetFactor !== factor || currentPolygonOffsetUnits !== units ) ) {

			gl.polygonOffset( factor, units );

			currentPolygonOffsetFactor = factor;
			currentPolygonOffsetUnits = units;

		}

	};

	this.getScissorTest = function () {

		return currentScissorTest;

	};

	this.setScissorTest = function ( scissorTest ) {

		currentScissorTest = scissorTest;

		if ( scissorTest ) {

			this.enable( gl.SCISSOR_TEST );

		} else {

			this.disable( gl.SCISSOR_TEST );

		}

	};

	// texture

	this.activeTexture = function ( webglSlot ) {

		if ( webglSlot === undefined ) webglSlot = gl.TEXTURE0 + maxTextures - 1;

		if ( currentTextureSlot !== webglSlot ) {

			gl.activeTexture( webglSlot );
			currentTextureSlot = webglSlot;

		}

	};

	this.bindTexture = function ( webglType, webglTexture ) {

		if ( currentTextureSlot === undefined ) {

			_this.activeTexture();

		}

		var boundTexture = currentBoundTextures[ currentTextureSlot ];

		if ( boundTexture === undefined ) {

			boundTexture = { type: undefined, texture: undefined };
			currentBoundTextures[ currentTextureSlot ] = boundTexture;

		}

		if ( boundTexture.type !== webglType || boundTexture.texture !== webglTexture ) {

			gl.bindTexture( webglType, webglTexture || emptyTexture );

			boundTexture.type = webglType;
			boundTexture.texture = webglTexture;

		}

	};

	this.compressedTexImage2D = function () {

		try {

			gl.compressedTexImage2D.apply( gl, arguments );

		} catch ( error ) {

			console.error( error );

		}

	};

	this.texImage2D = function () {

		try {

			gl.texImage2D.apply( gl, arguments );

		} catch ( error ) {

			console.error( error );

		}

	};

	// clear values

	this.clearColor = function ( r, g, b, a ) {

		color.set( r, g, b, a );

		if ( currentClearColor.equals( color ) === false ) {

			gl.clearColor( r, g, b, a );
			currentClearColor.copy( color );

		}

	};

	this.clearDepth = function ( depth ) {

		if ( currentClearDepth !== depth ) {

			gl.clearDepth( depth );
			currentClearDepth = depth;

		}

	};

	this.clearStencil = function ( stencil ) {

		if ( currentClearStencil !== stencil ) {

			gl.clearStencil( stencil );
			currentClearStencil = stencil;

		}

	};

	//

	this.scissor = function ( scissor ) {

		if ( currentScissor.equals( scissor ) === false ) {

			gl.scissor( scissor.x, scissor.y, scissor.z, scissor.w );
			currentScissor.copy( scissor );

		}

	};

	this.viewport = function ( viewport ) {

		if ( currentViewport.equals( viewport ) === false ) {

			gl.viewport( viewport.x, viewport.y, viewport.z, viewport.w );
			currentViewport.copy( viewport );

		}

	};

	//

	this.reset = function () {

		for ( var i = 0; i < enabledAttributes.length; i ++ ) {

			if ( enabledAttributes[ i ] === 1 ) {

				gl.disableVertexAttribArray( i );
				enabledAttributes[ i ] = 0;

			}

		}

		capabilities = {};

		compressedTextureFormats = null;

		currentBlending = null;

		currentColorWrite = null;
		currentDepthWrite = null;
		currentStencilWrite = null;

		currentFlipSided = null;

	};

};
<|endoftext|>


<commit_before>/* global describe, it, beforeEach */

const Brave = require('../lib/brave')
const messages = require('../../js/constants/messages')
const {urlInput, urlBarSuggestions} = require('../lib/selectors')

describe('urlBarSuggestions', function () {
  function * setup (client) {
    yield client
      .waitForUrl(Brave.newTabUrl)
      .waitForBrowserWindow()
      .waitForVisible(urlInput)
  }

  Brave.beforeEach(this)
  beforeEach(function * () {
    this.page1Url = Brave.server.url('page1.html')
    this.page2Url = Brave.server.url('page2.html')

    yield setup(this.app.client)
    const page1Url = this.page1Url
    const page2Url = this.page2Url
    yield this.app.client
      .tabByIndex(0)
      .loadUrl(page1Url)
      .windowByUrl(Brave.browserWindowUrl)
      .waitUntil(function () {
        return this.getAppState().then((val) => {
          return !!val.value.sites.find((site) => site.location === page1Url)
        })
      })
      .tabByIndex(0)
      .loadUrl(this.page2Url)
      .windowByUrl(Brave.browserWindowUrl)
      .ipcSend(messages.SHORTCUT_NEW_FRAME)
      .waitForUrl(Brave.newTabUrl)
      .windowByUrl(Brave.browserWindowUrl)
      .waitForExist('.tab[data-frame-key="2"].active')
      .waitForElementFocus(urlInput)
      .waitUntil(function () {
        return this.getAppState().then((val) => {
          return !!val.value.sites.find((site) => site.location === page2Url)
        })
      })
  })

  it('show suggestion when single letter is typed in', function * () {
    yield this.app.client.ipcSend('shortcut-focus-url')
      .waitForElementFocus(urlInput)
      .setValue(urlInput, 'a')
      .waitUntil(function () {
        return this.getValue(urlInput).then((val) => val === 'a')
      })
      .waitForExist(urlBarSuggestions)
  })

  it('deactivates suggestions on escape', function * () {
    yield this.app.client
      .setValue(urlInput, 'Page 1')
      .waitUntil(function () {
        return this.getValue(urlInput).then((val) => val === 'Page 1')
      })
      .waitForExist(urlBarSuggestions + ' li.suggestionItem[data-index="1"]')
      .keys(Brave.keys.ESCAPE)
      .waitUntil(function () {
        return this.isExisting(urlBarSuggestions).then((exists) => exists === false)
      })
  })

  it('deactivates suggestions on backspace', function * () {
    yield this.app.client
      .setValue(urlInput, 'Page 1')
      .waitUntil(function () {
        return this.getValue(urlInput).then((val) => val === 'Page 1')
      })
      .waitForExist(urlBarSuggestions + ' li.suggestionItem[data-index="1"]')
      .keys(Brave.keys.BACKSPACE)
      .waitUntil(function () {
        return this.isExisting(urlBarSuggestions).then((exists) => exists === false)
      })
  })

  it('deactivates suggestions on delete', function * () {
    yield this.app.client
      .setValue(urlInput, 'Page 1')
      .waitUntil(function () {
        return this.getValue(urlInput).then((val) => val === 'Page 1')
      })
      .waitForExist(urlBarSuggestions + ' li.suggestionItem[data-index="1"]')
      .keys(Brave.keys.DELETE)
      .waitUntil(function () {
        return this.isExisting(urlBarSuggestions).then((exists) => exists === false)
      })
  })

  it('navigates to a suggestion when clicked', function * () {
    yield this.app.client
      .setValue(urlInput, 'Page 1')
      .waitUntil(function () {
        return this.getValue(urlInput).then((val) => val === 'Page 1')
      })
      .waitForExist(urlBarSuggestions + ' li.suggestionItem[data-index="1"]')
      .click(urlBarSuggestions + ' li.suggestionItem[data-index="1"]')
      .tabByIndex(1).getUrl().should.eventually.equal(this.page1Url)
  })

  it('navigates to a suggestion with keyboard', function * () {
    yield this.app.client
      .setValue(urlInput, 'Page')
      .waitUntil(function () {
        return this.getValue(urlInput).then((val) => val === 'Page')
      })
      .waitForExist(urlBarSuggestions)
      .keys(Brave.keys.DOWN)
      .waitForExist(urlBarSuggestions + ' li.suggestionItem[data-index="1"].selected')
      .keys(Brave.keys.DOWN)
      .waitForExist(urlBarSuggestions + ' li.suggestionItem[data-index="2"].selected')
      .keys(Brave.keys.ENTER)
      .tabByIndex(1).getUrl().should.become(this.page1Url)
  })

  it('selects a location auto complete result but not for titles', function * () {
    const page2Url = Brave.server.url('page2.html')
    yield this.app.client
      .setValue(urlInput, 'http://')
      .waitUntil(function () {
        return this.getValue(urlInput).then((val) => val === page2Url)
      })
      .waitForExist(urlBarSuggestions + ' li.selected')
      .setValue(urlInput, 'Page')
      .waitForExist(urlBarSuggestions + ' li.selected', 1000, true)
  })

  it('on suggestion mouseover, appends autocomplete URLs without interrupting typing', function * () {
    const page1Url = Brave.server.url('page1.html')
    const page2Url = Brave.server.url('page2.html')
    const pagePartialUrl = Brave.server.url('page')
    // test that entering a partial URL which matches two options autocompletes initially to 1st option,
    // but switches to other option when that is highlighted, while keeping cursor in same position,
    // so that finally, if the rest of the 1st option is entered via keyboard, it overwrites the suggestion from the mouse
    yield this.app.client
      .keys(pagePartialUrl)
      .waitUntil(function () {
        return this.getValue(urlInput).then(function (val) {
          return val === page2Url // after entering partial URL matching two options, 1st is tentatively filled in (_without_ moving cursor to end)
        })
      })
      .waitForExist(urlBarSuggestions + ' li.suggestionItem')
      .moveToObject(urlBarSuggestions + ' li.suggestionItem:not(.selected)')
      .waitUntil(function () {
        return this.getValue(urlInput).then(function (val) {
          return val === page1Url // mousing over 2nd option tentatively completes URL with 2nd option (_without_ moving cursor to end)
        })
      })
      .keys('2.html')
      .waitUntil(function () {
        return this.getValue(urlInput).then(function (val) {
          return val === page2Url // without moving mouse, typing rest of 1st option URL overwrites the autocomplete from mouseover
        })
      })
  })

  it('selection is not reset', function * () {
    const pagePartialUrl = Brave.server.url('page')
    yield this.app.client
      .setValue(urlInput, pagePartialUrl)
      .waitForExist(urlBarSuggestions)
      .keys(Brave.keys.DOWN)
      .keys(Brave.keys.CONTROL)
      .keys(Brave.keys.CONTROL)
      .waitForSelectedText('1.html')
  })
})
<commit_msg>Fix intermittent fail for url suggestions<commit_after>/* global describe, it, beforeEach */

const Brave = require('../lib/brave')
const messages = require('../../js/constants/messages')
const {urlInput, urlBarSuggestions} = require('../lib/selectors')

describe('urlBarSuggestions', function () {
  function * setup (client) {
    yield client
      .waitForUrl(Brave.newTabUrl)
      .waitForBrowserWindow()
      .waitForVisible(urlInput)
  }

  Brave.beforeEach(this)
  beforeEach(function * () {
    this.page1Url = Brave.server.url('page1.html')
    this.page2Url = Brave.server.url('page2.html')

    yield setup(this.app.client)
    const page1Url = this.page1Url
    const page2Url = this.page2Url
    yield this.app.client
      .tabByIndex(0)
      .loadUrl(page1Url)
      .windowByUrl(Brave.browserWindowUrl)
      .waitUntil(function () {
        return this.getAppState().then((val) => {
          return !!val.value.sites.find((site) => site.location === page1Url)
        })
      })
      .tabByIndex(0)
      .loadUrl(this.page2Url)
      .windowByUrl(Brave.browserWindowUrl)
      .ipcSend(messages.SHORTCUT_NEW_FRAME)
      .waitForUrl(Brave.newTabUrl)
      .windowByUrl(Brave.browserWindowUrl)
      .waitForExist('.tab[data-frame-key="2"].active')
      .waitForElementFocus(urlInput)
      .waitUntil(function () {
        return this.getAppState().then((val) => {
          return !!val.value.sites.find((site) => site.location === page2Url)
        })
      })
  })

  it('show suggestion when single letter is typed in', function * () {
    yield this.app.client.ipcSend('shortcut-focus-url')
      .waitForElementFocus(urlInput)
      .setValue(urlInput, 'a')
      .waitUntil(function () {
        return this.getValue(urlInput).then((val) => val === 'a')
      })
      .waitForExist(urlBarSuggestions)
  })

  it('deactivates suggestions on escape', function * () {
    yield this.app.client
      .setValue(urlInput, 'Page 1')
      .waitUntil(function () {
        return this.getValue(urlInput).then((val) => val === 'Page 1')
      })
      .waitForExist(urlBarSuggestions + ' li.suggestionItem[data-index="1"]')
      .keys(Brave.keys.ESCAPE)
      .waitUntil(function () {
        return this.isExisting(urlBarSuggestions).then((exists) => exists === false)
      })
  })

  it('deactivates suggestions on backspace', function * () {
    yield this.app.client
      .setValue(urlInput, 'Page 1')
      .waitUntil(function () {
        return this.getValue(urlInput).then((val) => val === 'Page 1')
      })
      .waitForExist(urlBarSuggestions + ' li.suggestionItem[data-index="1"]')
      .keys(Brave.keys.BACKSPACE)
      .waitUntil(function () {
        return this.isExisting(urlBarSuggestions).then((exists) => exists === false)
      })
  })

  it('deactivates suggestions on delete', function * () {
    yield this.app.client
      .setValue(urlInput, 'Page 1')
      .waitUntil(function () {
        return this.getValue(urlInput).then((val) => val === 'Page 1')
      })
      .waitForExist(urlBarSuggestions + ' li.suggestionItem[data-index="1"]')
      .keys(Brave.keys.DELETE)
      .waitUntil(function () {
        return this.isExisting(urlBarSuggestions).then((exists) => exists === false)
      })
  })

  it('navigates to a suggestion when clicked', function * () {
    yield this.app.client
      .setValue(urlInput, 'Page 1')
      .waitUntil(function () {
        return this.getValue(urlInput).then((val) => val === 'Page 1')
      })
      .waitForExist(urlBarSuggestions + ' li.suggestionItem[data-index="1"]')
      .click(urlBarSuggestions + ' li.suggestionItem[data-index="1"]')
      .tabByIndex(1).getUrl().should.eventually.equal(this.page1Url)
  })

  it('navigates to a suggestion with keyboard', function * () {
    yield this.app.client
      .setValue(urlInput, 'Page')
      .waitUntil(function () {
        return this.getValue(urlInput).then((val) => val === 'Page')
      })
      .waitForExist(urlBarSuggestions)
      .keys(Brave.keys.DOWN)
      .waitForExist(urlBarSuggestions + ' li.suggestionItem[data-index="1"].selected')
      .keys(Brave.keys.DOWN)
      .waitForExist(urlBarSuggestions + ' li.suggestionItem[data-index="2"].selected')
      .keys(Brave.keys.ENTER)
      .tabByIndex(1).getUrl().should.become(this.page1Url)
  })

  it('selects a location auto complete result but not for titles', function * () {
    const page2Url = Brave.server.url('page2.html')
    yield this.app.client
      .setValue(urlInput, 'http://')
      .waitUntil(function () {
        return this.getValue(urlInput).then((val) => val === page2Url)
      })
      .waitForExist(urlBarSuggestions + ' li.selected')
      .setValue(urlInput, 'Page')
      .waitForExist(urlBarSuggestions + ' li.selected', 1000, true)
  })

  it('on suggestion mouseover, appends autocomplete URLs without interrupting typing', function * () {
    const page1Url = Brave.server.url('page1.html')
    const page2Url = Brave.server.url('page2.html')
    const pagePartialUrl = Brave.server.url('page')
    // test that entering a partial URL which matches two options autocompletes initially to 1st option,
    // but switches to other option when that is highlighted, while keeping cursor in same position,
    // so that finally, if the rest of the 1st option is entered via keyboard, it overwrites the suggestion from the mouse
    yield this.app.client
      .keys(pagePartialUrl)
      .waitUntil(function () {
        return this.getValue(urlInput).then(function (val) {
          return val === page2Url // after entering partial URL matching two options, 1st is tentatively filled in (_without_ moving cursor to end)
        })
      })
      .waitForExist(urlBarSuggestions + ' li.suggestionItem')
      .moveToObject(urlBarSuggestions + ' li.suggestionItem:not(.selected)')
      .waitUntil(function () {
        return this.getValue(urlInput).then(function (val) {
          return val === page1Url // mousing over 2nd option tentatively completes URL with 2nd option (_without_ moving cursor to end)
        })
      })
      .keys('2.html')
      .waitUntil(function () {
        return this.getValue(urlInput).then(function (val) {
          return val === page2Url // without moving mouse, typing rest of 1st option URL overwrites the autocomplete from mouseover
        })
      })
  })

  it('selection is not reset', function * () {
    const pagePartialUrl = Brave.server.url('page')
    yield this.app.client
      .moveToObject(urlInput)
      .setValue(urlInput, pagePartialUrl)
      .waitForExist(urlBarSuggestions)
      .keys(Brave.keys.DOWN)
      .keys(Brave.keys.CONTROL)
      .keys(Brave.keys.CONTROL)
      .waitForSelectedText('1.html')
  })
})
<|endoftext|>


<commit_before>'use strict';
const mongoose = require('mongoose');
const Schema = mongoose.Schema;

let scorecardTemplate = require('./templates/scorecard_template');

const CourseSchema = new Schema({
  name: {
    type: String,
    required: true,
    unique: true,
  },
  location: {
    type: String,
    required: true,
  },
  scorecard: scorecardTemplate,
},
{
  collection: 'courses',
});

module.exports = mongoose.model('Course', CourseSchema);
<commit_msg>added create and update dates to course model<commit_after>'use strict';
const mongoose = require('mongoose');
const Schema = mongoose.Schema;

let scorecardTemplate = require('./templates/scorecard_template');

const CourseSchema = new Schema({
  name: {
    type: String,
    required: true,
    unique: true,
  },
  location: {
    type: String,
    required: true,
  },
  scorecard: scorecardTemplate,
  createdAt: {
    type: Number,
    required: true,
  },
  updatedAt: {
    type: Number,
    required: true,
  },
},
{
  collection: 'courses',
});

module.exports = mongoose.model('Course', CourseSchema);
<|endoftext|>


<commit_before>'use strict';

const sinon = require('sinon'),
    chai = require('chai');

chai.should();

describe('Order Controller', function () {

    let req, res, models, saveStub;

    beforeEach(function() {
        models = require('../../app/models/orderModel');
        saveStub = sinon.stub();
        req = res = {};
        req.body = sinon.spy();
        res.status = sinon.spy();
        res.send = sinon.spy();
    });

    describe('ordering a beverage', function () {

        it('should return status 201 when the order was successful', sinon.test(function (done) {
            saveStub.yields(null);
            const Order = this.stub(models, 'Order');
            Order.returns({
                save: saveStub
            });
            const ctrl = require('../../app/controllers/orderController')(Order);

            ctrl.post(req, res);

            res.status.calledWith(201).should.be.true;
            done();
        }));

        it('should not send order back if it could not be saved', sinon.test(function (done) {
            saveStub.yields({});
            const Order = this.stub(models, 'Order');
            Order.returns({
                save: saveStub
            });
            const ctrl = require('../../app/controllers/orderController')(Order);

            ctrl.post(req, res);

            res.send.called.should.be.false;
            done();

        }));

        it('should save an order', sinon.test(function(done) {
            saveStub.yields(null);
            const Order = this.stub(models, 'Order');
            Order.returns({
                save: saveStub
            });

            const ctrl = require('../../app/controllers/orderController')(Order);
            ctrl.post(req, res);

            sinon.assert.calledOnce(saveStub);
            done();
        }));

        it('should send http status 500 when an error occurs during save', sinon.test(function(done) {
            saveStub.yields({});
            const Order = this.stub(models, 'Order');
            Order.returns({
                save: saveStub
            });

            const ctrl = require('../../app/controllers/orderController')(Order);

            ctrl.post(req, res);

            res.status.calledWith(500).should.be.true;
            done();

        }));
    });
});
<commit_msg>refactored order ctrl tests<commit_after>'use strict';

const sinon = require('sinon'),
    chai = require('chai');

chai.should();

describe('Order Controller', function () {

    let req, res, models, saveStub;

    beforeEach(function() {
        models = require('../../app/models/orderModel');
        saveStub = sinon.stub();
        req = res = {};
        req.body = sinon.spy();
        res.status = sinon.spy();
        res.send = sinon.spy();
    });

    function setupOrderCtrl(sandbox, saveYieldValue) {
        saveStub.yields(saveYieldValue);
        const Order = sandbox.stub(models, 'Order');
        Order.returns({
            save: saveStub
        });
        const ctrl = require('../../app/controllers/orderController')(Order);
        return ctrl;
    }

    describe('ordering a beverage', function () {

        it('should return status 201 when the order was successful', sinon.test(function (done) {
            const ctrl = setupOrderCtrl(this, null);

            ctrl.post(req, res);

            res.status.calledWith(201).should.be.true;
            done();
        }));

        it('should not send order back if it could not be saved', sinon.test(function (done) {
            const ctrl = setupOrderCtrl(this, {});

            ctrl.post(req, res);

            res.send.called.should.be.false;
            done();

        }));

        it('should save an order', sinon.test(function(done) {
            const ctrl = setupOrderCtrl(this, null);

            ctrl.post(req, res);

            sinon.assert.calledOnce(saveStub);
            done();
        }));

        it('should send http status 500 when an error occurs during save', sinon.test(function(done) {
            const ctrl = setupOrderCtrl(this, {});

            ctrl.post(req, res);

            res.status.calledWith(500).should.be.true;
            done();

        }));
    });
});
<|endoftext|>


<commit_before>
import moment from 'moment'
import UserRevenueModel from './UserRevenueModel'
import { isNil } from 'lodash/lang'

import decodeAmazonCPM from './decodeAmazonCPM'

const AMAZON_CPM_REVENUE_TYPE = 'AMAZON_CPM'
const AGGREGATION_MAX = 'MAX'

/**
 * Convert an encoded revenue object into a float value by a method specified
 * by the revenue object's "encodingType" field.
 * @param {object} revenueObj - The user authorizer object.
 * @param {string} revenueObj.encodingType - The name of the kind of object, which determines
 *   the logic we use to convert it into a number.
 * @param {string} revenueObj.encodedValue - The input value to use when decoding, which
 *   should resolve into a float.
 * @return {number} A float representing $USD amount of revenue
 */
const decodeRevenueObj = (revenueObj) => {
  var revenueVal
  switch (revenueObj.encodingType) {
    case AMAZON_CPM_REVENUE_TYPE:
      revenueVal = decodeAmazonCPM(revenueObj.encodedValue) / 1000
      break
    default:
      throw new Error('Invalid "encodingType" field for revenue object transformation')
  }
  return revenueVal
}

/**
 * Resolve an array of revenue float values down to a single float value by using
 * the specified aggregation logic.
 * @param {number[]} revenues - An array of one or more floats
 * @param {string} aggregationOperation - The name of the method we should use to resolve
 *   the revenue values down to a single revenue value
 * @return {number} A float representing $USD amount of revenue
 */
const aggregateRevenues = (revenues, aggregationOperation) => {
  var aggregatedRevenue
  switch (aggregationOperation) {
    case AGGREGATION_MAX:
      aggregatedRevenue = revenues.reduce((a, b) => Math.max(a, b))
      break
    default:
      throw new Error(`Invalid "aggregationOperation" value. Must be one of: "${AGGREGATION_MAX}"`)
  }
  return aggregatedRevenue
}

/**
 * Log revenue earned by a user.
 * @param {object} userContext - The user authorizer object.
 * @param {string} userId - The user id.
 * @param {number|null} revenue - The $USD amount of revenue earned. Required if no argument
 *   is provided for "encodedRevenue".
 * @param {string|null} dfpAdvertiserId - The DFP ID of the advertiser who paid the revenue.
 *   May be null if the advertiser identifier is not available on the client.
 *   Note that we stringify the DFP ID number because some are greater than 32 bits,
 *   which is not supported by default in GraphQL spec.
 * @param {Object|null} encodedRevenue - An object that can be transformed into a revenue number.
 *   Required if no argument is provided for "revenue".
 * @param {string|null} aggregationOperation - What logic we should use to determine a final
 *   revenue value when more than one value is provided. Required if both "revenue" and
 *   "encodedRevenue" are provided.
 * @return {Object} If successful, a single key ("success") with value `true`
 */
const logRevenue = async (userContext, userId, revenue = null, dfpAdvertiserId = null,
  encodedRevenue = null, aggregationOperation = null) => {
  // Decode the encoded revenue, if needed
  const decodedRevenue = isNil(encodedRevenue) ? null : decodeRevenueObj(encodedRevenue)

  var revenueToLog = null
  // Only received "revenue"
  if (!isNil(revenue) && isNil(decodedRevenue)) {
    revenueToLog = revenue
  // Only received "encodedRevenue"
  } else if (!isNil(decodedRevenue) && isNil(revenue)) {
    revenueToLog = decodedRevenue
  // Received both "revenue" and "encodedRevenue"
  } else if (!isNil(revenue) && !isNil(decodedRevenue)) {
    if (isNil(aggregationOperation)) {
      throw new Error('Revenue logging requires an "aggregationOperation" value if both "revenue" and "encodedRevenue" values are provided')
    }
    revenueToLog = aggregateRevenues([revenue, decodedRevenue], aggregationOperation)
  // Received no valid revenue value
  } else {
    throw new Error('Revenue logging requires either "revenue" or "encodedRevenue" values')
  }

  // If the revenue is an object, transform it into a number
  // const revenueNum = isPlainObject(revenue) ? decodeRevenueObj(revenue) : revenue

  try {
    await UserRevenueModel.create(userContext, {
      userId: userId,
      timestamp: moment.utc().toISOString(),
      revenue: revenueToLog,
      ...dfpAdvertiserId && { dfpAdvertiserId: dfpAdvertiserId }
    })
  } catch (e) {
    throw e
  }
  return { success: true }
}

export default logRevenue
<commit_msg>Remove unused code<commit_after>
import moment from 'moment'
import UserRevenueModel from './UserRevenueModel'
import { isNil } from 'lodash/lang'

import decodeAmazonCPM from './decodeAmazonCPM'

const AMAZON_CPM_REVENUE_TYPE = 'AMAZON_CPM'
const AGGREGATION_MAX = 'MAX'

/**
 * Convert an encoded revenue object into a float value by a method specified
 * by the revenue object's "encodingType" field.
 * @param {object} revenueObj - The user authorizer object.
 * @param {string} revenueObj.encodingType - The name of the kind of object, which determines
 *   the logic we use to convert it into a number.
 * @param {string} revenueObj.encodedValue - The input value to use when decoding, which
 *   should resolve into a float.
 * @return {number} A float representing $USD amount of revenue
 */
const decodeRevenueObj = (revenueObj) => {
  var revenueVal
  switch (revenueObj.encodingType) {
    case AMAZON_CPM_REVENUE_TYPE:
      revenueVal = decodeAmazonCPM(revenueObj.encodedValue) / 1000
      break
    default:
      throw new Error('Invalid "encodingType" field for revenue object transformation')
  }
  return revenueVal
}

/**
 * Resolve an array of revenue float values down to a single float value by using
 * the specified aggregation logic.
 * @param {number[]} revenues - An array of one or more floats
 * @param {string} aggregationOperation - The name of the method we should use to resolve
 *   the revenue values down to a single revenue value
 * @return {number} A float representing $USD amount of revenue
 */
const aggregateRevenues = (revenues, aggregationOperation) => {
  var aggregatedRevenue
  switch (aggregationOperation) {
    case AGGREGATION_MAX:
      aggregatedRevenue = revenues.reduce((a, b) => Math.max(a, b))
      break
    default:
      throw new Error(`Invalid "aggregationOperation" value. Must be one of: "${AGGREGATION_MAX}"`)
  }
  return aggregatedRevenue
}

/**
 * Log revenue earned by a user.
 * @param {object} userContext - The user authorizer object.
 * @param {string} userId - The user id.
 * @param {number|null} revenue - The $USD amount of revenue earned. Required if no argument
 *   is provided for "encodedRevenue".
 * @param {string|null} dfpAdvertiserId - The DFP ID of the advertiser who paid the revenue.
 *   May be null if the advertiser identifier is not available on the client.
 *   Note that we stringify the DFP ID number because some are greater than 32 bits,
 *   which is not supported by default in GraphQL spec.
 * @param {Object|null} encodedRevenue - An object that can be transformed into a revenue number.
 *   Required if no argument is provided for "revenue".
 * @param {string|null} aggregationOperation - What logic we should use to determine a final
 *   revenue value when more than one value is provided. Required if both "revenue" and
 *   "encodedRevenue" are provided.
 * @return {Object} If successful, a single key ("success") with value `true`
 */
const logRevenue = async (userContext, userId, revenue = null, dfpAdvertiserId = null,
  encodedRevenue = null, aggregationOperation = null) => {
  // Decode the encoded revenue, if needed
  const decodedRevenue = isNil(encodedRevenue) ? null : decodeRevenueObj(encodedRevenue)

  var revenueToLog = null
  // Only received "revenue"
  if (!isNil(revenue) && isNil(decodedRevenue)) {
    revenueToLog = revenue
  // Only received "encodedRevenue"
  } else if (!isNil(decodedRevenue) && isNil(revenue)) {
    revenueToLog = decodedRevenue
  // Received both "revenue" and "encodedRevenue"
  } else if (!isNil(revenue) && !isNil(decodedRevenue)) {
    if (isNil(aggregationOperation)) {
      throw new Error('Revenue logging requires an "aggregationOperation" value if both "revenue" and "encodedRevenue" values are provided')
    }
    revenueToLog = aggregateRevenues([revenue, decodedRevenue], aggregationOperation)
  // Received no valid revenue value
  } else {
    throw new Error('Revenue logging requires either "revenue" or "encodedRevenue" values')
  }

  try {
    await UserRevenueModel.create(userContext, {
      userId: userId,
      timestamp: moment.utc().toISOString(),
      revenue: revenueToLog,
      ...dfpAdvertiserId && { dfpAdvertiserId: dfpAdvertiserId }
    })
  } catch (e) {
    throw e
  }
  return { success: true }
}

export default logRevenue
<|endoftext|>


<commit_before>function tcp(){
  var _tcp = chrome.sockets.tcp;
  this.option = {},
  this.socketId = 0,

  this.create = function(callback){
    _tcp.create(this.option, function(socketInfo){
      this.socketId = socketInfo.socketId;
      callback();
    }.bind(this));
  }.bind(this),

  this.update = function(){
    _tcp.update(this.socketId, newSocketOption, callback);
  }.bind(this),

  this.pause = function(isPaused, callback){
    _tcp.setPaused(this.socketId, isPaused, callback);
  }.bind(this),

  this.keepAlive = function(enable, delay, callback){
    _tcp.setKeepAlive(this.socketId, enable, delay, function(code){
      if(code < 0){
        this.error(code);
      }else{
        callback();
      }
    }.bind(this));
  }.bind(this),

  this.disconnect = function(callback){
    _tcp.disconnect(this.socketId, callback);
  }.bind(this),

  this.close = function(callback){
    _tcp.close(this.socketId, callback);
  }.bind(this),

  this.error = function(code){
    console.log('An error occured with code ' + code);
  },

  this.init = function(callback){
    this.create(callback);
  }.bind(this)
}
<commit_msg>add connect send receive in tcp.js<commit_after>function tcp(){
  var _tcp = chrome.sockets.tcp;
  this.option = {},
  this.socketId = 0,

  this.create = function(callback){
    _tcp.create(this.option, function(socketInfo){
      this.socketId = socketInfo.socketId;
      callback();
    }.bind(this));
  }.bind(this),

  this.update = function(){
    _tcp.update(this.socketId, newSocketOption, callback);
  }.bind(this),

  this.pause = function(isPaused, callback){
    _tcp.setPaused(this.socketId, isPaused, callback);
  }.bind(this),

  this.keepAlive = function(enable, delay, callback){
    _tcp.setKeepAlive(this.socketId, enable, delay, function(code){
      if(code < 0){
        this.error(code);
      }else{
        callback();
      }
    }.bind(this));
  }.bind(this),

  this.disconnect = function(callback){
    _tcp.disconnect(this.socketId, callback);
  }.bind(this),

  this.close = function(callback){
    _tcp.close(this.socketId, callback);
  }.bind(this),

  this.error = function(code){
    console.log('An error occured with code ' + code);
  },

  this.init = function(callback){
    this.create(callback);
  }.bind(this),

  this.connect = function(address, port, callback){
    _tcp.connect(this.socketId, address, port, function(){
      _tcp.onReceive.addListener(function(info){
        if(info.socketId == this.socketId){
          this.receive(info);
        }
      }.bind(this));
      _tcp.onReceiveError.addListener(function(info){
        if(info.socketId == this.socketId){
          this.error(info.resultCode);
        }
      }.bind(this));
    }.bind(this));
  }.bind(this),

  this.send = function(data, callback){
    _tcp.send(this.socketId, data, callback);
  }.bind(this),

  this.receive = function(info){
    console.log('Received data.');
  }
}
<|endoftext|>


<commit_before>const fs = require('fs')
const tape = require('tape')
const Kernel = require('ewasm-kernel')
const KernelInterface = require('ewasm-kernel/interface.js')
const KernelEnvironment = require('ewasm-kernel/environment.js')
const ethUtil = require('ethereumjs-util')
const compiler = require('../index.js')
const dir = `${__dirname}/opcode`

let testFiles = fs.readdirSync(dir).filter((name) => name.endsWith('.json'))

tape('testing EVM1 Ops', (t) => {
  testFiles.forEach((path) => {
    let opTest = require(`${dir}/${path}`)
    opTest.forEach((test) => {
      const testEnvironment = new KernelEnvironment()
      const testInterface = new KernelInterface(testEnvironment)
      const testInstance = buildTest(test.op)

      t.comment(`testing ${test.description}`)

      // populate the stack with predefined values
      test.stack.in.reverse().forEach((item, index) => {
        item = hexToUint8Array(item)
        setMemory(testInstance, item, index * 32)
      })

      // populate the memory
      if (test.memory) {
        Object.keys(test.memory.in).forEach((offset) => {
          test.memory.in[offset].forEach((item, index) => {
            offset |= 0
            item = hexToUint8Array(item)
            setMemory(testInstance, item, offset + index * 32)
          })
        })
      }

      // Runs the opcode. An empty stack must start with the stack pointer at -8.
      // also we have to add 8 to the resulting sp to accommodate for the fact
      // that the sp is pointing to memory segment holding the last stack item
      let sp = testInstance.exports[test.op](test.stack.in.length * 32 - 8) + 8
      t.equal(sp / 32, test.stack.out.length, 'should have corrent number of items on the stack')
      sp = 0

      // compare the output stack against the predefined values
      test.stack.out.forEach((item, index) => {
        const expectedItem = hexToUint8Array(item)
        const result = getMemory(testInstance, sp, sp = sp + 32)
        t.equals(result.toString(), expectedItem.toString(), 'should have correct item on stack')
      })

      // check the memory
      if (test.memory) {
        Object.keys(test.memory.out).forEach((offset) => {
          test.memory.out[offset].forEach((item, index) => {
            offset |= 0
            const expectedItem = hexToUint8Array(item)
            const result = getMemory(testInstance, offset + index * 32, offset + index * 32 + expectedItem.length)
            t.equals(result.toString(), expectedItem.toString(), `should have the correct memory slot at ${offset}:${index}`)
          })
        })
      }

      // check for EVM return value
      if (test.return) {
        const expectedItem = hexToUint8Array(test.return)
        //const result = testEnvironment.returnValue
        const result = testInterface.env.returnValue
        t.equals(result.toString(), expectedItem.toString(), 'should have correct return value')
      }
    })
  })
  t.end()
})

function buildTest (op, interface) {
  const funcs = compiler.resolveFunctions(new Set([op]))
  const linked = compiler.buildModule(funcs, [], [op])
  const wasm = compiler.compileWAST(linked)
  return Kernel.codeHandler(wasm, interface)
}

function hexToUint8Array(item, length) {
  return new Uint8Array(ethUtil.setLength(new Buffer(item.slice(2), 'hex'), 32)).reverse()
}

function setMemory(instance, value, start) {
  new Uint8Array(instance.exports.memory).set(value, start)
}

function getMemory(instance, start, end) {
  return new Uint8Array(instance.exports.memory).slice(start, end)
}
<commit_msg>Include opcode in test comment for readability<commit_after>const fs = require('fs')
const tape = require('tape')
const Kernel = require('ewasm-kernel')
const KernelInterface = require('ewasm-kernel/interface.js')
const KernelEnvironment = require('ewasm-kernel/environment.js')
const ethUtil = require('ethereumjs-util')
const compiler = require('../index.js')
const dir = `${__dirname}/opcode`

let testFiles = fs.readdirSync(dir).filter((name) => name.endsWith('.json'))

tape('testing EVM1 Ops', (t) => {
  testFiles.forEach((path) => {
    let opTest = require(`${dir}/${path}`)
    opTest.forEach((test) => {
      const testEnvironment = new KernelEnvironment()
      const testInterface = new KernelInterface(testEnvironment)
      const testInstance = buildTest(test.op)

      // FIXME: have separate `t.test()` for better grouping
      t.comment(`testing ${test.op} ${test.description}`)

      // populate the stack with predefined values
      test.stack.in.reverse().forEach((item, index) => {
        item = hexToUint8Array(item)
        setMemory(testInstance, item, index * 32)
      })

      // populate the memory
      if (test.memory) {
        Object.keys(test.memory.in).forEach((offset) => {
          test.memory.in[offset].forEach((item, index) => {
            offset |= 0
            item = hexToUint8Array(item)
            setMemory(testInstance, item, offset + index * 32)
          })
        })
      }

      // Runs the opcode. An empty stack must start with the stack pointer at -8.
      // also we have to add 8 to the resulting sp to accommodate for the fact
      // that the sp is pointing to memory segment holding the last stack item
      let sp = testInstance.exports[test.op](test.stack.in.length * 32 - 8) + 8
      t.equal(sp / 32, test.stack.out.length, 'should have corrent number of items on the stack')
      sp = 0

      // compare the output stack against the predefined values
      test.stack.out.forEach((item, index) => {
        const expectedItem = hexToUint8Array(item)
        const result = getMemory(testInstance, sp, sp = sp + 32)
        t.equals(result.toString(), expectedItem.toString(), 'should have correct item on stack')
      })

      // check the memory
      if (test.memory) {
        Object.keys(test.memory.out).forEach((offset) => {
          test.memory.out[offset].forEach((item, index) => {
            offset |= 0
            const expectedItem = hexToUint8Array(item)
            const result = getMemory(testInstance, offset + index * 32, offset + index * 32 + expectedItem.length)
            t.equals(result.toString(), expectedItem.toString(), `should have the correct memory slot at ${offset}:${index}`)
          })
        })
      }

      // check for EVM return value
      if (test.return) {
        const expectedItem = hexToUint8Array(test.return)
        //const result = testEnvironment.returnValue
        const result = testInterface.env.returnValue
        t.equals(result.toString(), expectedItem.toString(), 'should have correct return value')
      }
    })
  })
  t.end()
})

function buildTest (op, interface) {
  const funcs = compiler.resolveFunctions(new Set([op]))
  const linked = compiler.buildModule(funcs, [], [op])
  const wasm = compiler.compileWAST(linked)
  return Kernel.codeHandler(wasm, interface)
}

function hexToUint8Array(item, length) {
  return new Uint8Array(ethUtil.setLength(new Buffer(item.slice(2), 'hex'), 32)).reverse()
}

function setMemory(instance, value, start) {
  new Uint8Array(instance.exports.memory).set(value, start)
}

function getMemory(instance, start, end) {
  return new Uint8Array(instance.exports.memory).slice(start, end)
}
<|endoftext|>


<commit_before>import axios from 'axios'
import moment from 'moment'
import fakedata from './messages'
// ------------------------------------
// Constants
// ------------------------------------
export const MARK_READ = 'MARK_READ'
export const MARK_UNREAD = 'MARK_UNREAD'
export const LOAD_EMAILS_SUCCESS = 'LOAD_EMAILS_SUCCESS'

const getApiUrl = (method) => {
  const proxy = 'http://cors-proxy.htmldriven.com/?url='
  const host = 'https://s3.us-east-2.amazonaws.com'
  const path = `twine-public/apis/twine-mail-${method}.json`
  return `${proxy}${host}/${path}`
}

const removeEmailsDatesInvalid = (emails) => {
  return emails.map((email) => {
    if (!moment(email.date).isValid()) {
      console.log(`ERROR: Malformed Data: Invalid Timestamp: Date = null: id:${email.id}`)
    }
    return email
  })
}

const removeEmailsAddressesInvalid = (emails) => {
  return emails.map((email) => {
    email.to = email.to.filter((address) => {
      if (!address.includes('@')) {
        console.log(`ERROR: Malformed Data: Email Address: Missing '@':
        id:${email.id}, address:${address}`)
      }
      return address.includes('@')
    })
    return email
  })
}

// ------------------------------------
// Actions
// ------------------------------------
export function loadEmailsSuccess (emails) {
  return {
    type: LOAD_EMAILS_SUCCESS,
    emails
  }
}

export function markRead (value = 1) {
  return function (dispatch, getState) {
    return axios.get(getApiUrl('put'), {})
      .then(function (response) {
        dispatch({
          type    : MARK_READ,
          payload : value
        })
      }).catch(function () {
        console.log(`ERROR: ${MARK_READ}`)
      })
  }
}

export function markUnread (value = 1) {
  return function (dispatch, getState) {
    return axios.get(getApiUrl('put'), {})
      .then(function (response) {
        dispatch({
          type    : MARK_UNREAD,
          payload : value
        })
      }).catch(function () {
        console.log(`ERROR: ${MARK_UNREAD}`)
      })
  }
}

export function loadEmails () {
  return function (dispatch) {
    return axios.get(getApiUrl('get'), {})
      .then(function (response) {
        dispatch(loadEmailsSuccess(JSON.parse(response.data.body).emails))
      }).catch(function () {
        console.log(`ERROR: ${LOAD_EMAILS_SUCCESS}`)
      })
  }
}

// ------------------------------------
// Action Handlers
// ------------------------------------
const ACTION_HANDLERS = {
  [LOAD_EMAILS_SUCCESS]  : (state, action) => {
    return removeEmailsAddressesInvalid(removeEmailsDatesInvalid(action.emails))
  },
  [MARK_READ]            : (state, action) => {
    return state.map((message) => {
      if (message.id === action.payload) {
        message.unread = false
      }
      return message
    })
  },
  [MARK_UNREAD]          : (state, action) => {
    return state.map((message) => {
      if (message.id === action.payload) {
        message.unread = true
      }
      return message
    })
  }
}

// ------------------------------------
// Reducer
// ------------------------------------

const initialState = [] || fakedata.emails
export default function emailReducer (state = initialState, action) {
  const handler = ACTION_HANDLERS[action.type]

  return handler ? handler(state, action) : state
}
<commit_msg>changes CORS proxy, original was return 502 error<commit_after>import axios from 'axios'
import moment from 'moment'
import fakedata from './messages'
// ------------------------------------
// Constants
// ------------------------------------
export const MARK_READ = 'MARK_READ'
export const MARK_UNREAD = 'MARK_UNREAD'
export const LOAD_EMAILS_SUCCESS = 'LOAD_EMAILS_SUCCESS'

const getApiUrl = (method) => {
  const proxy = 'https://cors-anywhere.herokuapp.com/'
  const host = 'https://s3.us-east-2.amazonaws.com'
  const path = `twine-public/apis/twine-mail-${method}.json`
  return `${proxy}${host}/${path}`
}

const removeEmailsDatesInvalid = (emails) => {
  return emails.map((email) => {
    if (!moment(email.date).isValid()) {
      console.log(`ERROR: Malformed Data: Invalid Timestamp: Date = null: id:${email.id}`)
    }
    return email
  })
}

const removeEmailsAddressesInvalid = (emails) => {
  return emails.map((email) => {
    email.to = email.to.filter((address) => {
      if (!address.includes('@')) {
        console.log(`ERROR: Malformed Data: Email Address: Missing '@':
        id:${email.id}, address:${address}`)
      }
      return address.includes('@')
    })
    return email
  })
}

// ------------------------------------
// Actions
// ------------------------------------
export function loadEmailsSuccess (emails) {
  return {
    type: LOAD_EMAILS_SUCCESS,
    emails
  }
}

export function markRead (value = 1) {
  return function (dispatch, getState) {
    return axios.get(getApiUrl('put'), {})
      .then(function (response) {
        dispatch({
          type    : MARK_READ,
          payload : value
        })
      }).catch(function () {
        console.log(`ERROR: ${MARK_READ}`)
      })
  }
}

export function markUnread (value = 1) {
  return function (dispatch, getState) {
    return axios.get(getApiUrl('put'), {})
      .then(function (response) {
        dispatch({
          type    : MARK_UNREAD,
          payload : value
        })
      }).catch(function () {
        console.log(`ERROR: ${MARK_UNREAD}`)
      })
  }
}

export function loadEmails () {
  return function (dispatch) {
    return axios.get(getApiUrl('get'), {})
      .then(function (response) {
        dispatch(loadEmailsSuccess(response.data.emails))
      }).catch(function () {
        console.log(`ERROR: ${LOAD_EMAILS_SUCCESS}`)
      })
  }
}

// ------------------------------------
// Action Handlers
// ------------------------------------
const ACTION_HANDLERS = {
  [LOAD_EMAILS_SUCCESS]  : (state, action) => {
    return removeEmailsAddressesInvalid(removeEmailsDatesInvalid(action.emails))
  },
  [MARK_READ]            : (state, action) => {
    return state.map((message) => {
      if (message.id === action.payload) {
        message.unread = false
      }
      return message
    })
  },
  [MARK_UNREAD]          : (state, action) => {
    return state.map((message) => {
      if (message.id === action.payload) {
        message.unread = true
      }
      return message
    })
  }
}

// ------------------------------------
// Reducer
// ------------------------------------

const initialState = [] || fakedata.emails
export default function emailReducer (state = initialState, action) {
  const handler = ACTION_HANDLERS[action.type]

  return handler ? handler(state, action) : state
}
<|endoftext|>


<commit_before>import RpmMeter, { PULSE_EVENT } from 'spin-bike-rpm-meter';
import { startTripClock } from '../utils/action-helpers';

export const START_TRIP = 'START_TRIP';
export const STOP_TRIP = 'STOP_TRIP';
export const GET_TRIP_DATA = 'GET_TRIP_DATA';
export const TICK_TRIP_CLOCK = 'TICK_TRIP_CLOCK';
export const STOP_MOVING = 'STOP_MOVING';

export function startTrip() {
	return function(dispatch, getState) {
		const rpmMeter = new RpmMeter();

		return rpmMeter.start()
			.then(stopFunction => {
				const tripClock = startTripClock(() => {
					dispatch(tickTripClockAction());
				});

				dispatch(startTripAction(rpmMeter, stopFunction, tripClock));

				rpmMeter.on(PULSE_EVENT, pulseData => {
					const state = getState();

					let { movingThresholdTimeout } = state.tripDashboard;

					clearTimeout(movingThresholdTimeout);

					movingThresholdTimeout = setTimeout(() => {
						dispatch(stopMovingAction());

					}, 2000);

					dispatch(getTripDataAction(pulseData, movingThresholdTimeout));
				});
			});
	}
}

export function stopTrip() {
	return function(dispatch, getState) {
		const state = getState();

		const { stopFunction, tripClock } = state.rpmMeter;

		stopFunction();
		tripClock.stop();
		dispatch(stopTripAction());
	}
}

function stopMovingAction() {
	return {
		type: STOP_MOVING,
		payload: {
			isMoving: false
		}
	}
}

function tickTripClockAction() {
	return {
		type: TICK_TRIP_CLOCK
	}
}

function getTripDataAction(pulseData, movingThresholdTimeout) {
	return {
		type: GET_TRIP_DATA,
		payload: {
			pulseData,
			movingThresholdTimeout
		}
	};
}

function startTripAction(rpmMeter, stopFunction, tripClock) {
	return {
		type: START_TRIP,
		payload: {
			rpmMeter,
			stopFunction,
			tripClock
		}
	};
}

function stopTripAction() {
	return {
		type: STOP_TRIP
	}
}<commit_msg>Add short documentation for the startTrip fn<commit_after>import RpmMeter, { PULSE_EVENT } from 'spin-bike-rpm-meter';
import { startTripClock } from '../utils/action-helpers';

export const START_TRIP = 'START_TRIP';
export const STOP_TRIP = 'STOP_TRIP';
export const GET_TRIP_DATA = 'GET_TRIP_DATA';
export const TICK_TRIP_CLOCK = 'TICK_TRIP_CLOCK';
export const STOP_MOVING = 'STOP_MOVING';

export function startTrip() {
	return function(dispatch, getState) {
		const rpmMeter = new RpmMeter();

		return rpmMeter.start()
			.then(stopFunction => {

				// Start trip clock for calculating total elapsed time.
				const tripClock = startTripClock(() => {

					// 1. Dispatch tick action each second (clock interval is 1s).
					dispatch(tickTripClockAction());
				});

				// 2. Dispatch action that trip was started.
				dispatch(startTripAction(rpmMeter, stopFunction, tripClock));

				// Listen to RPMMeter "pulses" (revolutions of pedals on spin bike).
				rpmMeter.on(PULSE_EVENT, pulseData => {
					const state = getState();

					let { movingThresholdTimeout } = state.tripDashboard;

					clearTimeout(movingThresholdTimeout);

					movingThresholdTimeout = setTimeout(() => {

						// 3. Dispatch a stop moving action if cyclist hasn't made a revolution of pedals on his/her
						// bike in the last 2 seconds.
						dispatch(stopMovingAction());
					}, 2000);

					// 4. Dispatch action that a full revolution of pedals on stationary bike was made.
					// Include secondsBetweenPulses (= revolutions) in pulseData object.
					dispatch(getTripDataAction(pulseData, movingThresholdTimeout));
				});
			});
	}
}

export function stopTrip() {
	return function(dispatch, getState) {
		const state = getState();

		const { stopFunction, tripClock } = state.rpmMeter;

		stopFunction();
		tripClock.stop();
		dispatch(stopTripAction());
	}
}

function stopMovingAction() {
	return {
		type: STOP_MOVING,
		payload: {
			isMoving: false
		}
	}
}

function tickTripClockAction() {
	return {
		type: TICK_TRIP_CLOCK
	}
}

function getTripDataAction(pulseData, movingThresholdTimeout) {
	return {
		type: GET_TRIP_DATA,
		payload: {
			pulseData,
			movingThresholdTimeout
		}
	};
}

function startTripAction(rpmMeter, stopFunction, tripClock) {
	return {
		type: START_TRIP,
		payload: {
			rpmMeter,
			stopFunction,
			tripClock
		}
	};
}

function stopTripAction() {
	return {
		type: STOP_TRIP
	}
}<|endoftext|>


<commit_before>"use strict";

var sprintf  = require('../string/sprintf');
var repeat   = require('mout/string/repeat');
var truncate = require('nyks/string/truncate');

var LF = "\n";
var COLS       = 76;
var STR_PAD_LEFT  = "left";
var STR_PAD_RIGHT = "right";
var STR_PAD_BOTH  = "both";

var pad = function(str, pad, mode, mask, pad_len){
  if(!pad_len) pad_len = COLS;
  if(!mask)    mask    = '%s';
  if(!pad)     pad     = '─';
  if(!mode)    mode    = STR_PAD_BOTH;

  pad_len -= sprintf(mask, str).length;

  var left = (mode == STR_PAD_BOTH ) ? Math.floor(pad_len/2) : 0;

  return sprintf(mask,
      repeat(pad, Math.max(left, 0)) + str + repeat(pad, Math.max(pad_len - left, 0)));
}

var output = function(line){
  process.stderr.write(line);
}

var box = function(/*[title, msg]*/){
  var args = [].slice.call(arguments);

  var dotrim = true, pad_len = COLS;

  for(var msg, a=1 ; a < args.length ; a+=2) {
    msg = args[a];
    if(typeof msg !== "string")
        msg = JSON.stringify(msg, null, 2);

    msg = msg.trim().replace("	", "    ").split(/\r?\n/);//use 4 tab indent

    msg.forEach(function(tmp_line, i) {
      if(dotrim)
        msg[i] = truncate(tmp_line,  COLS);
      //msg[i] = preg_replace('#&[^;]*?#m','…',);
      pad_len = Math.max(pad_len, msg[i].length + 2); //2 chars enclosure
    })
    args[a] = msg;
  }

  for(var a=0; a < args.length; a+=2) {
    output( pad(" "+args[a]+" ", "═", STR_PAD_BOTH, a ? "╠%s╣":"╔%s╗", pad_len) + LF );
    args[a+1].forEach(function(line) {
      output( pad(line, " ", STR_PAD_RIGHT, "║%s║", pad_len) + LF);
    });
  }
  output( pad('', "═", STR_PAD_BOTH, "╚%s╝", pad_len) + LF );
}

module.exports = box;
<commit_msg>Prevent cli crash<commit_after>"use strict";

var sprintf  = require('../string/sprintf');
var repeat   = require('mout/string/repeat');
var truncate = require('nyks/string/truncate');

var LF = "\n";
var COLS       = 76;
var STR_PAD_LEFT  = "left";
var STR_PAD_RIGHT = "right";
var STR_PAD_BOTH  = "both";

var pad = function(str, pad, mode, mask, pad_len){
  if(!pad_len) pad_len = COLS;
  if(!mask)    mask    = '%s';
  if(!pad)     pad     = '─';
  if(!mode)    mode    = STR_PAD_BOTH;

  pad_len -= sprintf(mask, str).length;

  var left = (mode == STR_PAD_BOTH ) ? Math.floor(pad_len/2) : 0;

  return sprintf(mask,
      repeat(pad, Math.max(left, 0)) + str + repeat(pad, Math.max(pad_len - left, 0)));
}

var output = function(line){
  process.stderr.write(line);
}

var box = function(/*[title, msg]*/){
  var args = [].slice.call(arguments);
  if(!args.length)
    return;

  var dotrim = true, pad_len = COLS;

  for(var msg, a=1 ; a < args.length ; a+=2) {
    msg = args[a];
    if(typeof msg !== "string")
        msg = JSON.stringify(msg, null, 2);

    msg = msg.trim().replace("	", "    ").split(/\r?\n/);//use 4 tab indent

    msg.forEach(function(tmp_line, i) {
      if(dotrim)
        msg[i] = truncate(tmp_line,  COLS);
      //msg[i] = preg_replace('#&[^;]*?#m','…',);
      pad_len = Math.max(pad_len, msg[i].length + 2); //2 chars enclosure
    })
    args[a] = msg;
  }

  for(var a=0; a < args.length; a+=2) {
    output( pad(" "+args[a]+" ", "═", STR_PAD_BOTH, a ? "╠%s╣":"╔%s╗", pad_len) + LF );
    args[a+1].forEach(function(line) {
      output( pad(line, " ", STR_PAD_RIGHT, "║%s║", pad_len) + LF);
    });
  }
  output( pad('', "═", STR_PAD_BOTH, "╚%s╝", pad_len) + LF );
}

module.exports = box;
<|endoftext|>


<commit_before>'use strict'

/* IMPORT */

const {packages, workspace} = global.atom
const {registerSpawnCommand} = require(packages.resolvePackagePath('quick-spawn'))

/* DO THINGS */

// NOTE:
//  → Not every function-calls below is necessary
//  → Keep only what you need
registerWorkspaceBash()
registerSidebarBash()

/* DEFINE HOW TO DO THINGS */

// DESCRIPTION: registerWorkspaceBash
//  → Distinct:
//   * Registers for workspace
//   * Uses getTabDirectory() as first current-working-directory
//  → Returns 1 subscription
function registerWorkspaceBash () {
  return registerSpawnCommand({
    execCmd: 'bash', // required; program needs to be executed
    atomCmd: 'simple-bash:workspace', // required; registers an atom-command, appears as "Simple Bash: Workspace" in Command-Palette
    atomTarget: 'atom-workspace', // required; determines where to attach the atom-command
    workingDirectory: getTabDirectory, // optional; determines Bash's first working-directory, you can change it later by 'cd new-wdir'
    attached: false, // optional, default to false; determines whether Bash process should be attached to Atom process
    suspended: true, // optional, default to true; if false, Bash would be started right after quick-spawn's activation
    viewStdIO: ['stdin', 'stdout', 'stderr'], // optional, determines whether stdin, stdout and stderr should be shown; shows all three by default
    type: 'tab', // optional, default to 'tab'
    atomKeybinding: 'ctrl-shift-b s w', // optional
    atomMenuBar: ['Packages', 'Quick Spawn', 'Simple Bash'], // optional
    atomContextMenu: { // optional
      target: 'atom-workspace', // required
      path: ['Quick Spawn', 'Simple Bash'], // required
    },
    exitOnClose: true, // optional, default to true; should the spawned process be terminated when close the view
    closeOnExit: true, // optional, default to false; should the view be closed when bash exits
    detachedTextBox: 'none', // optional, default to 'none'
    __proto__: null // optional (of course); avoid the mess from Object.prototype
  })
}

// DESCRIPTION: registerSidebarBash
//  → Distinct:
//   * Registers for directory tree-view
//   * Uses getItemDirectory() as first current-working-directory
//  → Returns 1 subscription
function registerSidebarBash () {
  return registerSpawnCommand({})
}

// DESCRIPTION: getTabDirectory
function getTabDirectory () {
  return workspace.getActivePaneItem().getDirectoryPath()
}
<commit_msg>Correct function name<commit_after>'use strict'

/* IMPORT */

const {packages, workspace} = global.atom
const {registerSingleSubscription} = require(packages.resolvePackagePath('quick-spawn'))

/* DO THINGS */

// NOTE:
//  → Not every function-calls below is necessary
//  → Keep only what you need
registerWorkspaceBash()
registerSidebarBash()

/* DEFINE HOW TO DO THINGS */

// DESCRIPTION: registerWorkspaceBash
//  → Distinct:
//   * Registers for workspace
//   * Uses getTabDirectory() as first current-working-directory
//  → Returns 1 subscription
function registerWorkspaceBash () {
  return registerSingleSubscription({
    execCmd: 'bash', // required; program needs to be executed
    atomCmd: 'simple-bash:workspace', // required; registers an atom-command, appears as "Simple Bash: Workspace" in Command-Palette
    atomTarget: 'atom-workspace', // required; determines where to attach the atom-command
    workingDirectory: getTabDirectory, // optional; determines Bash's first working-directory, you can change it later by 'cd new-wdir'
    attached: false, // optional, default to false; determines whether Bash process should be attached to Atom process
    suspended: true, // optional, default to true; if false, Bash would be started right after quick-spawn's activation
    viewStdIO: ['stdin', 'stdout', 'stderr'], // optional, determines whether stdin, stdout and stderr should be shown; shows all three by default
    type: 'tab', // optional, default to 'tab'
    atomKeybinding: 'ctrl-shift-b s w', // optional
    atomMenuBar: ['Packages', 'Quick Spawn', 'Simple Bash'], // optional
    atomContextMenu: { // optional
      target: 'atom-workspace', // required
      path: ['Quick Spawn', 'Simple Bash'], // required
    },
    exitOnClose: true, // optional, default to true; should the spawned process be terminated when close the view
    closeOnExit: true, // optional, default to false; should the view be closed when bash exits
    detachedTextBox: 'none', // optional, default to 'none'
    __proto__: null // optional (of course); avoid the mess from Object.prototype
  })
}

// DESCRIPTION: registerSidebarBash
//  → Distinct:
//   * Registers for directory tree-view
//   * Uses getItemDirectory() as first current-working-directory
//  → Returns 1 subscription
function registerSidebarBash () {
  return registerSingleSubscription({})
}

// DESCRIPTION: getTabDirectory
function getTabDirectory () {
  return workspace.getActivePaneItem().getDirectoryPath()
}
<|endoftext|>


<commit_before>/* Meltdown viewer and editor integration for annotatorjs.
   Based in part on annotator.ui.markdown.

   Include annotator.meltdown.css to adjust for annotator/meltdown
   style interactions.
*/

var _t = annotator.util.gettext;

var annotatormeltdown = {
    /**
    * use js-xss to filter text (disable javascript, etc)
    */
    filterXSS: function(text) {

        return filterXSS(text, {
            whiteList: {
              audio:  ['controls', 'loop', 'preload', 'src'],  // disable autoplay
              'source': ['src', 'type']  // allow source tag, used in audio
            }
        });
    },

    converter: new showdown.Converter({extensions: ['footnotes']}),
    parser: function(text) {
        // convert markdown into html
        var result = annotatormeltdown.converter.makeHtml(annotatormeltdown.filterXSS(text));
        return result;
    },

    /**
     * Replacement viewer render method.
     * Returns annotation text content parsed as Markdown.
     */
    render: function (annotation) {
        if (annotation.text) {
            return annotatormeltdown.parser(annotation.text);
        } else {
            return "" + _t('No comment') + "";
        }
    },

    /**
     * function:: viewerExtension(viewer)
     *
     * An extension for the :class:`~annotator.ui.viewer.Viewer`. Allows the
     * viewer to interpret annotation text as `Markdown`_ and uses the
     * `showdown`_ library if present in the page to render annotations
     * with Markdown text as HTML.
     *
     * .. _Markdown: https://daringfireball.net/projects/markdown/
     * .. _showdown: https://github.com/showdownjs/showdown
     *
     * **Usage**::
     *
     *     app.include(annotator.ui.main, {
     *         viewerExtensions: [annotatormeltdown.viewerExtension]
     *     });
     */
    viewerExtension: function viewerExtension(viewer) {
        if (!window.showdown || typeof window.showdown !== 'object') {
            console.warn(_t("To use the Meltdown viewer extension, you must " +
                "include showdown in the page."));
            return;
        }
        // only set the renderer when showdown is available
        viewer.setRenderer(annotatormeltdown.render);
    },

    // Editor textarea keyboard shortcuts.
    // Revise default annotator shortcut to map shift+enter to save
    // instead of just enter.
    textarea_keydown: function (event) {
        if (event.which === 27) {
            // "Escape" key => abort.
            this.cancel();
        } else if (event.which === 13 && event.shiftKey) {
            // If "return" was pressed *with*the shift key, we're done.
            this.submit();
        }
    },

    customize_meltdown: function() {
        // add an html5 audio input to the 'kitchen sink' control menu
        $.meltdown.defaults.controls[9].push('audio');
        $.meltdown.controlDefs.audio = {
            label: "Audio",
            altText: "Audio",
            before: "\n<audio controls='controls'>\n<source src='",
            placeholder: "http://",
            after: "' type='audio/mpeg'/>\n</audio>"
        };
        // modify codeblock style to match showdown
        $.meltdown.controlDefs.codeblock['before'] = "```\n";
        $.meltdown.controlDefs.codeblock['after'] = "\n```";

    },

    // Extend Editor show method to initialize meltdown and set minimum
    // width the first time the editor window is shown.
    show: function(position) {
        // use unextended method to handle normal show functionality
        this._pre_meltdown_show(position);
        var textarea = $(this.element).find("textarea").first();

        // enable meltdown on the textarea and set a min-width
        if (! this.meltdown_initialized) {
            annotatormeltdown.customize_meltdown();
            textarea.first().meltdown({
                previewCollapses: false,
                openPreview: true,
                parser: annotatormeltdown.parser,
            });

            if (this.meltdown_options.min_width) {
                this.element.find('.annotator-widget')
                    .css('min-width', this.meltdown_options.min_width);
            }
            // if font awesome is configured available, add class for css to pick up on
            if (this.meltdown_options.font_awesome) {
                this.element.find('.meltdown_controls').addClass('fa-avail');
            }
            /* annotator purposely sets the editor at a higher z-index
            than anything else on the page; make sure controls drop-down
            menu is still higher so it isn't obscured by annotator buttons */
            $('.meltdown_controlgroup-dropdown').css('z-index', parseInt($('.annotator-editor').css('z-index')) + 1);

            this.meltdown_initialized = true;
        } else {
            // make sure preview area is updated for current text
            textarea.first().meltdown("update");
        }
        // always ensure textarea has focus for immediate input
        textarea.first().focus();
    },

    submit: function () {
        // run xss filter on textarea *before* saving, to remove
        // unwanted tags and attributes from the stored attribute
        var textarea= $(this.element).find("textarea").first();
        textarea.val(annotatormeltdown.filterXSS(textarea.val()));
        return this._pre_meltdown_submit();
    },

    /**
     * function:: getEditorExtension(options)
     *
     * Build and return an extension for :class:`~annotator.ui.editor.Editor`.
     * Converts the editor textarea to a `Meltdown`_ input, with preview
     * panel and toolbar
     *
     * .. _Meltdown: https://github.com/iphands/Meltdown
     *
     * **Usage**::
     *
     *     app.include(annotator.ui.main, {
     *         editorExtensions: [annotatormeltdown.getEditorExtension()]
     *     });
     *
     * You can optionally specify a minimum editor window width (by default,
     * minimum width will be set to 375px):
     *
     *     app.include(annotator.ui.main, {
     *         editorExtensions: [annotatormeltdown.getEditorExtension({min_width: '500px'})]
     *     });
     *
     * You can also indicate when FontAwesome is available and should be used
     * for the audio button in the controls by specifying `font_awesome: true`
     * in the options to getEditorExtension.
     *
     */
    getEditorExtension: function getEditorExtension(options) {

        var meltdown_opts = {
            min_width: '375px',  // default minimum width
            font_awesome: false,  // set to true when font awesome is available
        };
        $.extend(meltdown_opts, options);
        return function editorExtension(editor) {
            // Make sure meltdown is available before configuring anything
            if (!jQuery.meltdown || typeof jQuery.meltdown !== 'object') {
                console.warn(_t("To use the Meltdown editor extension, you must " +
                    "include meltdown in the page."));
                return;
            }
            // override editor methods and add options
            editor._onTextareaKeydown = annotatormeltdown.textarea_keydown;
            editor._pre_meltdown_show = editor.show; // preserve unextended show method
            editor.show = annotatormeltdown.show;
            // track meltdown initialization since it only needs to be done once
            editor.meltdown_initialized = false;
            editor.meltdown_options = meltdown_opts;
            editor._pre_meltdown_submit = editor.submit; // preserve unextended submit method
            editor.submit = annotatormeltdown.submit;

        };
    },
};
<commit_msg>Add newlines to audio template to ensure it is processed as block html<commit_after>/* Meltdown viewer and editor integration for annotatorjs.
   Based in part on annotator.ui.markdown.

   Include annotator.meltdown.css to adjust for annotator/meltdown
   style interactions.
*/

var _t = annotator.util.gettext;

var annotatormeltdown = {
    /**
    * use js-xss to filter text (disable javascript, etc)
    */
    filterXSS: function(text) {

        return filterXSS(text, {
            whiteList: {
              audio:  ['controls', 'loop', 'preload', 'src'],  // disable autoplay
              'source': ['src', 'type']  // allow source tag, used in audio
            }
        });
    },

    converter: new showdown.Converter({extensions: ['footnotes']}),
    parser: function(text) {
        // convert markdown into html
        var result = annotatormeltdown.converter.makeHtml(annotatormeltdown.filterXSS(text));
        return result;
    },

    /**
     * Replacement viewer render method.
     * Returns annotation text content parsed as Markdown.
     */
    render: function (annotation) {
        if (annotation.text) {
            return annotatormeltdown.parser(annotation.text);
        } else {
            return "" + _t('No comment') + "";
        }
    },

    /**
     * function:: viewerExtension(viewer)
     *
     * An extension for the :class:`~annotator.ui.viewer.Viewer`. Allows the
     * viewer to interpret annotation text as `Markdown`_ and uses the
     * `showdown`_ library if present in the page to render annotations
     * with Markdown text as HTML.
     *
     * .. _Markdown: https://daringfireball.net/projects/markdown/
     * .. _showdown: https://github.com/showdownjs/showdown
     *
     * **Usage**::
     *
     *     app.include(annotator.ui.main, {
     *         viewerExtensions: [annotatormeltdown.viewerExtension]
     *     });
     */
    viewerExtension: function viewerExtension(viewer) {
        if (!window.showdown || typeof window.showdown !== 'object') {
            console.warn(_t("To use the Meltdown viewer extension, you must " +
                "include showdown in the page."));
            return;
        }
        // only set the renderer when showdown is available
        viewer.setRenderer(annotatormeltdown.render);
    },

    // Editor textarea keyboard shortcuts.
    // Revise default annotator shortcut to map shift+enter to save
    // instead of just enter.
    textarea_keydown: function (event) {
        if (event.which === 27) {
            // "Escape" key => abort.
            this.cancel();
        } else if (event.which === 13 && event.shiftKey) {
            // If "return" was pressed *with*the shift key, we're done.
            this.submit();
        }
    },

    customize_meltdown: function() {
        // add an html5 audio input to the 'kitchen sink' control menu
        $.meltdown.defaults.controls[9].push('audio');
        $.meltdown.controlDefs.audio = {
            label: "Audio",
            altText: "Audio",
            before: "\n\n<audio controls='controls'>\n<source src='",
            placeholder: "http://",
            after: "' type='audio/mpeg'/>\n</audio>\n\n"
        };
        // modify codeblock style to match showdown
        $.meltdown.controlDefs.codeblock['before'] = "```\n";
        $.meltdown.controlDefs.codeblock['after'] = "\n```";

    },

    // Extend Editor show method to initialize meltdown and set minimum
    // width the first time the editor window is shown.
    show: function(position) {
        // use unextended method to handle normal show functionality
        this._pre_meltdown_show(position);
        var textarea = $(this.element).find("textarea").first();

        // enable meltdown on the textarea and set a min-width
        if (! this.meltdown_initialized) {
            annotatormeltdown.customize_meltdown();
            textarea.first().meltdown({
                previewCollapses: false,
                openPreview: true,
                parser: annotatormeltdown.parser,
            });

            if (this.meltdown_options.min_width) {
                this.element.find('.annotator-widget')
                    .css('min-width', this.meltdown_options.min_width);
            }
            // if font awesome is configured available, add class for css to pick up on
            if (this.meltdown_options.font_awesome) {
                this.element.find('.meltdown_controls').addClass('fa-avail');
            }
            /* annotator purposely sets the editor at a higher z-index
            than anything else on the page; make sure controls drop-down
            menu is still higher so it isn't obscured by annotator buttons */
            $('.meltdown_controlgroup-dropdown').css('z-index', parseInt($('.annotator-editor').css('z-index')) + 1);

            this.meltdown_initialized = true;
        } else {
            // make sure preview area is updated for current text
            textarea.first().meltdown("update");
        }
        // always ensure textarea has focus for immediate input
        textarea.first().focus();
    },

    submit: function () {
        // run xss filter on textarea *before* saving, to remove
        // unwanted tags and attributes from the stored attribute
        var textarea= $(this.element).find("textarea").first();
        textarea.val(annotatormeltdown.filterXSS(textarea.val()));
        return this._pre_meltdown_submit();
    },

    /**
     * function:: getEditorExtension(options)
     *
     * Build and return an extension for :class:`~annotator.ui.editor.Editor`.
     * Converts the editor textarea to a `Meltdown`_ input, with preview
     * panel and toolbar
     *
     * .. _Meltdown: https://github.com/iphands/Meltdown
     *
     * **Usage**::
     *
     *     app.include(annotator.ui.main, {
     *         editorExtensions: [annotatormeltdown.getEditorExtension()]
     *     });
     *
     * You can optionally specify a minimum editor window width (by default,
     * minimum width will be set to 375px):
     *
     *     app.include(annotator.ui.main, {
     *         editorExtensions: [annotatormeltdown.getEditorExtension({min_width: '500px'})]
     *     });
     *
     * You can also indicate when FontAwesome is available and should be used
     * for the audio button in the controls by specifying `font_awesome: true`
     * in the options to getEditorExtension.
     *
     */
    getEditorExtension: function getEditorExtension(options) {

        var meltdown_opts = {
            min_width: '375px',  // default minimum width
            font_awesome: false,  // set to true when font awesome is available
        };
        $.extend(meltdown_opts, options);
        return function editorExtension(editor) {
            // Make sure meltdown is available before configuring anything
            if (!jQuery.meltdown || typeof jQuery.meltdown !== 'object') {
                console.warn(_t("To use the Meltdown editor extension, you must " +
                    "include meltdown in the page."));
                return;
            }
            // override editor methods and add options
            editor._onTextareaKeydown = annotatormeltdown.textarea_keydown;
            editor._pre_meltdown_show = editor.show; // preserve unextended show method
            editor.show = annotatormeltdown.show;
            // track meltdown initialization since it only needs to be done once
            editor.meltdown_initialized = false;
            editor.meltdown_options = meltdown_opts;
            editor._pre_meltdown_submit = editor.submit; // preserve unextended submit method
            editor.submit = annotatormeltdown.submit;

        };
    },
};
<|endoftext|>