<commit_before>import React from 'react';
import TextField from '@material-ui/core/TextField';
import { CirclePicker } from 'react-color';
import { mediaStatuses } from '../../customHelpers';
import UploadImage from '../UploadImage';

class MemeEditor extends React.Component {
  constructor(props) {
    super(props);
    this.state = {};
  }

  handleChange = (e) => {
    if (this.props.onParamChange) {
      const param = {};
      param[e.target.name] = e.target.value;
      this.props.onParamChange(param);
    }
  };

  handleColorSelect = (color) => {
    if (this.props.onParamChange) {
      this.props.onParamChange({ overlayColor: color.hex });
    }
  };

  handleImage = (image) => {
    if (this.props.onParamChange) {
      const param = { image };
      this.props.onParamChange(param);
    }
  };

  handleClearImage = () => {
    this.handleImage(null);
  };

  render() {
    const colors = mediaStatuses(this.props.media).statuses.map(s => s.style.color);

    return (

        <UploadImage onImage={this.handleImage} onClear={this.handleClearImage} />
        <TextField
          name="headline"
          label="Headline"
          onChange={this.handleChange}
          value={this.props.params.headline}
          margin="normal"
          fullWidth
        />
        <TextField
          name="description"
          label="Description"
          onChange={this.handleChange}
          value={this.props.params.description}
          margin="normal"
          fullWidth
          multiline
        />
        <TextField
          name="statusText"
          label="Status Text"
          onChange={this.handleChange}
          value={this.props.params.statusText}
          margin="normal"
        />
        <TextField
          name="overlayColor"
          label="Overlay color"
          onChange={this.handleChange}
          value={this.props.params.overlayColor}
          margin="normal"
        />
        <CirclePicker onChangeComplete={this.handleColorSelect} colors={colors} />

    );
  }
}

export default MemeEditor;
<commit_msg>#7262 Add margin to color picker<commit_after>import React from 'react';
import TextField from '@material-ui/core/TextField';
import { CirclePicker } from 'react-color';
import UploadImage from '../UploadImage';
import { mediaStatuses } from '../../customHelpers';
import { units } from '../../styles/js/shared';

class MemeEditor extends React.Component {
  constructor(props) {
    super(props);
    this.state = {};
  }

  handleChange = (e) => {
    if (this.props.onParamChange) {
      const param = {};
      param[e.target.name] = e.target.value;
      this.props.onParamChange(param);
    }
  };

  handleColorSelect = (color) => {
    if (this.props.onParamChange) {
      this.props.onParamChange({ overlayColor: color.hex });
    }
  };

  handleImage = (image) => {
    if (this.props.onParamChange) {
      const param = { image };
      this.props.onParamChange(param);
    }
  };

  handleClearImage = () => {
    this.handleImage(null);
  };

  render() {
    const colors = mediaStatuses(this.props.media).statuses.map(s => s.style.color);

    return (

        <UploadImage onImage={this.handleImage} onClear={this.handleClearImage} />
        <TextField
          name="headline"
          label="Headline"
          onChange={this.handleChange}
          value={this.props.params.headline}
          margin="normal"
          fullWidth
        />
        <TextField
          name="description"
          label="Description"
          onChange={this.handleChange}
          value={this.props.params.description}
          margin="normal"
          fullWidth
          multiline
        />
        <TextField
          name="statusText"
          label="Status Text"
          onChange={this.handleChange}
          value={this.props.params.statusText}
          margin="normal"
        />
        <TextField
          name="overlayColor"
          label="Overlay color"
          onChange={this.handleChange}
          value={this.props.params.overlayColor}
          margin="normal"
        />

          <CirclePicker onChangeComplete={this.handleColorSelect} colors={colors} />

    );
  }
}

export default MemeEditor;
<|endoftext|>


<commit_before>import { InterpolateDiscrete } from '../../constants.js';
import { KeyframeTrack } from '../KeyframeTrack.js';

/**
 *
 * A Track of Boolean keyframe values.
 *
 *
 * @author Ben Houston / http://clara.io/
 * @author David Sarno / http://lighthaus.us/
 * @author tschw
 */

function BooleanKeyframeTrack( name, times, values ) {

	KeyframeTrack.call( this, name, times, values );

}

BooleanKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), {

	constructor: BooleanKeyframeTrack,

	ValueTypeName: 'bool',
	ValueBufferType: Array,

	DefaultInterpolation: InterpolateDiscrete,

	InterpolantFactoryMethodLinear: undefined,
	InterpolantFactoryMethodSmooth: undefined

	// Note: Actually this track could have a optimized / compressed
	// representation of a single value and a custom interpolant that
	// computes "firstValue ^ isOdd( index )".

} );

export { BooleanKeyframeTrack };
<commit_msg>BooleanKeyframeTrack style<commit_after>import { InterpolateDiscrete } from '../../constants.js';
import { KeyframeTrack } from '../KeyframeTrack.js';

/**
 *
 * A Track of Boolean keyframe values.
 *
 *
 * @author Ben Houston / http://clara.io/
 * @author David Sarno / http://lighthaus.us/
 * @author tschw
 */

function BooleanKeyframeTrack( name, times, values ) {

	KeyframeTrack.call( this, name, times, values );

}

BooleanKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), {

	constructor: BooleanKeyframeTrack,

	ValueTypeName: 'bool',
	ValueBufferType: Array,

	DefaultInterpolation: InterpolateDiscrete,

	InterpolantFactoryMethodLinear: undefined,
	InterpolantFactoryMethodSmooth: undefined

	// Note: Actually this track could have a optimized / compressed
	// representation of a single value and a custom interpolant that
	// computes "firstValue ^ isOdd( index )".

} );

export { BooleanKeyframeTrack };
<|endoftext|>


<commit_before>'use strict';

var app = require('../..');
import request from 'supertest';

var newMasters;

describe('Masters API:', function() {

  describe('GET /api/masters', function() {
    var masterss;

    beforeEach(function(done) {
      request(app)
        .get('/api/masters')
        .expect(200)
        .expect('Content-Type', /json/)
        .end((err, res) => {
          if (err) {
            return done(err);
          }
          masterss = res.body;
          done();
        });
    });

    it('should respond with JSON array', function() {
      masterss.should.be.instanceOf(Array);
    });

  });

  describe('POST /api/masters', function() {
    beforeEach(function(done) {
      request(app)
        .post('/api/masters')
        .send({
          name: 'New Masters',
          info: 'This is the brand new masters!!!'
        })
        .expect(201)
        .expect('Content-Type', /json/)
        .end((err, res) => {
          if (err) {
            return done(err);
          }
          newMasters = res.body;
          done();
        });
    });

    it('should respond with the newly created masters', function() {
      newMasters.name.should.equal('New Masters');
      newMasters.info.should.equal('This is the brand new masters!!!');
    });

  });

  describe('GET /api/masters/:id', function() {
    var masters;

    beforeEach(function(done) {
      request(app)
        .get('/api/masters/' + newMasters._id)
        .expect(200)
        .expect('Content-Type', /json/)
        .end((err, res) => {
          if (err) {
            return done(err);
          }
          masters = res.body;
          done();
        });
    });

    afterEach(function() {
      masters = {};
    });

    it('should respond with the requested masters', function() {
      masters.name.should.equal('New Masters');
      masters.info.should.equal('This is the brand new masters!!!');
    });

  });

  describe('PUT /api/masters/:id', function() {
    var updatedMasters;

    beforeEach(function(done) {
      request(app)
        .put('/api/masters/' + newMasters._id)
        .send({
          name: 'Updated Masters',
          info: 'This is the updated masters!!!'
        })
        .expect(200)
        .expect('Content-Type', /json/)
        .end(function(err, res) {
          if (err) {
            return done(err);
          }
          updatedMasters = res.body;
          done();
        });
    });

    afterEach(function() {
      updatedMasters = {};
    });

    it('should respond with the updated masters', function() {
      updatedMasters.name.should.equal('Updated Masters');
      updatedMasters.info.should.equal('This is the updated masters!!!');
    });

  });

  describe('DELETE /api/masters/:id', function() {

    it('should respond with 204 on successful removal', function(done) {
      request(app)
        .delete('/api/masters/' + newMasters._id)
        .expect(204)
        .end((err, res) => {
          if (err) {
            return done(err);
          }
          done();
        });
    });

    it('should respond with 404 when masters does not exist', function(done) {
      request(app)
        .delete('/api/masters/' + newMasters._id)
        .expect(404)
        .end((err, res) => {
          if (err) {
            return done(err);
          }
          done();
        });
    });

  });

});
<commit_msg>Updates to synch with actual Masters data model<commit_after>'use strict';

var app = require('../..');
import request from 'supertest';

var newMasters;

describe('Masters API:', function() {

  describe('GET /api/masters', function() {
    var masterss;

    beforeEach(function(done) {
      request(app)
        .get('/api/masters')
        .expect(200)
        .expect('Content-Type', /json/)
        .end((err, res) => {
          if (err) {
            return done(err);
          }
          masterss = res.body;
          done();
        });
    });

    it('should respond with JSON array', function() {
      masterss.should.be.instanceOf(Array);
    });

  });

  describe('POST /api/masters', function() {
    beforeEach(function(done) {
      request(app)
        .post('/api/masters')
        .send({
          name: 'New Masters',
        })
        .expect(201)
        .expect('Content-Type', /json/)
        .end((err, res) => {
          if (err) {
            return done(err);
          }
          newMasters = res.body;
          done();
        });
    });

    it('should respond with the newly created masters', function() {
      newMasters.name.should.equal('New Masters');
    });

  });

  describe('GET /api/masters/:id', function() {
    var masters;

    beforeEach(function(done) {
      request(app)
        .get('/api/masters/' + newMasters._id)
        .expect(200)
        .expect('Content-Type', /json/)
        .end((err, res) => {
          if (err) {
            return done(err);
          }
          masters = res.body;
          done();
        });
    });

    afterEach(function() {
      masters = {};
    });

    it('should respond with the requested masters', function() {
      masters.name.should.equal('New Masters');
    });

  });

  describe('PUT /api/masters/:id', function() {
    var updatedMasters;

    beforeEach(function(done) {
      request(app)
        .put('/api/masters/' + newMasters._id)
        .send({
          name: 'Updated Masters',
        })
        .expect(200)
        .expect('Content-Type', /json/)
        .end(function(err, res) {
          if (err) {
            return done(err);
          }
          updatedMasters = res.body;
          done();
        });
    });

    afterEach(function() {
      updatedMasters = {};
    });

    it('should respond with the updated masters', function() {
      updatedMasters.name.should.equal('Updated Masters');
    });

  });

  describe('DELETE /api/masters/:id', function() {

    it('should respond with 204 on successful removal', function(done) {
      request(app)
        .delete('/api/masters/' + newMasters._id)
        .expect(204)
        .end((err, res) => {
          if (err) {
            return done(err);
          }
          done();
        });
    });

    it('should respond with 404 when masters does not exist', function(done) {
      request(app)
        .delete('/api/masters/' + newMasters._id)
        .expect(404)
        .end((err, res) => {
          if (err) {
            return done(err);
          }
          done();
        });
    });

  });

});
<|endoftext|>


<commit_before>(function(){"use strict";var inputCounts={up:0,down:0,left:0,right:0,a:0,b:0,start:0,democracy:0,anarchy:0};var pastInputs=[];var TOP_THING=$("#broadcast-meta");$("Past 200 Commands"+"
"+""+"
"+"<input id='custom-stats-code' type='text' value='anarchy/democracy'>"+"").insertAfter(TOP_THING);var __icl=CurrentChat.insert_chat_line;CurrentChat.insert_chat_line=function(e){for(var button in inputCounts){if(CurrentChat.format_message(e).trim().match(new RegExp("^"+button+"$","i"))){inputCounts[button]++;pastInputs.push(button);while(pastInputs.length>200)inputCounts[pastInputs.shift()]--;break}}__icl.apply(CurrentChat,[e])};setInterval(function(){var stats_str=[];$.each(inputCounts,function(button,count){stats_str.push(button+": "+count)});$("#input-stats").html(stats_str.join(", "));var custom_code=$("#custom-stats-code").val();try{$("#custom-stats").html("&nbsp"+((new Function("with(this) { return "+custom_code+"; }")).call(inputCounts)).toString())}catch(e){$("#custom-stats").html(" invalid expression")}},1000)})();<commit_msg>update the compressed js<commit_after>(function(){"use strict";var e=200;var t;try{t=unsafeWindow}catch(n){t=window}var r=t.jQuery;var i=r("#ember735");r("Past "+e+" Commands"+"
"+""+"
"+"<input id='custom-stats-code' type='text' value='anarchy/democracy'>"+"").insertBefore(i);var s=function(){var n={up:0,down:0,left:0,right:0,a:0,b:0,start:0,democracy:0,anarchy:0};var i=[];var s=t.CurrentChat.insert_chat_line;t.CurrentChat.insert_chat_line=function(r){for(var o in n){if(r.message.trim().match(new RegExp("^"+o+"$","i"))){n[o]++;i.push(o);while(i.length>e)n[i.shift()]--;break}}s.apply(t.CurrentChat,[r])};t.setInterval(function(){var e=[];r.each(n,function(t,n){e.push(t+": "+n)});r("#input-stats").html(e.join(", "));var t=r("#custom-stats-code").val();try{r("#custom-stats").html("&nbsp"+(new Function("with(this) { return "+t+"; }")).call(n).toString())}catch(i){r("#custom-stats").html(" invalid expression")}},500)};r(function(){var e=t.setInterval(function(){if(r("#chat_loading_spinner").css("display")=="none"){t.clearInterval(e);s()}},100)})})()<|endoftext|>


<commit_before>/**
 * @author dmarcos / https://github.com/dmarcos
 *
 * It handles stereo rendering
 * If mozGetVRDevices and getVRDevices APIs are not available it gracefuly falls back to a
 * regular renderer
 *
 * The HMD supported is the Oculus DK1 and The Web API doesn't currently allow
 * to query for the display resolution (only the chrome API allows it).
 * The dimensions of the screen are temporarly hardcoded (1280 x 800).
 *
 * For VR mode to work it has to be used with the Oculus enabled builds of Firefox or Chrome:
 *
 * Firefox:
 *
 * http://mozvr.com/downloads.html
 *
 * Chrome builds:
 *
 * https://drive.google.com/a/google.com/folderview?id=0BzudLt22BqGRbW9WTHMtOWMzNjQ&usp=sharing#list
 *
 */
THREE.VREffect = function ( renderer, done ) {

	var cameraLeft = new THREE.PerspectiveCamera();
	var cameraRight = new THREE.PerspectiveCamera();

	this._renderer = renderer;

	this._init = function() {
		var self = this;
		if ( !navigator.mozGetVRDevices && !navigator.getVRDevices ) {
			if ( done ) {
				done("Your browser is not VR Ready");
			}
			return;
		}
		if ( navigator.getVRDevices ) {
			navigator.getVRDevices().then( gotVRDevices );
		} else {
			navigator.mozGetVRDevices( gotVRDevices );
		}
		function gotVRDevices( devices ) {
			var vrHMD;
			var error;
			for ( var i = 0; i < devices.length; ++i ) {
				if ( devices[i] instanceof HMDVRDevice ) {
					vrHMD = devices[i];
					self._vrHMD = vrHMD;
					self.leftEyeTranslation = vrHMD.getEyeTranslation( "left" );
					self.rightEyeTranslation = vrHMD.getEyeTranslation( "right" );
					self.leftEyeFOV = vrHMD.getRecommendedEyeFieldOfView( "left" );
					self.rightEyeFOV = vrHMD.getRecommendedEyeFieldOfView( "right" );
					break; // We keep the first we encounter
				}
			}
			if ( done ) {
				if ( !vrHMD ) {
				 error = 'HMD not available';
				}
				done( error );
			}
		}
	};

	this._init();

	this.render = function ( scene, camera ) {
		var renderer = this._renderer;
		var vrHMD = this._vrHMD;
		// VR render mode if HMD is available
		if (
			(
				document.mozFullScreenElement ||
				document.webkitFullscreenElement
			) &&
			vrHMD
		) {
			this.renderStereo.apply( this, arguments );
			return;
		}
		// Regular render mode if not HMD
		renderer.render.apply( this._renderer, arguments );
	};

	this.renderStereo = function( scene, camera, renderTarget, forceClear ) {

		var leftEyeTranslation = this.leftEyeTranslation;
		var rightEyeTranslation = this.rightEyeTranslation;
		var renderer = this._renderer;
		var rendererWidth = renderer.domElement.width / renderer.devicePixelRatio;
		var rendererHeight = renderer.domElement.height / renderer.devicePixelRatio;
		var eyeDivisionLine = rendererWidth / 2;

		renderer.enableScissorTest( true );
		renderer.clear();

		if ( camera.parent === undefined ) {
			camera.updateMatrixWorld();
		}

		cameraLeft.projectionMatrix = this.FovToProjection( this.leftEyeFOV, true, camera.near, camera.far );
		cameraRight.projectionMatrix = this.FovToProjection( this.rightEyeFOV, true, camera.near, camera.far );

		camera.matrixWorld.decompose( cameraLeft.position, cameraLeft.quaternion, cameraLeft.scale );
		camera.matrixWorld.decompose( cameraRight.position, cameraRight.quaternion, cameraRight.scale );

		cameraLeft.translateX( leftEyeTranslation.x );
		cameraRight.translateX( rightEyeTranslation.x );

		// render left eye
		renderer.setViewport( 0, 0, eyeDivisionLine, rendererHeight );
		renderer.setScissor( 0, 0, eyeDivisionLine, rendererHeight );
		renderer.render( scene, cameraLeft );

		// render right eye
		renderer.setViewport( eyeDivisionLine, 0, eyeDivisionLine, rendererHeight );
		renderer.setScissor( eyeDivisionLine, 0, eyeDivisionLine, rendererHeight );
		renderer.render( scene, cameraRight );

		renderer.enableScissorTest( false );

	};

	this.setSize = function( width, height ) {
		renderer.setSize( width, height );
	};

	this.setFullScreen = function( enable ) {
		var renderer = this._renderer;
		var vrHMD = this._vrHMD;
		var canvasOriginalSize = this._canvasOriginalSize;
		if (!vrHMD) {
			return;
		}
		// If state doesn't change we do nothing
		if ( enable === this._fullScreen ) {
			return;
		}
		this._fullScreen = !!enable;

		// VR Mode disabled
		if ( !enable ) {
			// Restores canvas original size
			renderer.setSize( canvasOriginalSize.width, canvasOriginalSize.height );
			return;
		}
		// VR Mode enabled
		this._canvasOriginalSize = {
			width: renderer.domElement.width,
			height: renderer.domElement.height
		};
		// Hardcoded Rift display size
		renderer.setSize( 1280, 800, false );
		this.startFullscreen();
	};

	this.startFullscreen = function() {
		var self = this;
		var renderer = this._renderer;
		var vrHMD = this._vrHMD;
		var canvas = renderer.domElement;
		var fullScreenChange =
			canvas.mozRequestFullScreen? 'mozfullscreenchange' : 'webkitfullscreenchange';

		document.addEventListener( fullScreenChange, onFullScreenChanged, false );
		function onFullScreenChanged() {
			if ( !document.mozFullScreenElement && !document.webkitFullscreenElement ) {
				self.setFullScreen( false );
			}
		}
		if ( canvas.mozRequestFullScreen ) {
			canvas.mozRequestFullScreen( { vrDisplay: vrHMD } );
		} else {
			canvas.webkitRequestFullscreen( { vrDisplay: vrHMD } );
		}
	};

	this.FovToNDCScaleOffset = function( fov ) {
		var pxscale = 2.0 / (fov.leftTan + fov.rightTan);
		var pxoffset = (fov.leftTan - fov.rightTan) * pxscale * 0.5;
		var pyscale = 2.0 / (fov.upTan + fov.downTan);
		var pyoffset = (fov.upTan - fov.downTan) * pyscale * 0.5;
		return { scale: [pxscale, pyscale], offset: [pxoffset, pyoffset] };
	};

	this.FovPortToProjection = function( fov, rightHanded /* = true */, zNear /* = 0.01 */, zFar /* = 10000.0 */ )
	{
		rightHanded = rightHanded === undefined ? true : rightHanded;
		zNear = zNear === undefined ? 0.01 : zNear;
		zFar = zFar === undefined ? 10000.0 : zFar;

		var handednessScale = rightHanded ? -1.0 : 1.0;

		// start with an identity matrix
		var mobj = new THREE.Matrix4();
		var m = mobj.elements;

		// and with scale/offset info for normalized device coords
		var scaleAndOffset = this.FovToNDCScaleOffset(fov);

		// X result, map clip edges to [-w,+w]
		m[0*4+0] = scaleAndOffset.scale[0];
		m[0*4+1] = 0.0;
		m[0*4+2] = scaleAndOffset.offset[0] * handednessScale;
		m[0*4+3] = 0.0;

		// Y result, map clip edges to [-w,+w]
		// Y offset is negated because this proj matrix transforms from world coords with Y=up,
		// but the NDC scaling has Y=down (thanks D3D?)
		m[1*4+0] = 0.0;
		m[1*4+1] = scaleAndOffset.scale[1];
		m[1*4+2] = -scaleAndOffset.offset[1] * handednessScale;
		m[1*4+3] = 0.0;

		// Z result (up to the app)
		m[2*4+0] = 0.0;
		m[2*4+1] = 0.0;
		m[2*4+2] = zFar / (zNear - zFar) * -handednessScale;
		m[2*4+3] = (zFar * zNear) / (zNear - zFar);

		// W result (= Z in)
		m[3*4+0] = 0.0;
		m[3*4+1] = 0.0;
		m[3*4+2] = handednessScale;
		m[3*4+3] = 0.0;

		mobj.transpose();

		return mobj;
	};

	this.FovToProjection = function( fov, rightHanded /* = true */, zNear /* = 0.01 */, zFar /* = 10000.0 */ )
	{
		var fovPort = {
			upTan: Math.tan(fov.upDegrees * Math.PI / 180.0),
			downTan: Math.tan(fov.downDegrees * Math.PI / 180.0),
			leftTan: Math.tan(fov.leftDegrees * Math.PI / 180.0),
			rightTan: Math.tan(fov.rightDegrees * Math.PI / 180.0)
		};
		return this.FovPortToProjection(fovPort, rightHanded, zNear, zFar);
	};

};
<commit_msg>Update to latest WebVR API<commit_after>/**
 * @author dmarcos / https://github.com/dmarcos
 *
 * It handles stereo rendering
 * If mozGetVRDevices and getVRDevices APIs are not available it gracefuly falls back to a
 * regular renderer
 *
 * The HMD supported is the Oculus DK1 and The Web API doesn't currently allow
 * to query for the display resolution (only the chrome API allows it).
 * The dimensions of the screen are temporarly hardcoded (1280 x 800).
 *
 * For VR mode to work it has to be used with the Oculus enabled builds of Firefox or Chrome:
 *
 * Firefox:
 *
 * http://mozvr.com/downloads.html
 *
 * Chrome builds:
 *
 * https://drive.google.com/a/google.com/folderview?id=0BzudLt22BqGRbW9WTHMtOWMzNjQ&usp=sharing#list
 *
 */
THREE.VREffect = function ( renderer, done ) {

	var cameraLeft = new THREE.PerspectiveCamera();
	var cameraRight = new THREE.PerspectiveCamera();

	this._renderer = renderer;

	this._init = function() {
		var self = this;
		if ( !navigator.mozGetVRDevices && !navigator.getVRDevices ) {
			if ( done ) {
				done("Your browser is not VR Ready");
			}
			return;
		}
		if ( navigator.getVRDevices ) {
			navigator.getVRDevices().then( gotVRDevices );
		} else {
			navigator.mozGetVRDevices( gotVRDevices );
		}
		function gotVRDevices( devices ) {
			var vrHMD;
			var error;
			for ( var i = 0; i < devices.length; ++i ) {
				if ( devices[i] instanceof HMDVRDevice ) {
					vrHMD = devices[i];
					self._vrHMD = vrHMD;
                    if ( vrHMD.getEyeParameters ) {
                        self.left = vrHMD.getEyeParameters( "left" ),
                        self.right = vrHMD.getEyeParameters( "right" );
                    }
                    else {
                        self.left = {
                            renderRect: vrHMD.getRecommendedEyeRenderRect( "left" ),
                            eyeTranslation: vrHMD.getEyeTranslation( "left" ),
                            recommendedFieldOfView: vrHMD.getRecommendedEyeFieldOfView( "left" )
                        };

                        self.right = {
                            renderRect: vrHMD.getRecommendedEyeRenderRect( "right" ),
                            eyeTranslation: vrHMD.getEyeTranslation( "right" ),
                            recommendedFieldOfView: vrHMD.getRecommendedEyeFieldOfView( "right" )
                        };
                    }
					break; // We keep the first we encounter
				}
			}
			if ( done ) {
				if ( !vrHMD ) {
				 error = 'HMD not available';
				}
				done( error );
			}
		}
	};

	this._init();

	this.render = function ( scene, camera ) {
		var renderer = this._renderer;
		var vrHMD = this._vrHMD;
		// VR render mode if HMD is available
		if (
			(
				document.mozFullScreenElement ||
				document.webkitFullscreenElement
			) &&
			vrHMD
		) {
			this.renderStereo.apply( this, arguments );
			return;
		}
		// Regular render mode if not HMD
		renderer.render.apply( this._renderer, arguments );
	};

	this.renderStereo = function( scene, camera, renderTarget, forceClear ) {

		var leftEyeTranslation = this.left.eyeTranslation;
		var rightEyeTranslation = this.right.eyeTranslation;
		var renderer = this._renderer;
		var rendererWidth = renderer.domElement.width / renderer.devicePixelRatio;
		var rendererHeight = renderer.domElement.height / renderer.devicePixelRatio;
		var eyeDivisionLine = rendererWidth / 2;

		renderer.enableScissorTest( true );
		renderer.clear();

		if ( camera.parent === undefined ) {
			camera.updateMatrixWorld();
		}

		cameraLeft.projectionMatrix = this.FovToProjection( this.left.recommendedFieldOfView, true, camera.near, camera.far );
		cameraRight.projectionMatrix = this.FovToProjection( this.right.recommendedFieldOfView, true, camera.near, camera.far );

		camera.matrixWorld.decompose( cameraLeft.position, cameraLeft.quaternion, cameraLeft.scale );
		camera.matrixWorld.decompose( cameraRight.position, cameraRight.quaternion, cameraRight.scale );

		cameraLeft.translateX( leftEyeTranslation.x );
		cameraRight.translateX( rightEyeTranslation.x );

		// render left eye
		renderer.setViewport( 0, 0, eyeDivisionLine, rendererHeight );
		renderer.setScissor( 0, 0, eyeDivisionLine, rendererHeight );
		renderer.render( scene, cameraLeft );

		// render right eye
		renderer.setViewport( eyeDivisionLine, 0, eyeDivisionLine, rendererHeight );
		renderer.setScissor( eyeDivisionLine, 0, eyeDivisionLine, rendererHeight );
		renderer.render( scene, cameraRight );

		renderer.enableScissorTest( false );

	};

	this.setSize = function( width, height ) {
		renderer.setSize( width, height );
	};

	this.setFullScreen = function( enable ) {
		var renderer = this._renderer;
		var vrHMD = this._vrHMD;
		var canvasOriginalSize = this._canvasOriginalSize;
		if (!vrHMD) {
			return;
		}
		// If state doesn't change we do nothing
		if ( enable === this._fullScreen ) {
			return;
		}
		this._fullScreen = !!enable;

		// VR Mode disabled
		if ( !enable ) {
			// Restores canvas original size
			renderer.setSize( canvasOriginalSize.width, canvasOriginalSize.height );
			return;
		}
		// VR Mode enabled
		this._canvasOriginalSize = {
			width: renderer.domElement.width,
			height: renderer.domElement.height
		};
		// Hardcoded Rift display size
		renderer.setSize( 1280, 800, false );
		this.startFullscreen();
	};

	this.startFullscreen = function() {
		var self = this;
		var renderer = this._renderer;
		var vrHMD = this._vrHMD;
		var canvas = renderer.domElement;
		var fullScreenChange =
			canvas.mozRequestFullScreen? 'mozfullscreenchange' : 'webkitfullscreenchange';

		document.addEventListener( fullScreenChange, onFullScreenChanged, false );
		function onFullScreenChanged() {
			if ( !document.mozFullScreenElement && !document.webkitFullscreenElement ) {
				self.setFullScreen( false );
			}
		}
		if ( canvas.mozRequestFullScreen ) {
			canvas.mozRequestFullScreen( { vrDisplay: vrHMD } );
		} else {
			canvas.webkitRequestFullscreen( { vrDisplay: vrHMD } );
		}
	};

	this.FovToNDCScaleOffset = function( fov ) {
		var pxscale = 2.0 / (fov.leftTan + fov.rightTan);
		var pxoffset = (fov.leftTan - fov.rightTan) * pxscale * 0.5;
		var pyscale = 2.0 / (fov.upTan + fov.downTan);
		var pyoffset = (fov.upTan - fov.downTan) * pyscale * 0.5;
		return { scale: [pxscale, pyscale], offset: [pxoffset, pyoffset] };
	};

	this.FovPortToProjection = function( fov, rightHanded /* = true */, zNear /* = 0.01 */, zFar /* = 10000.0 */ )
	{
		rightHanded = rightHanded === undefined ? true : rightHanded;
		zNear = zNear === undefined ? 0.01 : zNear;
		zFar = zFar === undefined ? 10000.0 : zFar;

		var handednessScale = rightHanded ? -1.0 : 1.0;

		// start with an identity matrix
		var mobj = new THREE.Matrix4();
		var m = mobj.elements;

		// and with scale/offset info for normalized device coords
		var scaleAndOffset = this.FovToNDCScaleOffset(fov);

		// X result, map clip edges to [-w,+w]
		m[0*4+0] = scaleAndOffset.scale[0];
		m[0*4+1] = 0.0;
		m[0*4+2] = scaleAndOffset.offset[0] * handednessScale;
		m[0*4+3] = 0.0;

		// Y result, map clip edges to [-w,+w]
		// Y offset is negated because this proj matrix transforms from world coords with Y=up,
		// but the NDC scaling has Y=down (thanks D3D?)
		m[1*4+0] = 0.0;
		m[1*4+1] = scaleAndOffset.scale[1];
		m[1*4+2] = -scaleAndOffset.offset[1] * handednessScale;
		m[1*4+3] = 0.0;

		// Z result (up to the app)
		m[2*4+0] = 0.0;
		m[2*4+1] = 0.0;
		m[2*4+2] = zFar / (zNear - zFar) * -handednessScale;
		m[2*4+3] = (zFar * zNear) / (zNear - zFar);

		// W result (= Z in)
		m[3*4+0] = 0.0;
		m[3*4+1] = 0.0;
		m[3*4+2] = handednessScale;
		m[3*4+3] = 0.0;

		mobj.transpose();

		return mobj;
	};

	this.FovToProjection = function( fov, rightHanded /* = true */, zNear /* = 0.01 */, zFar /* = 10000.0 */ )
	{
		var fovPort = {
			upTan: Math.tan(fov.upDegrees * Math.PI / 180.0),
			downTan: Math.tan(fov.downDegrees * Math.PI / 180.0),
			leftTan: Math.tan(fov.leftDegrees * Math.PI / 180.0),
			rightTan: Math.tan(fov.rightDegrees * Math.PI / 180.0)
		};
		return this.FovPortToProjection(fovPort, rightHanded, zNear, zFar);
	};

};
<|endoftext|>


<commit_before>/*jslint indent: 2, unparam: true, plusplus: true*/
/*global document: false */
"use strict";

var inheritsFrom = function (child, parent) {
  child.prototype = Object.create(parent.prototype);
  child.prototype.super = parent.prototype;
};

var AutoComplete = function (el, item, elem) {
  this.type = el;
  this.el = document.querySelector("#" + el + "-autocomplete");
  this.filter = document.querySelector("#toggl-button-" + el + "-filter");
  this.filterClear = this.el.parentNode.querySelector(".filter-clear");
  this.placeholderItem = document.querySelector("#toggl-button-" + el + "-placeholder");
  this.placeholderDiv = this.placeholderItem.querySelector("div");
  this.clearSelected = this.el.querySelector("." + el + "-clear");
  this.addLink = this.el.parentNode.querySelector(".add-new-" + el);
  this.elem = elem;
  this.item = item;
  this.lastFilter = "";
  this.listItems = [];
  this.exactMatch = false;

  this.addEvents();
};

AutoComplete.prototype.addEvents = function () {
  var that = this;

  that.placeholderItem.addEventListener('click', function (e) {
    that.filter.focus();
  });

  that.filter.addEventListener('focus', function (e) {
    that.filter.parentNode.classList.add("open");
    that.listItems = that.el.querySelectorAll(that.item);
  });

  that.filter.addEventListener('keydown', function (e) {
    if (e.code === "Tab" || e.code === "Enter") {
      that.closeDropdown();
    }
    if (e.code === "Escape" &&
        that.placeholderItem.parentNode.classList.contains("open")) {
      that.closeDropdown();
      e.stopPropagation();
      e.preventDefault();
    }
  });

  that.filter.addEventListener('keyup', function (e) {
    that.filterSelection();
  });

  that.filterClear.addEventListener('click', function (e) {
    that.closeDropdown();
  });
};

AutoComplete.prototype.clearFilters = function () {
  this.el.classList.remove("filtered");
  var i,
    a = this.el.querySelectorAll(".filter"),
    b = this.el.querySelectorAll(".tasklist-opened");

  for (i = 0; i < a.length; i++) {
    a[i].classList.remove("filter");
  }

  for (i = 0; i < b.length; i++) {
    b[i].classList.remove("tasklist-opened");
  }
};

AutoComplete.prototype.addNew = function (text) {
  var val = text || this.filter.value,
    list = this.el.querySelector("." + this.type + "-list"),
    item = document.createElement("li");

  item.className = this.type + "-item selected-" + this.type;
  item.setAttribute("title", val);
  item.textContent = val;

  list.insertBefore(item, list.querySelector("li:first-child"));
  this.filter.value = "";
  this.filterSelection();
};

AutoComplete.prototype.closeDropdown = function (t) {
  var that = t || this;
  that.filter.value = "";
  that.el.classList.remove("filtered");
  that.placeholderItem.parentNode.classList.toggle("open");
  that.placeholderItem.parentNode.classList.remove("add-allowed");
  that.clearFilters();
};

//* Project autocomplete *//

var ProjectAutoComplete = function (el, item, elem) {
  AutoComplete.call(this, el, item, elem);
};

inheritsFrom(ProjectAutoComplete, AutoComplete);

ProjectAutoComplete.prototype.setup = function (selected, tid) {
  this.setSelected(selected, tid);
  this.placeholderDiv.textContent = this.placeholderDiv.title = this.generateLabel(null, selected, this.type, tid);
  this.setProjectBullet(selected, tid);
};

ProjectAutoComplete.prototype.addEvents = function () {
  var that = this;
  this.super.addEvents.call(this);

  that.el.addEventListener('click', function (e) {
    e.stopPropagation();
    that.selectProject(e.target);
  });
};

ProjectAutoComplete.prototype.setSelected = function (ids, tid) {
  var t = this.el.querySelector("li[data-tid='" + tid + "']");
  if (!!t) {
    this.selectTask(t, true);
    return;
  }
  this.setSelectedProject(ids);
};

ProjectAutoComplete.prototype.setSelectedProject = function (ids) {
  var selected = this.el.querySelectorAll(".selected-" + this.type),
    i;

  // Clear previously selected
  if (selected.length >= 1) {
    for (i = 0; i < selected.length; i++) {
      selected[i].classList.remove("selected-" + this.type);
    }
  }

  // Select project
  if (!!ids) {
    this.el.querySelector("li[data-pid='" + ids + "']").classList.add("selected-" + this.type);
  }
};

ProjectAutoComplete.prototype.selectTask = function (elem, silent) {
  // Set selected task
  var currentSelected = this.el.querySelector(".selected-task");

  if (!!currentSelected) {
    currentSelected.classList.remove("selected-task");
  }
  elem.classList.add("selected-task");

  // Set selected project
  this.selectProject(elem.parentNode.parentNode, silent, true);
};

ProjectAutoComplete.prototype.selectProject = function (elem, silent, removeTask) {
  if (elem.classList.contains("item-name")) {
    elem = elem.parentNode;
  }
  if (!elem.classList.contains(this.type + "-row")) {
    if (elem.classList.contains("task-count")) {
      this.toggleTaskList(elem);
    }
    if (elem.classList.contains("task-item")) {
      this.selectTask(elem);
    }
    return;
  }

  if (!removeTask && this.el.querySelector(".selected-task")) {
    this.el.querySelector(".selected-task").classList.remove("selected-task");
  }

  var currentSelected = this.el.querySelector(".selected-" + this.type),
    val = elem.getAttribute("data-pid");

  if (!!currentSelected) {
    currentSelected.classList.remove("selected-" + this.type);
  }
  elem.classList.add("selected-" + this.type);

  // Update placeholder
  this.placeholderDiv.textContent = this.placeholderDiv.title = this.generateLabel(this.getSelected(), val, this.type);
  this.setProjectBullet(val);

  if (!silent) {
    // Close dropdown
    this.closeDropdown();
  }
  return false;
};

AutoComplete.prototype.toggleTaskList = function (elem) {
  var opened = this.el.querySelector(".tasklist-opened");
  if (!!opened) {
    opened.classList.remove("tasklist-opened");
  }
  if (opened !== elem.parentNode) {
    elem.parentNode.classList.toggle("tasklist-opened");
  }
};

ProjectAutoComplete.prototype.getSelected = function () {
  var selected = this.el.querySelector(".selected-" + this.type),
    task = this.el.querySelector(".selected-task"),
    pid = (!!selected) ? parseInt(selected.getAttribute("data-pid"), 10) : 0,
    tid = (!!task) ? parseInt(task.getAttribute("data-tid"), 10) : null,
    name = (!!selected) ? selected.getAttribute("title") : "";

  return {
    el: selected,
    pid: pid,
    tid: tid,
    name: name
  };
};

ProjectAutoComplete.prototype.getSelectedProjectByPid = function (pid) {
  var selected = this.el.querySelector("li[data-pid='" + pid + "']"),
    name = (!!selected) ? selected.textContent : "";

  return {
    el: selected,
    pid: pid,
    name: name
  };
};

ProjectAutoComplete.prototype.setProjectBullet = function (pid, tid, el) {
  var project,
    elem = el || this.placeholderItem.querySelector(".project-bullet"),
    result,
    task;

  if (!!pid || pid === "0") {
    project = this.el.querySelector("li[data-pid='" + pid + "']");
    if (!!project) {
      elem.className = project.querySelector(".project-bullet").className;
      result = " - " + project.getAttribute("title");
      if (!!tid) {
        task = project.querySelector("li[data-tid='" + tid + "']");
        if (!!task) {
          result += " . " + task.getAttribute("title");
        }
      }
      return result;
    }
  }
  elem.className = "project-bullet";
  return "";
};

ProjectAutoComplete.prototype.generateLabel = function (select, id, type, tid) {
  var selected = false,
    client,
    result = "",
    task;

  select = this.getSelectedProjectByPid(id);

  if (!select) {
    select = this.getSelected();
  }

  if (!!select && !!select.el) {
    selected = select.pid;
    task = select.el.querySelector(".selected-task") || select.el.querySelector("li[data-tid='" + tid + "']");
    if (!!task) {
      result += task.getAttribute("title") + " . ";
    }
    client = select.el.parentNode.querySelector(".client-row");
    if (!!client) {
      result = client.textContent + " - ";
    }
    result += select.el.getAttribute("title");
  }

  if (parseInt(id, 10) === 0 || !selected) {
    return "Add " + type;
  }
  return result;
};

ProjectAutoComplete.prototype.filterSelection = function () {
  var key,
    val = this.filter.value.toLowerCase(),
    row,
    text;

  if (val === this.lastFilter) {
    return;
  }

  if (val.length > 0 && !this.el.classList.contains("filtered")) {
    this.el.classList.add("filtered");
  }
  if (val.length === 0) {
    this.clearFilters();
    return;
  }
  this.lastFilter = val;
  this.exactMatch = false;
  for (key in this.listItems) {
    if (this.listItems.hasOwnProperty(key)) {
      row = this.listItems[key];
      text = row.getAttribute("title").toLowerCase();
      if (text.indexOf(val) !== -1) {
        if (text === val) {
          this.exactMatch = val;
        }
        row.classList.add("filter");

        if (row.classList.contains("project-row")) {
          // row.parentNode refers to ul.client-list
          row.parentNode.classList.add("filter");
          // row.parentNode.parentNode refers to ul.ws-list
          row.parentNode.parentNode.classList.add("filter");
        }
        if (row.classList.contains("client-row")) {
          // row.parentNode refers to ul.client-list
          row.parentNode.classList.add("filter-match");
        }

        if (row.classList.contains("task-item")) {
          // row.parentNode.parentNode refers to li.project-row
          row.parentNode.parentNode.classList.add("filter");
          row.parentNode.parentNode.classList.add("tasklist-opened");
          row.classList.add("filter");
        }

      } else if (!!row.classList) {
        row.classList.remove("filter");

        if (row.classList.contains("task-item")) {
          row.classList.remove("filter");
          if (row.parentNode.querySelectorAll(".filter").length === 0) {
            row.parentNode.parentNode.classList.remove("tasklist-opened");
          }
        } else {
          if (row.parentNode.querySelectorAll(".filter").length === 0) {
            row.parentNode.classList.remove("filter");
          }
          if (row.parentNode.parentNode.querySelectorAll(".filter").length === 0) {
            row.parentNode.parentNode.classList.remove("filter");
          }
          if (row.classList.contains("client-row")) {
            row.classList.remove("filter-match");
            row.parentNode.classList.remove("filter-match");
            row.parentNode.parentNode.classList.remove("filter-match");
          }
        }
      }
    }
  }
};

//* Tag autocomplete *//

var TagAutoComplete = function (el, item, elem) {
  AutoComplete.call(this, el, item, elem);
};

inheritsFrom(TagAutoComplete, AutoComplete);

TagAutoComplete.prototype.setup = function (selected) {
  this.setSelected(selected);
};

TagAutoComplete.prototype.addEvents = function () {
  var that = this;
  this.super.addEvents.call(this);

  this.el.addEventListener('click', function (e) {
    e.stopPropagation();
    that.selectTag(e);
  });

  this.clearSelected.addEventListener('click', function (e) {
    that.clearSelectedTags();
  });

  that.addLink.addEventListener('click', function (e) {
    that.addNew();
  });
};

TagAutoComplete.prototype.closeDropdown = function () {
  this.super.closeDropdown(this, this);
  this.updatePlaceholder();
};

TagAutoComplete.prototype.selectTag = function (e) {
  e.target.classList.toggle("selected-tag");
};

TagAutoComplete.prototype.setSelected = function (tags) {
  var i,
    item;

  this.clearSelectedTags();

  if (!!tags) {
    for (i = 0; i < tags.length; i += 1) {
      item = this.el.querySelector("li[title='" + tags[i] + "']");
      if (!item) {
        this.addNew(tags[i]);
      } else {
        item.classList.add("selected-" + this.type);
      }
    }
  }

  this.updatePlaceholder(tags);
};

TagAutoComplete.prototype.clearSelectedTags = function (tags) {
  var current = this.el.querySelectorAll(".tag-list li.selected-tag"),
    i;

  for (i = 0; i < current.length; i += 1) {
    current[i].classList.remove("selected-tag");
  }
};

TagAutoComplete.prototype.updatePlaceholder = function (tags) {
  if (!tags) {
    tags = this.getSelected();
  }

  if (!!tags && tags.length) {
    tags = tags.join(',');
  } else {
    tags = "Add tags";
  }
  this.placeholderDiv.textContent = this.placeholderDiv.title = tags;
};

TagAutoComplete.prototype.getSelected = function () {
  var tags = [],
    tag,
    i,
    s = this.el.querySelectorAll(".tag-list .selected-tag");
  for (i = 0; i < s.length; i += 1) {
    tag = s[i].textContent;
    tags.push(tag);
  }
  return tags;
};

TagAutoComplete.prototype.filterSelection = function () {
  var key,
    val = this.filter.value.toLowerCase(),
    row,
    text;

  if (val === this.lastFilter) {
    return;
  }

  if (val.length > 0 && !this.el.classList.contains("filtered")) {
    this.el.classList.add("filtered");
  }
  if (val.length === 0) {
    this.clearFilters();
    return;
  }
  this.lastFilter = val;
  this.exactMatch = false;
  for (key in this.listItems) {
    if (this.listItems.hasOwnProperty(key)) {
      row = this.listItems[key];
      text = row.getAttribute("title").toLowerCase();
      if (text.indexOf(val) !== -1) {
        if (text === val) {
          this.exactMatch = val;
        }
        row.classList.add("filter");
      } else if (!!row.classList) {
        row.classList.remove("filter");
      }
    }
  }
  this.updateAddLink();
};

TagAutoComplete.prototype.updateAddLink = function (e) {
  if (!!this.addLink) {
    if (!!this.exactMatch) {
      this.placeholderItem.parentNode.classList.remove("add-allowed");
    } else {
      this.placeholderItem.parentNode.classList.add("add-allowed");
    }
  }
};<commit_msg>Moved addNew to TagAutocomplete<commit_after>/*jslint indent: 2, unparam: true, plusplus: true*/
/*global document: false */
"use strict";

var inheritsFrom = function (child, parent) {
  child.prototype = Object.create(parent.prototype);
  child.prototype.super = parent.prototype;
};

var AutoComplete = function (el, item, elem) {
  this.type = el;
  this.el = document.querySelector("#" + el + "-autocomplete");
  this.filter = document.querySelector("#toggl-button-" + el + "-filter");
  this.filterClear = this.el.parentNode.querySelector(".filter-clear");
  this.placeholderItem = document.querySelector("#toggl-button-" + el + "-placeholder");
  this.placeholderDiv = this.placeholderItem.querySelector("div");
  this.clearSelected = this.el.querySelector("." + el + "-clear");
  this.addLink = this.el.parentNode.querySelector(".add-new-" + el);
  this.elem = elem;
  this.item = item;
  this.lastFilter = "";
  this.listItems = [];
  this.exactMatch = false;

  this.addEvents();
};

AutoComplete.prototype.addEvents = function () {
  var that = this;

  that.placeholderItem.addEventListener('click', function (e) {
    that.filter.focus();
  });

  that.filter.addEventListener('focus', function (e) {
    that.filter.parentNode.classList.add("open");
    that.listItems = that.el.querySelectorAll(that.item);
  });

  that.filter.addEventListener('keydown', function (e) {
    if (e.code === "Tab" || e.code === "Enter") {
      that.closeDropdown();
    }
    if (e.code === "Escape" &&
        that.placeholderItem.parentNode.classList.contains("open")) {
      that.closeDropdown();
      e.stopPropagation();
      e.preventDefault();
    }
  });

  that.filter.addEventListener('keyup', function (e) {
    that.filterSelection();
  });

  that.filterClear.addEventListener('click', function (e) {
    that.closeDropdown();
  });
};

AutoComplete.prototype.clearFilters = function () {
  this.el.classList.remove("filtered");
  var i,
    a = this.el.querySelectorAll(".filter"),
    b = this.el.querySelectorAll(".tasklist-opened");

  for (i = 0; i < a.length; i++) {
    a[i].classList.remove("filter");
  }

  for (i = 0; i < b.length; i++) {
    b[i].classList.remove("tasklist-opened");
  }
};

AutoComplete.prototype.closeDropdown = function (t) {
  var that = t || this;
  that.filter.value = "";
  that.el.classList.remove("filtered");
  that.placeholderItem.parentNode.classList.toggle("open");
  that.placeholderItem.parentNode.classList.remove("add-allowed");
  that.clearFilters();
};

//* Project autocomplete *//

var ProjectAutoComplete = function (el, item, elem) {
  AutoComplete.call(this, el, item, elem);
};

inheritsFrom(ProjectAutoComplete, AutoComplete);

ProjectAutoComplete.prototype.setup = function (selected, tid) {
  this.setSelected(selected, tid);
  this.placeholderDiv.textContent = this.placeholderDiv.title = this.generateLabel(null, selected, this.type, tid);
  this.setProjectBullet(selected, tid);
};

ProjectAutoComplete.prototype.addEvents = function () {
  var that = this;
  this.super.addEvents.call(this);

  that.el.addEventListener('click', function (e) {
    e.stopPropagation();
    that.selectProject(e.target);
  });
};

ProjectAutoComplete.prototype.setSelected = function (ids, tid) {
  var t = this.el.querySelector("li[data-tid='" + tid + "']");
  if (!!t) {
    this.selectTask(t, true);
    return;
  }
  this.setSelectedProject(ids);
};

ProjectAutoComplete.prototype.setSelectedProject = function (ids) {
  var selected = this.el.querySelectorAll(".selected-" + this.type),
    i;

  // Clear previously selected
  if (selected.length >= 1) {
    for (i = 0; i < selected.length; i++) {
      selected[i].classList.remove("selected-" + this.type);
    }
  }

  // Select project
  if (!!ids) {
    this.el.querySelector("li[data-pid='" + ids + "']").classList.add("selected-" + this.type);
  }
};

ProjectAutoComplete.prototype.selectTask = function (elem, silent) {
  // Set selected task
  var currentSelected = this.el.querySelector(".selected-task");

  if (!!currentSelected) {
    currentSelected.classList.remove("selected-task");
  }
  elem.classList.add("selected-task");

  // Set selected project
  this.selectProject(elem.parentNode.parentNode, silent, true);
};

ProjectAutoComplete.prototype.selectProject = function (elem, silent, removeTask) {
  if (elem.classList.contains("item-name")) {
    elem = elem.parentNode;
  }
  if (!elem.classList.contains(this.type + "-row")) {
    if (elem.classList.contains("task-count")) {
      this.toggleTaskList(elem);
    }
    if (elem.classList.contains("task-item")) {
      this.selectTask(elem);
    }
    return;
  }

  if (!removeTask && this.el.querySelector(".selected-task")) {
    this.el.querySelector(".selected-task").classList.remove("selected-task");
  }

  var currentSelected = this.el.querySelector(".selected-" + this.type),
    val = elem.getAttribute("data-pid");

  if (!!currentSelected) {
    currentSelected.classList.remove("selected-" + this.type);
  }
  elem.classList.add("selected-" + this.type);

  // Update placeholder
  this.placeholderDiv.textContent = this.placeholderDiv.title = this.generateLabel(this.getSelected(), val, this.type);
  this.setProjectBullet(val);

  if (!silent) {
    // Close dropdown
    this.closeDropdown();
  }
  return false;
};

AutoComplete.prototype.toggleTaskList = function (elem) {
  var opened = this.el.querySelector(".tasklist-opened");
  if (!!opened) {
    opened.classList.remove("tasklist-opened");
  }
  if (opened !== elem.parentNode) {
    elem.parentNode.classList.toggle("tasklist-opened");
  }
};

ProjectAutoComplete.prototype.getSelected = function () {
  var selected = this.el.querySelector(".selected-" + this.type),
    task = this.el.querySelector(".selected-task"),
    pid = (!!selected) ? parseInt(selected.getAttribute("data-pid"), 10) : 0,
    tid = (!!task) ? parseInt(task.getAttribute("data-tid"), 10) : null,
    name = (!!selected) ? selected.getAttribute("title") : "";

  return {
    el: selected,
    pid: pid,
    tid: tid,
    name: name
  };
};

ProjectAutoComplete.prototype.getSelectedProjectByPid = function (pid) {
  var selected = this.el.querySelector("li[data-pid='" + pid + "']"),
    name = (!!selected) ? selected.textContent : "";

  return {
    el: selected,
    pid: pid,
    name: name
  };
};

ProjectAutoComplete.prototype.setProjectBullet = function (pid, tid, el) {
  var project,
    elem = el || this.placeholderItem.querySelector(".project-bullet"),
    result,
    task;

  if (!!pid || pid === "0") {
    project = this.el.querySelector("li[data-pid='" + pid + "']");
    if (!!project) {
      elem.className = project.querySelector(".project-bullet").className;
      result = " - " + project.getAttribute("title");
      if (!!tid) {
        task = project.querySelector("li[data-tid='" + tid + "']");
        if (!!task) {
          result += " . " + task.getAttribute("title");
        }
      }
      return result;
    }
  }
  elem.className = "project-bullet";
  return "";
};

ProjectAutoComplete.prototype.generateLabel = function (select, id, type, tid) {
  var selected = false,
    client,
    result = "",
    task;

  select = this.getSelectedProjectByPid(id);

  if (!select) {
    select = this.getSelected();
  }

  if (!!select && !!select.el) {
    selected = select.pid;
    task = select.el.querySelector(".selected-task") || select.el.querySelector("li[data-tid='" + tid + "']");
    if (!!task) {
      result += task.getAttribute("title") + " . ";
    }
    client = select.el.parentNode.querySelector(".client-row");
    if (!!client) {
      result = client.textContent + " - ";
    }
    result += select.el.getAttribute("title");
  }

  if (parseInt(id, 10) === 0 || !selected) {
    return "Add " + type;
  }
  return result;
};

ProjectAutoComplete.prototype.filterSelection = function () {
  var key,
    val = this.filter.value.toLowerCase(),
    row,
    text;

  if (val === this.lastFilter) {
    return;
  }

  if (val.length > 0 && !this.el.classList.contains("filtered")) {
    this.el.classList.add("filtered");
  }
  if (val.length === 0) {
    this.clearFilters();
    return;
  }
  this.lastFilter = val;
  this.exactMatch = false;
  for (key in this.listItems) {
    if (this.listItems.hasOwnProperty(key)) {
      row = this.listItems[key];
      text = row.getAttribute("title").toLowerCase();
      if (text.indexOf(val) !== -1) {
        if (text === val) {
          this.exactMatch = val;
        }
        row.classList.add("filter");

        if (row.classList.contains("project-row")) {
          // row.parentNode refers to ul.client-list
          row.parentNode.classList.add("filter");
          // row.parentNode.parentNode refers to ul.ws-list
          row.parentNode.parentNode.classList.add("filter");
        }
        if (row.classList.contains("client-row")) {
          // row.parentNode refers to ul.client-list
          row.parentNode.classList.add("filter-match");
        }

        if (row.classList.contains("task-item")) {
          // row.parentNode.parentNode refers to li.project-row
          row.parentNode.parentNode.classList.add("filter");
          row.parentNode.parentNode.classList.add("tasklist-opened");
          row.classList.add("filter");
        }

      } else if (!!row.classList) {
        row.classList.remove("filter");

        if (row.classList.contains("task-item")) {
          row.classList.remove("filter");
          if (row.parentNode.querySelectorAll(".filter").length === 0) {
            row.parentNode.parentNode.classList.remove("tasklist-opened");
          }
        } else {
          if (row.parentNode.querySelectorAll(".filter").length === 0) {
            row.parentNode.classList.remove("filter");
          }
          if (row.parentNode.parentNode.querySelectorAll(".filter").length === 0) {
            row.parentNode.parentNode.classList.remove("filter");
          }
          if (row.classList.contains("client-row")) {
            row.classList.remove("filter-match");
            row.parentNode.classList.remove("filter-match");
            row.parentNode.parentNode.classList.remove("filter-match");
          }
        }
      }
    }
  }
};

//* Tag autocomplete *//

var TagAutoComplete = function (el, item, elem) {
  AutoComplete.call(this, el, item, elem);
};

inheritsFrom(TagAutoComplete, AutoComplete);

TagAutoComplete.prototype.setup = function (selected) {
  this.setSelected(selected);
};

TagAutoComplete.prototype.addEvents = function () {
  var that = this;
  this.super.addEvents.call(this);

  this.el.addEventListener('click', function (e) {
    e.stopPropagation();
    that.selectTag(e);
  });

  this.clearSelected.addEventListener('click', function (e) {
    that.clearSelectedTags();
  });

  that.addLink.addEventListener('click', function (e) {
    that.addNew();
  });
};

TagAutoComplete.prototype.closeDropdown = function () {
  this.super.closeDropdown(this, this);
  this.updatePlaceholder();
};

TagAutoComplete.prototype.selectTag = function (e) {
  e.target.classList.toggle("selected-tag");
};

TagAutoComplete.prototype.setSelected = function (tags) {
  var i,
    item;

  this.clearSelectedTags();

  if (!!tags) {
    for (i = 0; i < tags.length; i += 1) {
      item = this.el.querySelector("li[title='" + tags[i] + "']");
      if (!item) {
        this.addNew(tags[i]);
      } else {
        item.classList.add("selected-" + this.type);
      }
    }
  }

  this.updatePlaceholder(tags);
};

TagAutoComplete.prototype.clearSelectedTags = function (tags) {
  var current = this.el.querySelectorAll(".tag-list li.selected-tag"),
    i;

  for (i = 0; i < current.length; i += 1) {
    current[i].classList.remove("selected-tag");
  }
};

TagAutoComplete.prototype.updatePlaceholder = function (tags) {
  if (!tags) {
    tags = this.getSelected();
  }

  if (!!tags && tags.length) {
    tags = tags.join(',');
  } else {
    tags = "Add tags";
  }
  this.placeholderDiv.textContent = this.placeholderDiv.title = tags;
};

TagAutoComplete.prototype.getSelected = function () {
  var tags = [],
    tag,
    i,
    s = this.el.querySelectorAll(".tag-list .selected-tag");
  for (i = 0; i < s.length; i += 1) {
    tag = s[i].textContent;
    tags.push(tag);
  }
  return tags;
};

TagAutoComplete.prototype.filterSelection = function () {
  var key,
    val = this.filter.value.toLowerCase(),
    row,
    text;

  if (val === this.lastFilter) {
    return;
  }

  if (val.length > 0 && !this.el.classList.contains("filtered")) {
    this.el.classList.add("filtered");
  }
  if (val.length === 0) {
    this.clearFilters();
    return;
  }
  this.lastFilter = val;
  this.exactMatch = false;
  for (key in this.listItems) {
    if (this.listItems.hasOwnProperty(key)) {
      row = this.listItems[key];
      text = row.getAttribute("title").toLowerCase();
      if (text.indexOf(val) !== -1) {
        if (text === val) {
          this.exactMatch = val;
        }
        row.classList.add("filter");
      } else if (!!row.classList) {
        row.classList.remove("filter");
      }
    }
  }
  this.updateAddLink();
};

TagAutoComplete.prototype.updateAddLink = function (e) {
  if (!!this.addLink) {
    if (!!this.exactMatch) {
      this.placeholderItem.parentNode.classList.remove("add-allowed");
    } else {
      this.placeholderItem.parentNode.classList.add("add-allowed");
    }
  }
};

TagAutoComplete.prototype.addNew = function (text) {
  var val = text || this.filter.value,
    list = this.el.querySelector("." + this.type + "-list"),
    item = document.createElement("li");

  item.className = this.type + "-item selected-" + this.type;
  item.setAttribute("title", val);
  item.textContent = val;

  list.insertBefore(item, list.querySelector("li:first-child"));
  this.filter.value = "";
  this.filterSelection();
};<|endoftext|>


<commit_before>
    // handles interfacing with GIFEncoder
    var MFAnimatedGIF = function(opts) {
        var encoder;

        var _rotate = function(image, rotation) {
            var canvas = document.createElement("canvas");

            canvas.width = image.width;
            canvas.height = image.height;

            var ctx = canvas.getContext('2d');
            ctx.translate(image.width/2, image.height/2);
            ctx.rotate(rotation * Math.PI / 180.0);
            ctx.drawImage(image, -image.width/2, -image.height/2, image.width, image.height);
            ctx.rotate(rotation * Math.PI / 180.0);
            ctx.translate(-image.width/2, -image.height/2);

            return canvas;
        };

        var _initialize = function(opts) {

            var canvas = document.createElement("canvas");
            var context = canvas.getContext('2d');

            encoder = new GIFEncoder();
            encoder.setRepeat(opts.repeat);
            encoder.setDelay(opts.delay);
            canvas.width  = opts.width;
            canvas.height = opts.height;
            encoder.setSize(opts.width, opts.height);
            encoder.setQuality(App.maxQuality + 1 - (App.maxQuality * (opts.quality / 10)));

            encoder.start();

            for(var i=0; i<opts.images.length; i++) {
                var animframe = (opts.rotations[i] === 0) ? opts.images[i] : _rotate(opts.images[i], opts.rotations[i]);

                var ctx = canvas.getContext('2d');
                ctx.drawImage(animframe, 0, 0, animframe.width, animframe.height, 0, 0, canvas.width, canvas.height);

                encoder.addFrame(ctx);
            }

            encoder.finish();
        };

        var _rawDataURL = function() {
            return $.base64.encode(encoder.stream().getData());
        };

        var _dataURL = function() {
            return 'data:image/gif;base64,' + _rawDataURL();
        };

        var _binaryURL = function() {
            // Convert encoder data to binary format
            var data = encoder.stream().getData();
            var byteArray = new Uint8Array(data.length);
            for (var i = 0; i < data.length; i++) {
                byteArray[i] = data.charCodeAt(i) & 0xff;
            }

            var bb = new BlobBuilder();
            bb.append(byteArray.buffer);
            return window.URL.createObjectURL(bb.getBlob("image/gif"));
        };

        _initialize(opts);

        return {
            dataURL   : _dataURL,
            rawDataURL: _rawDataURL,
            binaryURL : _binaryURL
        };
    };

    var fileList = $("#inimglist");
    var body = $("body");

    var App = {};
    App.MAX_BYTES = 2*1024*1024; // 2MB
    App.initialRate = 300; // 300ms delay in frames
    App.initialQuality = 10; // 10 is supposed to give a good balance
    App.maxQuality = 20;
    App.timeline = [];
    App.mfAnimatedGIF = null;

    App.clear = function() {
        finalImage.attr("src", "about:blank").hide();
        fileList.empty();
        App.timeline = [];
        App.mfAnimatedGIF = null;
        body.removeClass("hasfiles");
    };
    App.rebuildTimeline = function() {
        App.timeline = fileList.find(".col").map(function() {
            var originalimg = new Image();
            var src = $(this).find("img").attr("src");
            if (src) {
                originalimg.src = src;
                return originalimg;
            }
        }).toArray();
    };

    setupDrag();

    // Bail out if the browser doesn't support required features
    // blobbuilder and a[download] are not required, as there is a fallback
    var support = FileReaderJS.enabled && Modernizr.draganddrop &&
                    document.querySelector && Modernizr.postmessage && window.JSON;
    if (!support) {
        body.addClass("disabled");
        var caniscript = document.createElement('script');
        caniscript.src = 'http://sandbox.thewikies.com/caniuse/json+filereader+draganddrop+querySelector+postmessage.html?callback=canicallback';
        document.body.appendChild(caniscript);
    }

    // drag and drop setup.
    var opts = {
        accept: 'image/*',
        on: {
            beforestart: function(file) {
                if (file.size > App.MAX_BYTES) {
                    return false;
                }
            },
            error: function(file) {
                fileList.append("
Error

");
            },
            skip: function(file) {
                fileList.append("
Skip

");
            },
            load: function(e, file) {
                fileList.append("

");

                var originalimg = new Image();

                if (App.timeline.length === 0){
                    // TODO: do we need this?
                    // App.timeline[0].width seems to work fine when called in groupend
                    originalimg.onload = function(){
                        App.initialWidth    = originalimg.width;
                        App.initialHeight   = originalimg.height;
                        $(window).trigger('firstimage');
                    };
                }

                originalimg.src = e.target.result;
                App.timeline.push(originalimg);
            },
            groupstart: function(group) {
                body.addClass("hasfiles");
            },
            groupend: function(group) {
                // TOOOOOO SLOW
                //buildGif();
            }
        }
    };

    // the library handles most of the dnd bits.
    FileReaderJS.setupDrop(document.body, opts);
    FileReaderJS.setupClipboard(document.body, opts);

    $(document).on('click', '.rotateimg', function(e) {
        var currentRotation = parseInt($(e.currentTarget).attr('data-rotation')) + 90;
        if(currentRotation >= 360) currentRotation = 0;
        $(e.currentTarget).rotate(currentRotation);
        $(e.currentTarget).attr('data-rotation', currentRotation);
    });

    // test to see if we can do cool download or fallback style.
        Modernizr.addTest({
            // BlobBuilder
            "blobbuilder": function() {
                return  !!(window.BlobBuilder || window.MozBlobBuilder || window.WebKitBlobBuilder);
            },
            // Blob URL support
            "bloburls": function() {
                return !!(window.URL && window.URL.createObjectURL || window.webkitURL && window.webkitURL.createObjectURL);
            },
            // download attribute
            "download": function() {
                return "download" in document.createElement("a");
            },
            // FormData
            "formdata": function() {
                return  !!(window.FormData && "append" in window.FormData.prototype);
            }
        });

        if(!Modernizr.download && $('#saveabro')) {
            var iframe = document.createElement("iframe");

            iframe.src = "http://saveasbro.com/gif/";
            iframe.setAttribute("style","position: absolute; visibility: hidden; left: -999em;");
            iframe.id = "saveasbro";
            document.body.appendChild(iframe);
        }

    var finalImage = $("#animresult");
    $(".clear").on('click', function() {
        App.clear();
        $('#sharelink').css({'display':'none'});
        return false;
    });

    // kick off GIF generation and download prep
    $('.play').on('click', function(e) {
        buildGif();
        return false;
    });

    $('#sharelink').on('click', function(e) {
        var filename = "animated."+((+new Date()) + "").substr(8);

        // Imgur takes the image data, filename, title, caption, success callback and error callback
        ShareGIFWith.imgur(App.mfAnimatedGIF.rawDataURL(), filename, '', '',
        function(deletePage, imgurPage, largeThumbnail, original, smallSquare) {
            alert(imgurPage);
        },
        function() {
            alert('Could not upload image to imgur.');
        });

        return false;
    });

    function buildGif() {

        if (!App.timeline.length) {
            return;
        }

        var rotations = [];
        $('.rotateimg').each(function(j, el) {
            rotations.push($(el).data('rotation'));
        });

        App.mfAnimatedGIF = new MFAnimatedGIF({
            images: App.timeline,
            rotations: rotations,
            delay : App.rate,
            quality : App.quality,
            repeat: 0,

            // use dimensions from first image as default
            height: App.animHeight || App.timeline[0].height,
            width : App.animWidth  || App.timeline[0].width
        });

        // todo the following will display the share link when ready
        // $('#sharelink').css({'display':'inline-block'});

        $('#animresult').attr('src', App.mfAnimatedGIF.dataURL());

        //
        // Create the download link
        //
        window.URL = window.webkitURL || window.URL;
        window.BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder;

        var filename = "animated."+((+new Date()) + "").substr(8);

        var a = document.querySelector('#downloadlink');
        a.style.display = "none";

        if(Modernizr.download && Modernizr.bloburls && Modernizr.blobbuilder) {
            a.download = filename + '.gif';
            a.href = App.mfAnimatedGIF.binaryURL(filename);
            a.style.display = "inline-block";

            a.onclick = function(e) {
              // Need a small delay for the revokeObjectURL to work properly.
              setTimeout(function() {
                window.URL.revokeObjectURL(a.href);
              }, 1500);
            };
        } else {
            window.onmessage = function(e) {
                e = e || window.event;

                var origin = e.origin || e.domain || e.uri;
                if(origin !== "http://saveasbro.com") return;
                a.href = e.data;
                a.style.display = "inline-block";
            };

            var iframe = document.querySelector('#saveasbro');
            iframe.contentWindow.postMessage(JSON.stringify({name:filename, data: App.mfAnimatedGIF.rawDataURL(), formdata: Modernizr.formdata}),"http://saveasbro.com/gif/");
        }

    }

function canicallback(data){
    $('#dropArea').html(data.html);
}
<commit_msg>prevent js error on non FileReader browsers. for fun. h<commit_after>
    // handles interfacing with GIFEncoder
    var MFAnimatedGIF = function(opts) {
        var encoder;

        var _rotate = function(image, rotation) {
            var canvas = document.createElement("canvas");

            canvas.width = image.width;
            canvas.height = image.height;

            var ctx = canvas.getContext('2d');
            ctx.translate(image.width/2, image.height/2);
            ctx.rotate(rotation * Math.PI / 180.0);
            ctx.drawImage(image, -image.width/2, -image.height/2, image.width, image.height);
            ctx.rotate(rotation * Math.PI / 180.0);
            ctx.translate(-image.width/2, -image.height/2);

            return canvas;
        };

        var _initialize = function(opts) {

            var canvas = document.createElement("canvas");
            var context = canvas.getContext('2d');

            encoder = new GIFEncoder();
            encoder.setRepeat(opts.repeat);
            encoder.setDelay(opts.delay);
            canvas.width  = opts.width;
            canvas.height = opts.height;
            encoder.setSize(opts.width, opts.height);
            encoder.setQuality(App.maxQuality + 1 - (App.maxQuality * (opts.quality / 10)));

            encoder.start();

            for(var i=0; i<opts.images.length; i++) {
                var animframe = (opts.rotations[i] === 0) ? opts.images[i] : _rotate(opts.images[i], opts.rotations[i]);

                var ctx = canvas.getContext('2d');
                ctx.drawImage(animframe, 0, 0, animframe.width, animframe.height, 0, 0, canvas.width, canvas.height);

                encoder.addFrame(ctx);
            }

            encoder.finish();
        };

        var _rawDataURL = function() {
            return $.base64.encode(encoder.stream().getData());
        };

        var _dataURL = function() {
            return 'data:image/gif;base64,' + _rawDataURL();
        };

        var _binaryURL = function() {
            // Convert encoder data to binary format
            var data = encoder.stream().getData();
            var byteArray = new Uint8Array(data.length);
            for (var i = 0; i < data.length; i++) {
                byteArray[i] = data.charCodeAt(i) & 0xff;
            }

            var bb = new BlobBuilder();
            bb.append(byteArray.buffer);
            return window.URL.createObjectURL(bb.getBlob("image/gif"));
        };

        _initialize(opts);

        return {
            dataURL   : _dataURL,
            rawDataURL: _rawDataURL,
            binaryURL : _binaryURL
        };
    };

    var fileList = $("#inimglist");
    var body = $("body");

    var App = {};
    App.MAX_BYTES = 2*1024*1024; // 2MB
    App.initialRate = 300; // 300ms delay in frames
    App.initialQuality = 10; // 10 is supposed to give a good balance
    App.maxQuality = 20;
    App.timeline = [];
    App.mfAnimatedGIF = null;

    App.clear = function() {
        finalImage.attr("src", "about:blank").hide();
        fileList.empty();
        App.timeline = [];
        App.mfAnimatedGIF = null;
        body.removeClass("hasfiles");
    };
    App.rebuildTimeline = function() {
        App.timeline = fileList.find(".col").map(function() {
            var originalimg = new Image();
            var src = $(this).find("img").attr("src");
            if (src) {
                originalimg.src = src;
                return originalimg;
            }
        }).toArray();
    };

    setupDrag();

    // drag and drop setup.
    var opts = {
        accept: 'image/*',
        on: {
            beforestart: function(file) {
                if (file.size > App.MAX_BYTES) {
                    return false;
                }
            },
            error: function(file) {
                fileList.append("
Error

");
            },
            skip: function(file) {
                fileList.append("
Skip

");
            },
            load: function(e, file) {
                fileList.append("

");

                var originalimg = new Image();

                if (App.timeline.length === 0){
                    // TODO: do we need this?
                    // App.timeline[0].width seems to work fine when called in groupend
                    originalimg.onload = function(){
                        App.initialWidth    = originalimg.width;
                        App.initialHeight   = originalimg.height;
                        $(window).trigger('firstimage');
                    };
                }

                originalimg.src = e.target.result;
                App.timeline.push(originalimg);
            },
            groupstart: function(group) {
                body.addClass("hasfiles");
            },
            groupend: function(group) {
                // TOOOOOO SLOW
                //buildGif();
            }
        }
    };

    // Bail out if the browser doesn't support required features
    // blobbuilder and a[download] are not required, as there is a fallback
    var support = FileReaderJS.enabled && Modernizr.draganddrop &&
                    document.querySelector && Modernizr.postmessage && window.JSON;
    if (!support) {
        body.addClass("disabled");
        var caniscript = document.createElement('script');
        caniscript.src = 'http://sandbox.thewikies.com/caniuse/json+filereader+draganddrop+querySelector+postmessage.html?callback=canicallback';
        document.body.appendChild(caniscript);

    } else {

        // the library handles most of the dnd bits.
        FileReaderJS.setupDrop(document.body, opts);
        FileReaderJS.setupClipboard(document.body, opts);

    }

    $(document).on('click', '.rotateimg', function(e) {
        var currentRotation = parseInt($(e.currentTarget).attr('data-rotation')) + 90;
        if(currentRotation >= 360) currentRotation = 0;
        $(e.currentTarget).rotate(currentRotation);
        $(e.currentTarget).attr('data-rotation', currentRotation);
    });

    // test to see if we can do cool download or fallback style.
        Modernizr.addTest({
            // BlobBuilder
            "blobbuilder": function() {
                return  !!(window.BlobBuilder || window.MozBlobBuilder || window.WebKitBlobBuilder);
            },
            // Blob URL support
            "bloburls": function() {
                return !!(window.URL && window.URL.createObjectURL || window.webkitURL && window.webkitURL.createObjectURL);
            },
            // download attribute
            "download": function() {
                return "download" in document.createElement("a");
            },
            // FormData
            "formdata": function() {
                return  !!(window.FormData && "append" in window.FormData.prototype);
            }
        });

        if(!Modernizr.download && $('#saveabro')) {
            var iframe = document.createElement("iframe");

            iframe.src = "http://saveasbro.com/gif/";
            iframe.setAttribute("style","position: absolute; visibility: hidden; left: -999em;");
            iframe.id = "saveasbro";
            document.body.appendChild(iframe);
        }

    var finalImage = $("#animresult");
    $(".clear").on('click', function() {
        App.clear();
        $('#sharelink').css({'display':'none'});
        return false;
    });

    // kick off GIF generation and download prep
    $('.play').on('click', function(e) {
        buildGif();
        return false;
    });

    $('#sharelink').on('click', function(e) {
        var filename = "animated."+((+new Date()) + "").substr(8);

        // Imgur takes the image data, filename, title, caption, success callback and error callback
        ShareGIFWith.imgur(App.mfAnimatedGIF.rawDataURL(), filename, '', '',
        function(deletePage, imgurPage, largeThumbnail, original, smallSquare) {
            alert(imgurPage);
        },
        function() {
            alert('Could not upload image to imgur.');
        });

        return false;
    });

    function buildGif() {

        if (!App.timeline.length) {
            return;
        }

        var rotations = [];
        $('.rotateimg').each(function(j, el) {
            rotations.push($(el).data('rotation'));
        });

        App.mfAnimatedGIF = new MFAnimatedGIF({
            images: App.timeline,
            rotations: rotations,
            delay : App.rate,
            quality : App.quality,
            repeat: 0,

            // use dimensions from first image as default
            height: App.animHeight || App.timeline[0].height,
            width : App.animWidth  || App.timeline[0].width
        });

        // todo the following will display the share link when ready
        // $('#sharelink').css({'display':'inline-block'});

        $('#animresult').attr('src', App.mfAnimatedGIF.dataURL());

        //
        // Create the download link
        //
        window.URL = window.webkitURL || window.URL;
        window.BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder;

        var filename = "animated."+((+new Date()) + "").substr(8);

        var a = document.querySelector('#downloadlink');
        a.style.display = "none";

        if(Modernizr.download && Modernizr.bloburls && Modernizr.blobbuilder) {
            a.download = filename + '.gif';
            a.href = App.mfAnimatedGIF.binaryURL(filename);
            a.style.display = "inline-block";

            a.onclick = function(e) {
              // Need a small delay for the revokeObjectURL to work properly.
              setTimeout(function() {
                window.URL.revokeObjectURL(a.href);
              }, 1500);
            };
        } else {
            window.onmessage = function(e) {
                e = e || window.event;

                var origin = e.origin || e.domain || e.uri;
                if(origin !== "http://saveasbro.com") return;
                a.href = e.data;
                a.style.display = "inline-block";
            };

            var iframe = document.querySelector('#saveasbro');
            iframe.contentWindow.postMessage(JSON.stringify({name:filename, data: App.mfAnimatedGIF.rawDataURL(), formdata: Modernizr.formdata}),"http://saveasbro.com/gif/");
        }

    }

function canicallback(data){
    $('#dropArea').html(data.html);
}
<|endoftext|>


<commit_before>'use strict';

const passport = require('passport');
const main = require('../app/controllers/main');
const api = require('../app/controllers/api');
const auth = require('../app/controllers/auth');

/**
 * Expose routes
 */

module.exports = function applyRoutes(app) {
  app.get('/', main.index);
  app.post('/auth/token', auth.token);
  app.get('/:type(orders|accounts|portions|vendors)', passport.authenticate('jwt'), api);
  app.get('/:type(orders|accounts|portions|vendors)/:id', passport.authenticate('jwt'), api);
  app.post('/:type(orders|portions|vendors)', passport.authenticate('jwt'), api);
  app.post('/:type(accounts)', api);
};
<commit_msg>Add server endpoint to update order via api<commit_after>'use strict';

const passport = require('passport');
const main = require('../app/controllers/main');
const api = require('../app/controllers/api');
const auth = require('../app/controllers/auth');

/**
 * Expose routes
 */

module.exports = function applyRoutes(app) {
  app.get('/', main.index);
  app.post('/auth/token', auth.token);
  app.get('/:type(orders|accounts|portions|vendors)', passport.authenticate('jwt'), api);
  app.get('/:type(orders|accounts|portions|vendors)/:id', passport.authenticate('jwt'), api);
  app.patch('/:type(orders)/:id', passport.authenticate('jwt'), api);
  app.post('/:type(orders|portions|vendors)', passport.authenticate('jwt'), api);
  app.post('/:type(accounts)', api);
};
<|endoftext|>


<commit_before>var request = require('request');
var AWS = require('aws-sdk');
var ec2 = new AWS.EC2({
  apiVersion: '2014-10-01'
, region: 'us-west-2'
});

var NaiveStrategy = require('./poolingStrategies/NaiveStrategy');

var poolingStrategy;

function InstancePool(strat) {

  // TODO: This should eventually change based on what
  // the user passes in.
  poolingStrategy = new NaiveStrategy();
}

InstancePool.terminate = function(instanceId, callback) {
  var params = {
    InstanceIds: [instanceId]
  };
  ec2.terminateInstances(params, function(err, data) {
    if (err) {
      console.log(err, data);
    } else {
      callback(instanceId);
    }
  });
};

InstancePool.create = function(type, callback) {
  // Get ami list
  var amis = require('./defaultAmis.json');

  // Define instance paramaters
  var params = {
    ImageId: amis[type]
  , InstanceType: type
  , KeyName: '123abc'
  , MinCount: 1
  , MaxCount: 1
  };

  // Create the instance.
  ec2.runInstances(params, function(err, data) {
    if (err) {
      console.log('There was a problem creating the instance.', err);
      return;
    }

    // Get the instance ID
    var instanceId = data.Instances[0].InstanceId;

    // Apply tags.
    var tags = {
      Resources: [instanceId]
    , Tags: [
        {
          Key: 'smake'
        , Value: 'pool'
        }
      ]
    };

    ec2.createTags(tags, function(err) {
      if (err) {
        console.log('There was a problem applying tags to instance ' + instanceId, err);
        callback(undefined);
      } else {
        if (callback) {
          callback(instanceId);
        }
      }
    });
  });
};

// Removes smake pool tags from the specified instance.
// The instanceId is passed to the callback function.
InstancePool.removeFromPool = function(instanceId, callback) {
  // Remove pool tags
  var params = {
    Resources: [instanceId]
  , Tags: [
      {
        Key: 'smake'
      }
    ]
  };

  ec2.deleteTags(params, function(err, data) {
    if (err) {
      console.log(err, err.stack);
    } else {
      callback(instanceId);
    }
  });
};

InstancePool.prototype.initialize = function(server, done) {
  // Get a list of required instance types.
  var requiredInstances = poolingStrategy.getRequiredInstances();

  // Get a list of all current pool instances.
  this.getPoolInstances(function(currentInstances) {
    if (currentInstances === undefined) {
      server.log('info', 'Did not find existing pool resources');
      // Create necessary instances.
      requiredInstances.forEach(function(requiredInstance) {
        var type = requiredInstance.type;
        var count = requiredInstance.count;
        for (var i = 0; i < count; i++) {
          InstancePool.create(type, function(instanceId) {
            server.log('info', 'Starting new ' + type + ' instance ' + instanceId);
          });
        }
      });
      done();
      return;
    }
    server.log('info', 'Assimilating existing pool resources...');

    // Compare required and current instance counts.
    var diff = {};
    // Below is an example diff object
    // {
    //   { 't1.micro': 3 } // Create three t1.micro instances
    //   { 't2.micro': -1} // Destroy one t2.micro instance
    //   ...
    // }
    requiredInstances.forEach(function(requiredInstance) {
      var type = requiredInstance.type;
      var required = requiredInstance.count;
      var currently = 0;
      if (currentInstances[type] !== undefined) {
        currently = currentInstances[type].ids.length;
      }
      diff[type] = required - currently;
    });

    // Iterate over diff and create/destroy instances as necessary.
    Object.keys(diff).forEach(function(instanceType) {
      var typeDiff = diff[instanceType];

      // Destroy instances
      if (typeDiff < 0) {
        var destroyed = 0;
        currentInstances[instanceType].ids.forEach(function(id) {
          if (destroyed++ < Math.abs(typeDiff)) {
            InstancePool.terminate(id, function(instanceId) {
              server.log('info', 'Terminating ' + instanceType + ' instance ' + id);
            });
          }
        });
      }

      // Create instancess
      if (typeDiff > 0) {
        var created = 0;
        if (currentInstances[instanceType]) {
          created = currentInstances[instanceType].ids.length;
        }
        for (; created < typeDiff; created++) {
          var instanceId = InstancePool.create(instanceType, function(instanceId) {
            server.log('info', 'Starting new ' + instanceType + ' instance ' + instanceId);
          });
        }
      }

      // If typeDiff == 0, do nothing
    });

    server.log('info', 'Instance pool started successfully!');

    done();
  });
};

// Returns all instances that are currently in the instance pool.
// These instances are not removed from the pool.
InstancePool.prototype.getPoolInstances = function(callback) {
  var filters = [
    {
      Name: 'tag-key'
    , Values: ['smake']
    }
  , {
      Name: 'tag-value'
    , Values: ['pool']
    }
  , {
      Name: 'instance-state-name'
    , Values: ['running', 'pending']
    }
  ];

  this.getPoolInstancesByFilters(filters, function(instances) {
    if (instances === undefined) {
      callback(undefined);
    } else {
      var currentInstances = {};
      instances.forEach(function(instance) {
        var type = instance.InstanceType;
        var id = instance.InstanceId;

        // Define an object for the instance type if it doesn't already exist.
        if (currentInstances[type] === undefined) {
          currentInstances[type] = {
            ids: []
          };
        }

        // Add the instance id to a list.
        currentInstances[type].ids.push(id);
      });
      callback(currentInstances);
    }
  });
};

// Returns pool instances, grouped by type.
// Output is formatted as follows:
// {
//   running: [id1, id2, id3, ...]
// , pending: [id4, id5, ...]
// }
InstancePool.prototype.getPoolInstancesByState = function(type, callback) {
  var filters = [
    {
      Name: 'tag-key'
    , Values: ['smake']
    }
  , {
      Name: 'tag-value'
    , Values: ['pool']
    }
  , {
      Name: 'instance-state-name'
    , Values: ['running', 'pending']
    }
  , {
      Name: 'instance-type'
    , Values: [type]
    }
  ];

  this.getPoolInstancesByFilters(filters, function(instances) {
    var instancesByState = {
      running: []
    , pending: []
    };
    instances.forEach(function(instance) {
      var type = instance.InstanceType;
      var id = instance.InstanceId;
      var state = instance.State.Name;

      // Add the instanceId to a list depending on its state.
      instancesByState[state].push(id);
    });
    callback(instancesByState);
  });
};

// Returns all instances that match the specified filters.
// Note that this method may return instances that are not
// part of the instance pool.
InstancePool.prototype.getPoolInstancesByFilters = function(filters, callback) {
  // Get an instance of the specified type
  var params = {
    Filters: filters
  };

  // Query EC2 for resources
  ec2.describeInstances(params, function(err, data) {
    if (err) {
      console.log(err, err.stack);
      callback(undefined);
    } else {
      var reservations = data.Reservations;
      if (!reservations) {
        callback(undefined);
        return;
      }
      if (!reservations[0]) {
        callback(undefined);
        return;
      }
      var instances = [];
      reservations.forEach(function(reservation) {
        reservation.Instances.forEach(function(instance) {
          instances.push(instance);
        });
      });

      callback(instances);
    }
  });
};

// Gets an instance of the specified type from the instance pool.
// The returned instance will no longer be part of the pool after calling
// this method. Its replacement will be handled by the pooling
// strategy specified when the instance pool was initialized.
InstancePool.prototype.getInstance = function(instance, callback) {
  this.getPoolInstancesByState(instance.type, function(instances) {
    // Choose an instance to return:
    var toReturn;
    // Prefer running instances...
    if (instances.running.length > 0) {
      toReturn = instances.running[0];
    // ...but can also return pending instances, if necessary
    } else if (instances.pending.length > 0) {
      toReturn = instances.pending[0];
    //TODO: Handle case wherein no instances (running or pending) are available.
    }

    InstancePool.removeFromPool(toReturn, function() {
      poolingStrategy.notifyOfRemoval(toReturn);
      callback(toReturn);
    });
  });
};

module.exports = InstancePool;
<commit_msg>Can change pooling strategy programatically<commit_after>var request = require('request');
var AWS = require('aws-sdk');
var ec2 = new AWS.EC2({
  apiVersion: '2014-10-01'
, region: 'us-west-2'
});

var poolingStrategy;

function InstancePool(strategy) {

  // Default to naive strategy.
  if (typeof(strategy) === 'undefined') {
    strategy = 'NaiveStrategy';
  }
  var _strategy = require('./poolingStrategies/' + strategy);
  poolingStrategy = new _strategy();
}

InstancePool.terminate = function(instanceId, callback) {
  var params = {
    InstanceIds: [instanceId]
  };
  ec2.terminateInstances(params, function(err, data) {
    if (err) {
      console.log(err, data);
    } else {
      callback(instanceId);
    }
  });
};

InstancePool.create = function(type, callback) {
  // Get ami list
  var amis = require('./defaultAmis.json');

  // Define instance paramaters
  var params = {
    ImageId: amis[type]
  , InstanceType: type
  , KeyName: '123abc'
  , MinCount: 1
  , MaxCount: 1
  };

  // Create the instance.
  ec2.runInstances(params, function(err, data) {
    if (err) {
      console.log('There was a problem creating the instance.', err);
      return;
    }

    // Get the instance ID
    var instanceId = data.Instances[0].InstanceId;

    // Apply tags.
    var tags = {
      Resources: [instanceId]
    , Tags: [
        {
          Key: 'smake'
        , Value: 'pool'
        }
      ]
    };

    ec2.createTags(tags, function(err) {
      if (err) {
        console.log('There was a problem applying tags to instance ' + instanceId, err);
        callback(undefined);
      } else {
        if (callback) {
          callback(instanceId);
        }
      }
    });
  });
};

// Removes smake pool tags from the specified instance.
// The instanceId is passed to the callback function.
InstancePool.removeFromPool = function(instanceId, callback) {
  // Remove pool tags
  var params = {
    Resources: [instanceId]
  , Tags: [
      {
        Key: 'smake'
      }
    ]
  };

  ec2.deleteTags(params, function(err, data) {
    if (err) {
      console.log(err, err.stack);
    } else {
      callback(instanceId);
    }
  });
};

InstancePool.prototype.initialize = function(server, done) {
  // Get a list of required instance types.
  var requiredInstances = poolingStrategy.getRequiredInstances();

  // Get a list of all current pool instances.
  this.getPoolInstances(function(currentInstances) {
    if (currentInstances === undefined) {
      server.log('info', 'Did not find existing pool resources');
      // Create necessary instances.
      requiredInstances.forEach(function(requiredInstance) {
        var type = requiredInstance.type;
        var count = requiredInstance.count;
        for (var i = 0; i < count; i++) {
          InstancePool.create(type, function(instanceId) {
            server.log('info', 'Starting new ' + type + ' instance ' + instanceId);
          });
        }
      });
      done();
      return;
    }
    server.log('info', 'Assimilating existing pool resources...');

    // Compare required and current instance counts.
    var diff = {};
    // Below is an example diff object
    // {
    //   { 't1.micro': 3 } // Create three t1.micro instances
    //   { 't2.micro': -1} // Destroy one t2.micro instance
    //   ...
    // }
    requiredInstances.forEach(function(requiredInstance) {
      var type = requiredInstance.type;
      var required = requiredInstance.count;
      var currently = 0;
      if (currentInstances[type] !== undefined) {
        currently = currentInstances[type].ids.length;
      }
      diff[type] = required - currently;
    });

    // Iterate over diff and create/destroy instances as necessary.
    Object.keys(diff).forEach(function(instanceType) {
      var typeDiff = diff[instanceType];

      // Destroy instances
      if (typeDiff < 0) {
        var destroyed = 0;
        currentInstances[instanceType].ids.forEach(function(id) {
          if (destroyed++ < Math.abs(typeDiff)) {
            InstancePool.terminate(id, function(instanceId) {
              server.log('info', 'Terminating ' + instanceType + ' instance ' + id);
            });
          }
        });
      }

      // Create instancess
      if (typeDiff > 0) {
        var created = 0;
        if (currentInstances[instanceType]) {
          created = currentInstances[instanceType].ids.length;
        }
        for (; created < typeDiff; created++) {
          var instanceId = InstancePool.create(instanceType, function(instanceId) {
            server.log('info', 'Starting new ' + instanceType + ' instance ' + instanceId);
          });
        }
      }

      // If typeDiff == 0, do nothing
    });

    server.log('info', 'Instance pool started successfully!');

    done();
  });
};

// Returns all instances that are currently in the instance pool.
// These instances are not removed from the pool.
InstancePool.prototype.getPoolInstances = function(callback) {
  var filters = [
    {
      Name: 'tag-key'
    , Values: ['smake']
    }
  , {
      Name: 'tag-value'
    , Values: ['pool']
    }
  , {
      Name: 'instance-state-name'
    , Values: ['running', 'pending']
    }
  ];

  this.getPoolInstancesByFilters(filters, function(instances) {
    if (instances === undefined) {
      callback(undefined);
    } else {
      var currentInstances = {};
      instances.forEach(function(instance) {
        var type = instance.InstanceType;
        var id = instance.InstanceId;

        // Define an object for the instance type if it doesn't already exist.
        if (currentInstances[type] === undefined) {
          currentInstances[type] = {
            ids: []
          };
        }

        // Add the instance id to a list.
        currentInstances[type].ids.push(id);
      });
      callback(currentInstances);
    }
  });
};

// Returns pool instances, grouped by type.
// Output is formatted as follows:
// {
//   running: [id1, id2, id3, ...]
// , pending: [id4, id5, ...]
// }
InstancePool.prototype.getPoolInstancesByState = function(type, callback) {
  var filters = [
    {
      Name: 'tag-key'
    , Values: ['smake']
    }
  , {
      Name: 'tag-value'
    , Values: ['pool']
    }
  , {
      Name: 'instance-state-name'
    , Values: ['running', 'pending']
    }
  , {
      Name: 'instance-type'
    , Values: [type]
    }
  ];

  this.getPoolInstancesByFilters(filters, function(instances) {
    var instancesByState = {
      running: []
    , pending: []
    };
    instances.forEach(function(instance) {
      var type = instance.InstanceType;
      var id = instance.InstanceId;
      var state = instance.State.Name;

      // Add the instanceId to a list depending on its state.
      instancesByState[state].push(id);
    });
    callback(instancesByState);
  });
};

// Returns all instances that match the specified filters.
// Note that this method may return instances that are not
// part of the instance pool.
InstancePool.prototype.getPoolInstancesByFilters = function(filters, callback) {
  // Get an instance of the specified type
  var params = {
    Filters: filters
  };

  // Query EC2 for resources
  ec2.describeInstances(params, function(err, data) {
    if (err) {
      console.log(err, err.stack);
      callback(undefined);
    } else {
      var reservations = data.Reservations;
      if (!reservations) {
        callback(undefined);
        return;
      }
      if (!reservations[0]) {
        callback(undefined);
        return;
      }
      var instances = [];
      reservations.forEach(function(reservation) {
        reservation.Instances.forEach(function(instance) {
          instances.push(instance);
        });
      });

      callback(instances);
    }
  });
};

// Gets an instance of the specified type from the instance pool.
// The returned instance will no longer be part of the pool after calling
// this method. Its replacement will be handled by the pooling
// strategy specified when the instance pool was initialized.
InstancePool.prototype.getInstance = function(instance, callback) {
  this.getPoolInstancesByState(instance.type, function(instances) {
    // Choose an instance to return:
    var toReturn;
    // Prefer running instances...
    if (instances.running.length > 0) {
      toReturn = instances.running[0];
    // ...but can also return pending instances, if necessary
    } else if (instances.pending.length > 0) {
      toReturn = instances.pending[0];
    //TODO: Handle case wherein no instances (running or pending) are available.
    }

    InstancePool.removeFromPool(toReturn, function() {
      poolingStrategy.notifyOfRemoval(toReturn);
      callback(toReturn);
    });
  });
};

module.exports = InstancePool;
<|endoftext|>