<commit_before>var async = require('async');
var _ = require('underscore');
	_.str = require('underscore.string');
	_.mixin(_.str.exports());

module.exports = function(mongoose){

	var Router = function(options){
		this.options = _.defaults(options||{}, {
			initiator: 'api',
			context: 'routing'
		});
	};

	_.extend(Router.prototype, {

		route: function(options){
			options = _.extend(options||{}, this.options);

			var me = this;

			return function(req, res, next){
				me.follow(options).bind(me)(req, res, function(err){
					if(err){
						return next(err);
					}
					me.checkout(options).bind(me)(req, res, next);
				});
			};
		},

		follow: function(options) {
			options = _.extend(options||{}, this.options);

			var me = this;

			return function(req, res, next){
				var splitPath = req.route.path.split('/').slice(1);
				req.splitPath = splitPath;
				async.reduce(splitPath, null, function(obj, path, callback){

					if(path === options.initiator){
						return callback(null, me);
					}

					if(!obj){
						return callback('ROUTE_FOLLOW_NO_OBJECT_FOUND');
					}

					path = _(path).camelize();
					if(_(path).startsWith(':')){
						var key = path.slice(1);
						var value = req.params[key];
						if(_(key.toLowerCase()).endsWith('id')){
							key = 'id';
						}
						var path = {};
						path[key] = value;
					}

					if(typeof obj.sgRouteFollow === 'function'){
						obj.sgRouteFollow(path, me.getFollowOptions(req), callback);
					}
					else{
						callback(null, obj);
					}
				}, function(err, data){
					if(err){
						return next(err);
					}
					res.data = data;
					next();
				});
			}

		},

		checkout: function(options){
			options = _.extend(options||{}, this.options);

			var me = this;

			return function(req, res, next){
				if(!res.data){
					return next('ROUTE_CHECKOUT_MISSING_DATA');
				}

				var sgRouteCheckoutMeth = 'sgRouteCheckout' + _(req.method.toLowerCase()).capitalize();
				if(typeof res.data[sgRouteCheckoutMeth] === 'function'){
					res.data[sgRouteCheckoutMeth](this.getCheckoutOptions(req), function(err, checkoutData){
						if(err){
							return next(err);
						}
						res.data = checkoutData;
						next();
					});
				}
				else{
					next();
				}
			}
		},

		getFollowOptions: function(req, action){
			var options = {
				req: req,
				user: req.user
			};

			if(req.method === 'POST'){
				options.data = req.body;
			}
			else{
				options.data = req.query;
			}

			return options;
		},

		getCheckoutOptions: function(req, action){
			var options = {
				req: req,
				user: req.user
			};

			return options;
		},

		sgRouteFollow: function(path, options, callback){
			if(path === 'user'){
				return callback(null, options.user);
			}
			callback(null, this.getModelByCollectionName(path));
		},

		getModelByCollectionName: function(name){
			return this.getModelsByCollectionName()[name];
		},

		getModelsByCollectionName: function(){
			if(!this._modelsByCollectionName){
				this._modelsByCollectionName = {};
				var me = this;
				_.values(mongoose.models).forEach(function(model){
					me._modelsByCollectionName[model.collection.name] = model;
				});
			}
			return this._modelsByCollectionName;
		}

	});

	return Router;
};

<commit_msg>Options now passed to getModelByCollectionName() function.<commit_after>var async = require('async');
var _ = require('underscore');
	_.str = require('underscore.string');
	_.mixin(_.str.exports());

module.exports = function(mongoose){

	var Router = function(options){
		this.options = _.defaults(options||{}, {
			initiator: 'api',
			context: 'routing'
		});
	};

	_.extend(Router.prototype, {

		route: function(options){
			options = _.extend(options||{}, this.options);

			var me = this;

			return function(req, res, next){
				me.follow(options).bind(me)(req, res, function(err){
					if(err){
						return next(err);
					}
					me.checkout(options).bind(me)(req, res, next);
				});
			};
		},

		follow: function(options) {
			options = _.extend(options||{}, this.options);

			var me = this;

			return function(req, res, next){
				var splitPath = req.route.path.split('/').slice(1);
				req.splitPath = splitPath;
				async.reduce(splitPath, null, function(obj, path, callback){

					if(path === options.initiator){
						return callback(null, me);
					}

					if(!obj){
						return callback('ROUTE_FOLLOW_NO_OBJECT_FOUND');
					}

					path = _(path).camelize();
					if(_(path).startsWith(':')){
						var key = path.slice(1);
						var value = req.params[key];
						if(_(key.toLowerCase()).endsWith('id')){
							key = 'id';
						}
						var path = {};
						path[key] = value;
					}

					if(typeof obj.sgRouteFollow === 'function'){
						obj.sgRouteFollow(path, me.getFollowOptions(req), callback);
					}
					else{
						callback(null, obj);
					}
				}, function(err, data){
					if(err){
						return next(err);
					}
					res.data = data;
					next();
				});
			}

		},

		checkout: function(options){
			options = _.extend(options||{}, this.options);

			var me = this;

			return function(req, res, next){
				if(!res.data){
					return next('ROUTE_CHECKOUT_MISSING_DATA');
				}

				var sgRouteCheckoutMeth = 'sgRouteCheckout' + _(req.method.toLowerCase()).capitalize();
				if(typeof res.data[sgRouteCheckoutMeth] === 'function'){
					res.data[sgRouteCheckoutMeth](this.getCheckoutOptions(req), function(err, checkoutData){
						if(err){
							return next(err);
						}
						res.data = checkoutData;
						next();
					});
				}
				else{
					next();
				}
			}
		},

		getFollowOptions: function(req, action){
			var options = {
				req: req,
				user: req.user
			};

			if(req.method === 'POST'){
				options.data = req.body;
			}
			else{
				options.data = req.query;
			}

			return options;
		},

		getCheckoutOptions: function(req, action){
			var options = {
				req: req,
				user: req.user
			};

			return options;
		},

		sgRouteFollow: function(path, options, callback){
			if(path === 'user'){
				return callback(null, options.user);
			}
			callback(null, this.getModelByCollectionName(path, options));
		},

		getModelByCollectionName: function(name, options){
			return this.getModelsByCollectionName()[name];
		},

		getModelsByCollectionName: function(){
			if(!this._modelsByCollectionName){
				this._modelsByCollectionName = {};
				var me = this;
				_.values(mongoose.models).forEach(function(model){
					me._modelsByCollectionName[model.collection.name] = model;
				});
			}
			return this._modelsByCollectionName;
		}

	});

	return Router;
};

<|endoftext|>


<commit_before>'use strict';

var request = require('request');
var sf = require('sf');
var url = require('url');
var CircuitBreaker = require('./CircuitBreaker');

module.exports = getThenCache;

function getThenCache(options, config, cache, eventHandler, stream, onError) {
    if(!options.explicitNoCache && options.cacheTTL > 0) {
        var start = Date.now();

        cache.get(options.cacheKey, function(err, content, oldContent) {
            if (err) return onError(err, oldContent);
            if (content) {
                var timing = Date.now() - start;
                eventHandler.logger('info', 'CACHE ' + options.cacheKey,{tracer:options.tracer, responseTime: timing, pcType:options.type});
                eventHandler.logger('debug', 'CACHE HIT for key: ' + options.cacheKey,{tracer:options.tracer,pcType:options.type});
                eventHandler.stats('increment', options.statsdKey + '.cacheHit');
                stream.end(content);
                return;
            }

            eventHandler.logger('debug', 'CACHE MISS for key: ' + options.cacheKey,{tracer:options.tracer,pcType:options.type});
            eventHandler.stats('increment', options.statsdKey + '.cacheMiss');

            if(options.url == 'cache') {
                stream.end("");
                return;
            }

            CircuitBreaker(options, config, eventHandler, pipeAndCacheContent, function(err, content) {
                if (err) return onError(err, oldContent);
                stream.end(content);
                cache.set(options.cacheKey, content, options.cacheTTL, function(err) {
                    eventHandler.logger('debug', 'CACHE SET for key: ' + options.cacheKey + ' @ TTL: ' + options.cacheTTL,{tracer:options.tracer,pcType:options.type});
                });
            });
        });

    } else {

        CircuitBreaker(options, config, eventHandler, pipeAndCacheContent, function(err, content) {
            if (err) return onError(err);
            stream.end(content);
        });

    }

    function pipeAndCacheContent(next) {

        var content = "", start = Date.now(), inErrorState = false;

        if(!url.parse(options.url).protocol) return handleError({message:'Invalid URL ' + options.url});

        var r = request({url: options.url, agent: false, timeout: options.timeout, headers: options.headers})
            .on('error', handleError)
            .on('data', function(data) {
                content += data.toString();
            })
            .on('response', function(response) {
                if(response.statusCode != 200) {
                    handleError({message:'status code ' + response.statusCode},response.statusCode)
                }
            })
            .on('end', function() {
                if(inErrorState) return;
                next(null, content);
                var timing = Date.now() - start;
                eventHandler.logger('info', 'OK ' + options.url,{tracer:options.tracer, responseTime: timing, pcType:options.type});
                eventHandler.stats('timing', options.statsdKey + '.responseTime', timing);
            });

        function handleError(err, statusCode) {
            if (!inErrorState) {
                inErrorState = true;
                next({statusCode: statusCode, message: err.message});
            }
        }

    }

}
<commit_msg>Final tweaks to make it pass tests.<commit_after>'use strict';

var request = require('request');
var sf = require('sf');
var url = require('url');
var CircuitBreaker = require('./CircuitBreaker');

module.exports = getThenCache;

function getThenCache(options, config, cache, eventHandler, stream, onError) {
    if(!options.explicitNoCache && options.cacheTTL > 0) {
        var start = Date.now();

        cache.get(options.cacheKey, function(err, content, oldContent) {
            if (err) return onError(err, oldContent);
            if (content) {
                var timing = Date.now() - start;
                eventHandler.logger('info', 'CACHE ' + options.cacheKey,{tracer:options.tracer, responseTime: timing, pcType:options.type});
                eventHandler.logger('debug', 'CACHE HIT for key: ' + options.cacheKey,{tracer:options.tracer,pcType:options.type});
                eventHandler.stats('increment', options.statsdKey + '.cacheHit');
                stream.end(content);
                return;
            }

            eventHandler.logger('debug', 'CACHE MISS for key: ' + options.cacheKey,{tracer:options.tracer,pcType:options.type});
            eventHandler.stats('increment', options.statsdKey + '.cacheMiss');

            if(options.url == 'cache') {
                stream.end("");
                return;
            }

            CircuitBreaker(options, config, eventHandler, pipeAndCacheContent, function(err, content) {
                if (err) return onError(err, oldContent);
                stream.end(content);
                cache.set(options.cacheKey, content, options.cacheTTL, function(err) {
                    eventHandler.logger('debug', 'CACHE SET for key: ' + options.cacheKey + ' @ TTL: ' + options.cacheTTL,{tracer:options.tracer,pcType:options.type});
                });
            });
        });

    } else {

        CircuitBreaker(options, config, eventHandler, pipeAndCacheContent, function(err, content) {
            if (err) return onError(err);
            stream.end(content);
        });

    }

    function pipeAndCacheContent(next) {

        var content = "", start = Date.now(), inErrorState = false;

        if(!url.parse(options.url).protocol) return handleError({message:'Invalid URL ' + options.url});

        var r = request({url: options.url, agent: false, timeout: options.timeout, headers: options.headers})
            .on('error', handleError)
            .on('data', function(data) {
                content += data.toString();
            })
            .on('response', function(response) {
                if(response.statusCode != 200) {
                    handleError({message:'status code ' + response.statusCode},response.statusCode)
                }
            })
            .on('end', function() {
                if(inErrorState) return;
                next(null, content);
                var timing = Date.now() - start;
                eventHandler.logger('info', 'OK ' + options.url,{tracer:options.tracer, responseTime: timing, pcType:options.type});
                eventHandler.stats('timing', options.statsdKey + '.responseTime', timing);
            });

        function handleError(err, statusCode) {
            if (!inErrorState) {
                inErrorState = true;
                var message = sf('Service {url} FAILED due to {errorMessage}', {
                    url: options.url,
                    errorMessage: err.message
                });
                next({statusCode: statusCode, message: message});
            }
        }

    }

}
<|endoftext|>


<commit_before>/**
 * @author David Corvoysier / Copyright Orange 2013
 *
 * Adapted from glfx.js, Copyright (C) 2011 by Evan Wallace
 *
 */
(function(global) {

    var defaultVertexSource = '\
    attribute vec2 vertex;\
    attribute vec2 _texCoord;\
    varying vec2 texCoord;\
    void main() {\
        texCoord = _texCoord;\
        gl_Position = vec4(vertex * 2.0 - 1.0, 0.0, 1.0);\
    }';

    var defaultFragmentSource = '\
    uniform sampler2D texture;\
    varying vec2 texCoord;\
    void main() {\
        gl_FragColor = texture2D(texture, texCoord);\
    }';

    function compileSource(gl,type, source) {
        var shader = gl.createShader(type);
        gl.shaderSource(shader, source);
        gl.compileShader(shader);
        if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
            throw 'compile error: ' + gl.getShaderInfoLog(shader);
        }
        return shader;
    }

    var Filter = function (canvas, vertexSource, fragmentSource, callback) {
        // Store WebGL context
        var gl = canvas.gl;
        var _callback = callback;
        // Create the filter program
        var _program = gl.createProgram();
        vertexSource = vertexSource || defaultVertexSource;
        fragmentSource = fragmentSource || defaultFragmentSource;
        fragmentSource = 'precision highp float;' + fragmentSource; // annoying requirement is annoying
        gl.attachShader(_program, compileSource(gl,gl.VERTEX_SHADER, vertexSource));
        gl.attachShader(_program, compileSource(gl,gl.FRAGMENT_SHADER, fragmentSource));
        gl.linkProgram(_program);
        if (!gl.getProgramParameter(_program, gl.LINK_STATUS)) {
            throw 'link error: ' + gl.getProgramInfoLog(_program);
        }
        // Create buffer coordinates
        var _vertexBuffer = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, _vertexBuffer);
        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ 0, 0, 0, 1, 1, 0, 1, 1 ]), gl.STATIC_DRAW);
        var _texCoordBuffer = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, _texCoordBuffer);
        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ 0, 0, 0, 1, 1, 0, 1, 1 ]), gl.STATIC_DRAW);
        var _invCoordBuffer = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, _invCoordBuffer);
        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ 0, 1, 0, 0, 1, 1, 1, 0 ]), gl.STATIC_DRAW);
        // Create a framebuffer for this filter
        var _fb = gl.createFramebuffer();

        return {
            run : function (frameIn,frameOut) {
                // Bind the framebuffer
                if(frameOut)
                    gl.bindFramebuffer(gl.FRAMEBUFFER, _fb);

                // Set the frameIn texture as input
                gl.activeTexture(gl.TEXTURE0);
                gl.bindTexture(gl.TEXTURE_2D, frameIn.texture);

                // Set coordinates
                var vertexAttribute = gl.getAttribLocation(_program, 'vertex');
                gl.enableVertexAttribArray(vertexAttribute);
                var texCoordAttribute = gl.getAttribLocation(_program, '_texCoord');
                gl.enableVertexAttribArray(texCoordAttribute);
                gl.useProgram(_program);
                gl.bindBuffer(gl.ARRAY_BUFFER, _vertexBuffer);
                gl.vertexAttribPointer(vertexAttribute, 2, gl.FLOAT, false, 0, 0);
                if(frameOut)
                    gl.bindBuffer(gl.ARRAY_BUFFER, _texCoordBuffer);
                else
                    gl.bindBuffer(gl.ARRAY_BUFFER, _invCoordBuffer);
                gl.vertexAttribPointer(texCoordAttribute, 2, gl.FLOAT, false, 0, 0);

                // Use the first texture
                var texLoc = gl.getUniformLocation(_program, 'texture');
                gl.uniform1i(texLoc, 0);

                // This callback allows the filter to be specialized
                if(_callback) {
                    // Get the extra parameters we may have received
                    var args = Array.prototype.slice.call(arguments);
                    // Add gl & program parameters at the beginning
                    args.splice(0, 0, gl, _program);
                    // Call our callback
                    _callback.apply (this, args);
                }

                if(frameOut && frameOut.texture) {
                    // Set the frameOut texture as output
                    gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, frameOut.texture, 0);
                    if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) !== gl.FRAMEBUFFER_COMPLETE) {
                        throw new Error('incomplete framebuffer');
                    }
                }

                // Draw
                gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);

                // Flip results
                gl.bindFramebuffer(gl.FRAMEBUFFER, null);
            }
        }
    };

    _filters = new Array();

    global.addFilter = function (name, vertexSource, fragmentSource, callback) {
        global[name] = function () {
            _filters[name] = _filters[name] || new Filter(global.canvas(), vertexSource, fragmentSource, callback);
            var args = Array.prototype.slice.call(arguments);
            _filters[name].run.apply (this, args);
        };
    };

    // Register our default filter, a simple carbon-copy
    global.addFilter('copy');

})(glimp);
<commit_msg>glimp_filter: use a single framebuffer for all filters<commit_after>/**
 * @author David Corvoysier / Copyright Orange 2013
 *
 * Adapted from glfx.js, Copyright (C) 2011 by Evan Wallace
 *
 */
(function(global) {

    var defaultVertexSource = '\
    attribute vec2 vertex;\
    attribute vec2 _texCoord;\
    varying vec2 texCoord;\
    void main() {\
        texCoord = _texCoord;\
        gl_Position = vec4(vertex * 2.0 - 1.0, 0.0, 1.0);\
    }';

    var defaultFragmentSource = '\
    uniform sampler2D texture;\
    varying vec2 texCoord;\
    void main() {\
        gl_FragColor = texture2D(texture, texCoord);\
    }';

    function compileSource(gl,type, source) {
        var shader = gl.createShader(type);
        gl.shaderSource(shader, source);
        gl.compileShader(shader);
        if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
            throw 'compile error: ' + gl.getShaderInfoLog(shader);
        }
        return shader;
    }

    // A framebuffer used to output filters results to a texture
    var _fb;

    var Filter = function (canvas, vertexSource, fragmentSource, callback) {
        // Store WebGL context
        var gl = canvas.gl;
        var _callback = callback;
        // Create the filter program
        var _program = gl.createProgram();
        vertexSource = vertexSource || defaultVertexSource;
        fragmentSource = fragmentSource || defaultFragmentSource;
        fragmentSource = 'precision highp float;' + fragmentSource; // annoying requirement is annoying
        gl.attachShader(_program, compileSource(gl,gl.VERTEX_SHADER, vertexSource));
        gl.attachShader(_program, compileSource(gl,gl.FRAGMENT_SHADER, fragmentSource));
        gl.linkProgram(_program);
        if (!gl.getProgramParameter(_program, gl.LINK_STATUS)) {
            throw 'link error: ' + gl.getProgramInfoLog(_program);
        }
        // Create buffer coordinates
        var _vertexBuffer = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, _vertexBuffer);
        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ 0, 0, 0, 1, 1, 0, 1, 1 ]), gl.STATIC_DRAW);
        var _texCoordBuffer = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, _texCoordBuffer);
        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ 0, 0, 0, 1, 1, 0, 1, 1 ]), gl.STATIC_DRAW);
        var _invCoordBuffer = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, _invCoordBuffer);
        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ 0, 1, 0, 0, 1, 1, 1, 0 ]), gl.STATIC_DRAW);

        return {
            run : function (frameIn,frameOut) {
                // Bind the framebuffer
                if(frameOut){
                    _fb = _fb || gl.createFramebuffer();
                    gl.bindFramebuffer(gl.FRAMEBUFFER, _fb);
                }

                // Set the frameIn texture as input
                gl.activeTexture(gl.TEXTURE0);
                gl.bindTexture(gl.TEXTURE_2D, frameIn.texture);

                // Set coordinates
                var vertexAttribute = gl.getAttribLocation(_program, 'vertex');
                gl.enableVertexAttribArray(vertexAttribute);
                var texCoordAttribute = gl.getAttribLocation(_program, '_texCoord');
                gl.enableVertexAttribArray(texCoordAttribute);
                gl.useProgram(_program);
                gl.bindBuffer(gl.ARRAY_BUFFER, _vertexBuffer);
                gl.vertexAttribPointer(vertexAttribute, 2, gl.FLOAT, false, 0, 0);
                if(frameOut)
                    gl.bindBuffer(gl.ARRAY_BUFFER, _texCoordBuffer);
                else
                    gl.bindBuffer(gl.ARRAY_BUFFER, _invCoordBuffer);
                gl.vertexAttribPointer(texCoordAttribute, 2, gl.FLOAT, false, 0, 0);

                // Use the first texture
                var texLoc = gl.getUniformLocation(_program, 'texture');
                gl.uniform1i(texLoc, 0);

                // This callback allows the filter to be specialized
                if(_callback) {
                    // Get the extra parameters we may have received
                    var args = Array.prototype.slice.call(arguments);
                    // Add gl & program parameters at the beginning
                    args.splice(0, 0, gl, _program);
                    // Call our callback
                    _callback.apply (this, args);
                }

                if(frameOut && frameOut.texture) {
                    // Set the frameOut texture as output
                    gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, frameOut.texture, 0);
                    if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) !== gl.FRAMEBUFFER_COMPLETE) {
                        throw new Error('incomplete framebuffer');
                    }
                }

                // Draw
                gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);

                // Flip results
                gl.bindFramebuffer(gl.FRAMEBUFFER, null);
            }
        }
    };

    _filters = new Array();

    global.addFilter = function (name, vertexSource, fragmentSource, callback) {
        global[name] = function () {
            _filters[name] = _filters[name] || new Filter(global.canvas(), vertexSource, fragmentSource, callback);
            var args = Array.prototype.slice.call(arguments);
            _filters[name].run.apply (this, args);
        };
    };

    // Register our default filter, a simple carbon-copy
    global.addFilter('copy');

})(glimp);
<|endoftext|>


<commit_before>/*---------------------------------------------------------------------------*\
 |  Search.js                                                                |
 |                                                                           |
 |  Copyright © 2017, Rajiv Bakulesh Shah, original author.                  |
 |                                                                           |
 |      This program is free software: you can redistribute it and/or modify |
 |      it under the terms of the GNU General Public License as published by |
 |      the Free Software Foundation, either version 3 of the License, or    |
 |      (at your option) any later version.                                  |
 |                                                                           |
 |      This program is distributed in the hope that it will be useful, but  |
 |      WITHOUT ANY WARRANTY; without even the implied warranty of           |
 |      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU    |
 |      General Public License for more details.                             |
 |                                                                           |
 |      You should have received a copy of the GNU General Public License    |
 |      along with this program.  If not, see:                               |
 |          <http://www.gnu.org/licenses/>                                   |
\*---------------------------------------------------------------------------*/

import React from 'react';
import { Link, withRouter } from 'react-router-dom';
import './Search.css';
import './monkey';

const querystring = require('querystring');

class Search extends React.PureComponent {
  constructor(props) {
    super(props);
    this.onSubmit = this.onSubmit.bind(this);
    this.updateState = this.updateState.bind(this);
    this.state = {
      query: this.props.query || '',
      results: this.props.results || [],
    };
  }

  componentWillReceiveProps(nextProps) {
    this.setState({
      query: nextProps.query || '',
      results: nextProps.results || [],
    });
  }

  onSubmit(eventObject) {
    eventObject.preventDefault();
    this.refs.results.redirect();
  }

  updateState(nextState) {
    this.setState(nextState);
  }

  render() {
    return (
      <form className="Search" onSubmit={this.onSubmit}>
        <Precache />
        <fieldset>
          <Input query={this.state.query} updateState={this.updateState} />
        </fieldset>
        <Results
          results={this.state.results}
          history={this.props.history}
          ref="results"
        />
      </form>
    );
  }
}

class Precache extends React.PureComponent {
  componentDidMount() {
    this.getQueries();
  }

  getQueries() {
    fetch(`${process.env.REACT_APP_API}/queries`)
      .then(response => response.json())
      .then(data => this.cacheQueries(data.queries))
      .catch(error => console.log(error));
  }

  cacheQueries(queries) {
    if (queries.length) {
      const query = queries.shift();
      const urlQueryString = querystring.stringify({q: query});
      fetch(`${process.env.REACT_APP_API}/songs/search?${urlQueryString}`)
        .then(() => this.cacheQueries(queries))
        .catch(error => {
          console.log(error);
          this.cacheQueries(queries);
        });
    }
  }

  render() {
    return null;
  }
}

class Input extends React.PureComponent {
  constructor(props) {
    super(props);
    this.GTFO_KEYS = [27];
    this.onKeyDown = this.onKeyDown.bind(this);
    this.onKeyPress = this.onKeyPress.bind(this);
    this.onFocus = this.onFocus.bind(this);
    this.onBlur = this.onBlur.bind(this);
    this.onChange = this.onChange.bind(this);
    this.input = null;
    this.video = null;
  }

  componentDidMount() {
    document.addEventListener('keydown', this.onKeyDown);
    document.addEventListener('keypress', this.onKeyPress);
    this.input = document.querySelectorAll('input[type=search]')[0];
  }

  componentDidUpdate() {
    if (!this.props.query) {
      this.input.blur();
    }
  }

  onKeyDown(eventObject) {
    if (document.activeElement !== this.input && this.GTFO_KEYS.includes(eventObject.which)) {
      window.location.href = '/gtfo';
    }
  }

  onKeyPress(eventObject) {
    const c = String.fromCharCode(eventObject.which);
    if (c && /^[0-9a-z]+$/i.test(c) && document.activeElement !== this.input) {
      this.input.focus();
    }
  }

  onFocus() {
    const videos = document.getElementsByClassName('Playing');
    if (videos.length) {
      this.video = videos[0];
      this.video.classList.remove('Playing');
    }
  }

  onBlur() {
    if (this.video) {
      this.video.classList.add('Playing');
    };
  }

  onChange(eventObject) {
    const query = eventObject.target.value;
    this.props.updateState({query: query});
    if (query) {
      const urlQueryString = querystring.stringify({q: query});
      fetch(`${process.env.REACT_APP_API}/songs/search?${urlQueryString}`)
        .then(response => response.json())
        .then(data => this.props.updateState({results: data.songs}))
        .catch(error => console.log(error));
      const formData = new FormData();
      formData.append('q', query);
      fetch(`${process.env.REACT_APP_API}/queries`, {method: 'POST', body: formData})
        .then(response => response.json())
        .then(data => console.log(data))
        .catch(error => console.log(error));
    } else {
      this.props.updateState({results: []});
    }
  }

  render() {
    return (
      <input
        type="search"
        placeholder="Search"
        maxLength="20"
        autoComplete="off"
        spellCheck="false"
        value={this.props.query}
        onFocus={this.onFocus}
        onBlur={this.onBlur}
        onChange={this.onChange}
      />
    );
  }
}

class Results extends React.PureComponent {
  constructor(props) {
    super(props);
    this.UP_KEYS = [38];
    this.DOWN_KEYS = [40];
    this.onKeyDown = this.onKeyDown.bind(this);
    this.onKeyUp = this.onKeyUp.bind(this);
    this.updateSelected = this.updateSelected.bind(this);
    this.state = {selected: null};
  }

  componentDidMount() {
    document.addEventListener('keydown', this.onKeyDown);
    document.addEventListener('keyup', this.onKeyUp);
  }

  componentWillReceiveProps() {
    this.setState({selected: null});
  }

  onKeyDown(eventObject) {
    if ((this.props.results.length && this.UP_KEYS.includes(eventObject.which)) || this.DOWN_KEYS.includes(eventObject.which)) {
      eventObject.preventDefault();
    }
  }

  onKeyUp(eventObject) {
    if (this.props.results.length) {
      if (this.UP_KEYS.includes(eventObject.which)) {
        this.updateSelected(-1);
      } else if (this.DOWN_KEYS.includes(eventObject.which)) {
        this.updateSelected(1);
      }
    }
  }

  updateSelected(direction) {
    let selected = this.state.selected;
    if (selected === null) {
      selected = -0.5 * direction - 0.5;
    }
    selected += direction;
    selected += this.props.results.length;
    selected %= this.props.results.length;
    this.setState({selected: selected});
  }

  redirect() {
    if (this.refs.result) {
      this.props.history.push(this.refs.result.target);
    }
  }

  render() {
    const items = [];
    for (let index = 0; index < this.props.results.length; index++) {
      const result = this.props.results[index];
      const key = result._id;
      const selected = index === this.state.selected;
      const ref = index === this.state.selected ? 'result' : null;
      const item = (
        <Result key={key} result={result} selected={selected} ref={ref} />
      );
      items.push(item);
    }
    return
{items}
;
  }
}

class Result extends React.PureComponent {
  constructor(props) {
    super(props);
    const {artist_id, song_id} = this.props.result;
    this.target = `/${artist_id}/${song_id}`;
  }

  componentWillReceiveProps(nextProps) {
    const {artist_id, song_id} = nextProps.result;
    this.target = `/${artist_id}/${song_id}`;
  }

  render() {
    const style = {textDecoration: this.props.selected ? 'underline' : null};
    const {artist, song} = this.props.result;
    const html = `${artist} — ${song}`;
    const title = html.htmlToText();
    return (

        <Link
          to={this.target}
          style={style}
          dangerouslySetInnerHTML={{__html: html}}
          title={title}
        />

    );
  }
}

export default withRouter(Search);
<commit_msg>Don't process API /v1/queries POST responses<commit_after>/*---------------------------------------------------------------------------*\
 |  Search.js                                                                |
 |                                                                           |
 |  Copyright © 2017, Rajiv Bakulesh Shah, original author.                  |
 |                                                                           |
 |      This program is free software: you can redistribute it and/or modify |
 |      it under the terms of the GNU General Public License as published by |
 |      the Free Software Foundation, either version 3 of the License, or    |
 |      (at your option) any later version.                                  |
 |                                                                           |
 |      This program is distributed in the hope that it will be useful, but  |
 |      WITHOUT ANY WARRANTY; without even the implied warranty of           |
 |      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU    |
 |      General Public License for more details.                             |
 |                                                                           |
 |      You should have received a copy of the GNU General Public License    |
 |      along with this program.  If not, see:                               |
 |          <http://www.gnu.org/licenses/>                                   |
\*---------------------------------------------------------------------------*/

import React from 'react';
import { Link, withRouter } from 'react-router-dom';
import './Search.css';
import './monkey';

const querystring = require('querystring');

class Search extends React.PureComponent {
  constructor(props) {
    super(props);
    this.onSubmit = this.onSubmit.bind(this);
    this.updateState = this.updateState.bind(this);
    this.state = {
      query: this.props.query || '',
      results: this.props.results || [],
    };
  }

  componentWillReceiveProps(nextProps) {
    this.setState({
      query: nextProps.query || '',
      results: nextProps.results || [],
    });
  }

  onSubmit(eventObject) {
    eventObject.preventDefault();
    this.refs.results.redirect();
  }

  updateState(nextState) {
    this.setState(nextState);
  }

  render() {
    return (
      <form className="Search" onSubmit={this.onSubmit}>
        <Precache />
        <fieldset>
          <Input query={this.state.query} updateState={this.updateState} />
        </fieldset>
        <Results
          results={this.state.results}
          history={this.props.history}
          ref="results"
        />
      </form>
    );
  }
}

class Precache extends React.PureComponent {
  componentDidMount() {
    this.getQueries();
  }

  getQueries() {
    fetch(`${process.env.REACT_APP_API}/queries`)
      .then(response => response.json())
      .then(data => this.cacheQueries(data.queries))
      .catch(error => console.log(error));
  }

  cacheQueries(queries) {
    if (queries.length) {
      const query = queries.shift();
      const urlQueryString = querystring.stringify({q: query});
      fetch(`${process.env.REACT_APP_API}/songs/search?${urlQueryString}`)
        .then(() => this.cacheQueries(queries))
        .catch(error => {
          console.log(error);
          this.cacheQueries(queries);
        });
    }
  }

  render() {
    return null;
  }
}

class Input extends React.PureComponent {
  constructor(props) {
    super(props);
    this.GTFO_KEYS = [27];
    this.onKeyDown = this.onKeyDown.bind(this);
    this.onKeyPress = this.onKeyPress.bind(this);
    this.onFocus = this.onFocus.bind(this);
    this.onBlur = this.onBlur.bind(this);
    this.onChange = this.onChange.bind(this);
    this.input = null;
    this.video = null;
  }

  componentDidMount() {
    document.addEventListener('keydown', this.onKeyDown);
    document.addEventListener('keypress', this.onKeyPress);
    this.input = document.querySelectorAll('input[type=search]')[0];
  }

  componentDidUpdate() {
    if (!this.props.query) {
      this.input.blur();
    }
  }

  onKeyDown(eventObject) {
    if (document.activeElement !== this.input && this.GTFO_KEYS.includes(eventObject.which)) {
      window.location.href = '/gtfo';
    }
  }

  onKeyPress(eventObject) {
    const c = String.fromCharCode(eventObject.which);
    if (c && /^[0-9a-z]+$/i.test(c) && document.activeElement !== this.input) {
      this.input.focus();
    }
  }

  onFocus() {
    const videos = document.getElementsByClassName('Playing');
    if (videos.length) {
      this.video = videos[0];
      this.video.classList.remove('Playing');
    }
  }

  onBlur() {
    if (this.video) {
      this.video.classList.add('Playing');
    };
  }

  onChange(eventObject) {
    const query = eventObject.target.value;
    this.props.updateState({query: query});
    if (query) {
      const urlQueryString = querystring.stringify({q: query});
      fetch(`${process.env.REACT_APP_API}/songs/search?${urlQueryString}`)
        .then(response => response.json())
        .then(data => this.props.updateState({results: data.songs}))
        .catch(error => console.log(error));
      const formData = new FormData();
      formData.append('q', query);
      fetch(`${process.env.REACT_APP_API}/queries`, {method: 'POST', body: formData})
        .catch(error => console.log(error));
    } else {
      this.props.updateState({results: []});
    }
  }

  render() {
    return (
      <input
        type="search"
        placeholder="Search"
        maxLength="20"
        autoComplete="off"
        spellCheck="false"
        value={this.props.query}
        onFocus={this.onFocus}
        onBlur={this.onBlur}
        onChange={this.onChange}
      />
    );
  }
}

class Results extends React.PureComponent {
  constructor(props) {
    super(props);
    this.UP_KEYS = [38];
    this.DOWN_KEYS = [40];
    this.onKeyDown = this.onKeyDown.bind(this);
    this.onKeyUp = this.onKeyUp.bind(this);
    this.updateSelected = this.updateSelected.bind(this);
    this.state = {selected: null};
  }

  componentDidMount() {
    document.addEventListener('keydown', this.onKeyDown);
    document.addEventListener('keyup', this.onKeyUp);
  }

  componentWillReceiveProps() {
    this.setState({selected: null});
  }

  onKeyDown(eventObject) {
    if ((this.props.results.length && this.UP_KEYS.includes(eventObject.which)) || this.DOWN_KEYS.includes(eventObject.which)) {
      eventObject.preventDefault();
    }
  }

  onKeyUp(eventObject) {
    if (this.props.results.length) {
      if (this.UP_KEYS.includes(eventObject.which)) {
        this.updateSelected(-1);
      } else if (this.DOWN_KEYS.includes(eventObject.which)) {
        this.updateSelected(1);
      }
    }
  }

  updateSelected(direction) {
    let selected = this.state.selected;
    if (selected === null) {
      selected = -0.5 * direction - 0.5;
    }
    selected += direction;
    selected += this.props.results.length;
    selected %= this.props.results.length;
    this.setState({selected: selected});
  }

  redirect() {
    if (this.refs.result) {
      this.props.history.push(this.refs.result.target);
    }
  }

  render() {
    const items = [];
    for (let index = 0; index < this.props.results.length; index++) {
      const result = this.props.results[index];
      const key = result._id;
      const selected = index === this.state.selected;
      const ref = index === this.state.selected ? 'result' : null;
      const item = (
        <Result key={key} result={result} selected={selected} ref={ref} />
      );
      items.push(item);
    }
    return
{items}
;
  }
}

class Result extends React.PureComponent {
  constructor(props) {
    super(props);
    const {artist_id, song_id} = this.props.result;
    this.target = `/${artist_id}/${song_id}`;
  }

  componentWillReceiveProps(nextProps) {
    const {artist_id, song_id} = nextProps.result;
    this.target = `/${artist_id}/${song_id}`;
  }

  render() {
    const style = {textDecoration: this.props.selected ? 'underline' : null};
    const {artist, song} = this.props.result;
    const html = `${artist} — ${song}`;
    const title = html.htmlToText();
    return (

        <Link
          to={this.target}
          style={style}
          dangerouslySetInnerHTML={{__html: html}}
          title={title}
        />

    );
  }
}

export default withRouter(Search);
<|endoftext|>


<commit_before>const JSONRPC = {};
JSONRPC.ClientPluginBase = require("../../ClientPluginBase");
JSONRPC.Utils = require("../../Utils");

const assert = require("assert");

module.exports =
/**
 *  this.rejectAllPromises(error) has to be called manually in a browser environment when a Worker is terminated or has finished working.
 */
class WorkerTransport extends JSONRPC.ClientPluginBase
{
	/**
	 * @param {Worker} worker
	 * @param {boolean|undefined} bBidirectionalWorkerMode
	 */
	constructor(worker, bBidirectionalWorkerMode)
	{
		super();

		// JSONRPC call ID as key, {promise: {Promise}, fnResolve: {Function}, fnReject: {Function}, outgoingRequest: {OutgoingRequest}} as values.
		this._objWorkerRequestsPromises = {};

		this._bBidirectionalWorkerMode = !!bBidirectionalWorkerMode;
		this._worker = worker;

		this._setupWorker();
	}

	/**
	 * @returns {Worker}
	 */
	get worker()
	{
		return this._worker;
	}

	/**
	 * @param {JSONRPC.OutgoingRequest} outgoingRequest
	 */
	async beforeJSONEncode(outgoingRequest)
	{
		// No serialization.
		outgoingRequest.requestBody = outgoingRequest.requestObject;
	}

	/**
	 * objResponse is the object obtained after JSON parsing for strResponse.
	 *
	 * @param {Object|undefined} objResponse
	 */
	async processResponse(objResponse)
	{
		if(
			(
				typeof objResponse.id !== "number"
				&& typeof objResponse.id !== "string"
			)
			|| !this._objWorkerRequestsPromises[objResponse.id]
		)
		{
			console.error(new Error("Couldn't find JSONRPC response call ID in this._objWorkerRequestsPromises. RAW response: " + JSON.stringify(objResponse)));
			console.error(new Error("RAW remote message: " + JSON.stringify(objResponse)));
			console.log("[" + process.pid + "] Unclean state. Unable to match Worker message to an existing Promise or qualify it as a request.");

			if(this.worker.terminate)
			{
				this.worker.terminate();
			}
			else if(this.worker !== process)
			{
				this.worker.kill();
			}

			return;
		}

		this._objWorkerRequestsPromises[objResponse.id].outgoingRequest.responseBody = objResponse;
		this._objWorkerRequestsPromises[objResponse.id].outgoingRequest.responseObject = objResponse;

		this._objWorkerRequestsPromises[objResponse.id].fnResolve(null);
		// Sorrounding code will parse the result and throw if necessary. fnReject is not going to be used in this function.

		delete this._objWorkerRequestsPromises[objResponse.id];
	}

	/**
	 * Populates the the OutgoingRequest class instance (outgoingRequest) with the RAW JSON response and the JSON parsed response object.
	 *
	 * @param {JSONRPC.OutgoingRequest} outgoingRequest
	 *
	 * @returns {Promise.<null>}
	 */
	async makeRequest(outgoingRequest)
	{
		if(outgoingRequest.isMethodCalled)
		{
			return;
		}

		if(this.worker.isDead && this.worker.isDead())
		{
			throw new Error("Worker not connected.");
		}

		outgoingRequest.isMethodCalled = true;

		if(outgoingRequest.isNotification)
		{
			// JSONRPC 2.0 notification requests don't have the id property at all, not even null. JSONRPC 2.0 servers do not send a response at all for these types of requests.
		}
		else
		{
			/**
			 * http://www.jsonrpc.org/specification#notification
			 *
			 * id
			 * An identifier established by the Client that MUST contain a String, Number, or NULL value if included. If it is not included it is assumed to be a notification. The value SHOULD normally not be Null [1] and Numbers SHOULD NOT contain fractional parts [2]
			 * The Server MUST reply with the same value in the Response object if included. This member is used to correlate the context between the two objects.
			 *
			 * [1] The use of Null as a value for the id member in a Request object is discouraged, because this specification uses a value of Null for Responses with an unknown id. Also, because JSON-RPC 1.0 uses an id value of Null for Notifications this could cause confusion in handling.
			 *
			 * [2] Fractional parts may be problematic, since many decimal fractions cannot be represented exactly as binary fractions.
			 *
			 * =====================================
			 *
			 * Asynchronous JSONRPC 2.0 clients must set the "id" property to be able to match responses to requests, as they arrive out of order.
			 * The "id" property cannot be null, but it can be omitted in the case of notification requests, which expect no response at all from the server.
			 */
			assert(
				typeof outgoingRequest.requestObject.id === "number" || typeof outgoingRequest.requestObject.id === "string",
				"outgoingRequest.requestObject.id must be of type number or string."
			);

			this._objWorkerRequestsPromises[outgoingRequest.requestObject.id] = {
				// unixtimeMilliseconds: (new Date()).getTime(),
				outgoingRequest: outgoingRequest,
				promise: null
			};

			this._objWorkerRequestsPromises[outgoingRequest.requestObject.id].promise = new Promise((fnResolve, fnReject) => {
				this._objWorkerRequestsPromises[outgoingRequest.requestObject.id].fnResolve = fnResolve;
				this._objWorkerRequestsPromises[outgoingRequest.requestObject.id].fnReject = fnReject;
			});
		}

		if(this.worker.postMessage)
		{
			this.worker.postMessage(outgoingRequest.requestObject, outgoingRequest.transferList);
		}
		else
		{
			this.worker.send(outgoingRequest.requestObject);
		}

		if(outgoingRequest.isNotification)
		{
			// JSONRPC 2.0 notification requests don't have the id property at all, not even null. JSONRPC 2.0 servers do not send a response at all for these types of requests.
		}
		else
		{
			return this._objWorkerRequestsPromises[outgoingRequest.requestObject.id].promise;
		}
	}

	/**
	 * @param {JSONRPC.OutgoingRequest} outgoingRequest
	 */
	async beforeJSONDecode(outgoingRequest)
	{
	}

	/**
	 * @param {Error} error
	 */
	rejectAllPromises(error)
	{
		//console.error(error);
		console.log("[" + process.pid + "] Rejecting all Promise instances in WorkerTransport.");

		let nCount = 0;

		for(let nCallID in this._objWorkerRequestsPromises)
		{
			this._objWorkerRequestsPromises[nCallID].fnReject(error);
			delete this._objWorkerRequestsPromises[nCallID];

			nCount++;
		}

		if(nCount)
		{
			console.error("[" + process.pid + "] Rejected " + nCount + " Promise instances in WorkerTransport.");
		}
	}

	/**
	 * @protected
	 */
	_setupWorker()
	{
		if(this._worker.addEventListener)
		{
			// There's no close/exit event in browser environments.
			// Call this manually when appropriate: this.rejectAllPromises(new Error("Worker closed"));

			// TODO: create API to be called to remove event listeners.

			const fnOnError = (error) => {
				this.rejectAllPromises(error);
			};
			const fnOnMessage = async (messageEvent) => {
				await this.processResponse(messageEvent.data);
			};

			this._worker.addEventListener("error", fnOnError);

			if(!this._bBidirectionalWorkerMode)
			{
				this._worker.addEventListener("message", fnOnMessage);
			}
		}
		else
		{
			const fnOnError = (error) => {
				this.rejectAllPromises(error);
			};
			const fnOnMessage = async (objMessage) => {
				await this.processResponse(objMessage);
			};
			const fnOnExit = (nCode, nSignal) => {
				this.rejectAllPromises(new Error("Worker closed. Code: " + JSON.stringify(nCode) + ". Signal: " + JSON.stringify(nSignal)));

				this._worker.removeListener("exit", fnOnExit);
				this._worker.removeListener("error", fnOnError);

				if(!this._bBidirectionalWorkerMode)
				{
					this._worker.removeListener("message", fnOnMessage);
				}
			};

			this._worker.on("exit", fnOnExit);
			this._worker.on("error", fnOnError);

			if(!this._bBidirectionalWorkerMode)
			{
				this._worker.on("message", fnOnMessage);
			}
		}
	}
};
<commit_msg>Internet Explorer 10<commit_after>const JSONRPC = {};
JSONRPC.ClientPluginBase = require("../../ClientPluginBase");
JSONRPC.Utils = require("../../Utils");

const assert = require("assert");

module.exports =
/**
 *  this.rejectAllPromises(error) has to be called manually in a browser environment when a Worker is terminated or has finished working.
 */
class WorkerTransport extends JSONRPC.ClientPluginBase
{
	/**
	 * @param {Worker} worker
	 * @param {boolean|undefined} bBidirectionalWorkerMode
	 */
	constructor(worker, bBidirectionalWorkerMode)
	{
		super();

		// JSONRPC call ID as key, {promise: {Promise}, fnResolve: {Function}, fnReject: {Function}, outgoingRequest: {OutgoingRequest}} as values.
		this._objWorkerRequestsPromises = {};

		this._bBidirectionalWorkerMode = !!bBidirectionalWorkerMode;
		this._worker = worker;

		this._setupWorker();
	}

	/**
	 * @returns {Worker}
	 */
	get worker()
	{
		return this._worker;
	}

	/**
	 * @param {JSONRPC.OutgoingRequest} outgoingRequest
	 */
	async beforeJSONEncode(outgoingRequest)
	{
		// No serialization.
		outgoingRequest.requestBody = outgoingRequest.requestObject;
	}

	/**
	 * objResponse is the object obtained after JSON parsing for strResponse.
	 *
	 * @param {Object|undefined} objResponse
	 */
	async processResponse(objResponse)
	{
		if(
			(
				typeof objResponse.id !== "number"
				&& typeof objResponse.id !== "string"
			)
			|| !this._objWorkerRequestsPromises[objResponse.id]
		)
		{
			console.error(new Error("Couldn't find JSONRPC response call ID in this._objWorkerRequestsPromises. RAW response: " + JSON.stringify(objResponse)));
			console.error(new Error("RAW remote message: " + JSON.stringify(objResponse)));
			console.log("[" + process.pid + "] Unclean state. Unable to match Worker message to an existing Promise or qualify it as a request.");

			if(this.worker.terminate)
			{
				this.worker.terminate();
			}
			else if(this.worker !== process)
			{
				this.worker.kill();
			}

			return;
		}

		this._objWorkerRequestsPromises[objResponse.id].outgoingRequest.responseBody = objResponse;
		this._objWorkerRequestsPromises[objResponse.id].outgoingRequest.responseObject = objResponse;

		this._objWorkerRequestsPromises[objResponse.id].fnResolve(null);
		// Sorrounding code will parse the result and throw if necessary. fnReject is not going to be used in this function.

		delete this._objWorkerRequestsPromises[objResponse.id];
	}

	/**
	 * Populates the the OutgoingRequest class instance (outgoingRequest) with the RAW JSON response and the JSON parsed response object.
	 *
	 * @param {JSONRPC.OutgoingRequest} outgoingRequest
	 *
	 * @returns {Promise.<null>}
	 */
	async makeRequest(outgoingRequest)
	{
		if(outgoingRequest.isMethodCalled)
		{
			return;
		}

		if(this.worker.isDead && this.worker.isDead())
		{
			throw new Error("Worker not connected.");
		}

		outgoingRequest.isMethodCalled = true;

		if(outgoingRequest.isNotification)
		{
			// JSONRPC 2.0 notification requests don't have the id property at all, not even null. JSONRPC 2.0 servers do not send a response at all for these types of requests.
		}
		else
		{
			/**
			 * http://www.jsonrpc.org/specification#notification
			 *
			 * id
			 * An identifier established by the Client that MUST contain a String, Number, or NULL value if included. If it is not included it is assumed to be a notification. The value SHOULD normally not be Null [1] and Numbers SHOULD NOT contain fractional parts [2]
			 * The Server MUST reply with the same value in the Response object if included. This member is used to correlate the context between the two objects.
			 *
			 * [1] The use of Null as a value for the id member in a Request object is discouraged, because this specification uses a value of Null for Responses with an unknown id. Also, because JSON-RPC 1.0 uses an id value of Null for Notifications this could cause confusion in handling.
			 *
			 * [2] Fractional parts may be problematic, since many decimal fractions cannot be represented exactly as binary fractions.
			 *
			 * =====================================
			 *
			 * Asynchronous JSONRPC 2.0 clients must set the "id" property to be able to match responses to requests, as they arrive out of order.
			 * The "id" property cannot be null, but it can be omitted in the case of notification requests, which expect no response at all from the server.
			 */
			assert(
				typeof outgoingRequest.requestObject.id === "number" || typeof outgoingRequest.requestObject.id === "string",
				"outgoingRequest.requestObject.id must be of type number or string."
			);

			this._objWorkerRequestsPromises[outgoingRequest.requestObject.id] = {
				// unixtimeMilliseconds: (new Date()).getTime(),
				outgoingRequest: outgoingRequest,
				promise: null
			};

			this._objWorkerRequestsPromises[outgoingRequest.requestObject.id].promise = new Promise((fnResolve, fnReject) => {
				this._objWorkerRequestsPromises[outgoingRequest.requestObject.id].fnResolve = fnResolve;
				this._objWorkerRequestsPromises[outgoingRequest.requestObject.id].fnReject = fnReject;
			});
		}

		if(this.worker.postMessage)
		{
			// Internet Explorer 10 does not support transferList. Not sending the param at all if empty.
			if(outgoingRequest.transferList.length)
			{
				this.worker.postMessage(outgoingRequest.requestObject, outgoingRequest.transferList);
			}
			else
			{
				this.worker.postMessage(outgoingRequest.requestObject);
			}
		}
		else
		{
			this.worker.send(outgoingRequest.requestObject);
		}

		if(outgoingRequest.isNotification)
		{
			// JSONRPC 2.0 notification requests don't have the id property at all, not even null. JSONRPC 2.0 servers do not send a response at all for these types of requests.
		}
		else
		{
			return this._objWorkerRequestsPromises[outgoingRequest.requestObject.id].promise;
		}
	}

	/**
	 * @param {JSONRPC.OutgoingRequest} outgoingRequest
	 */
	async beforeJSONDecode(outgoingRequest)
	{
	}

	/**
	 * @param {Error} error
	 */
	rejectAllPromises(error)
	{
		//console.error(error);
		console.log("[" + process.pid + "] Rejecting all Promise instances in WorkerTransport.");

		let nCount = 0;

		for(let nCallID in this._objWorkerRequestsPromises)
		{
			this._objWorkerRequestsPromises[nCallID].fnReject(error);
			delete this._objWorkerRequestsPromises[nCallID];

			nCount++;
		}

		if(nCount)
		{
			console.error("[" + process.pid + "] Rejected " + nCount + " Promise instances in WorkerTransport.");
		}
	}

	/**
	 * @protected
	 */
	_setupWorker()
	{
		if(this._worker.addEventListener)
		{
			// There's no close/exit event in browser environments.
			// Call this manually when appropriate: this.rejectAllPromises(new Error("Worker closed"));

			// TODO: create API to be called to remove event listeners.

			const fnOnError = (error) => {
				this.rejectAllPromises(error);
			};
			const fnOnMessage = async (messageEvent) => {
				await this.processResponse(messageEvent.data);
			};

			this._worker.addEventListener("error", fnOnError);

			if(!this._bBidirectionalWorkerMode)
			{
				this._worker.addEventListener("message", fnOnMessage);
			}
		}
		else
		{
			const fnOnError = (error) => {
				this.rejectAllPromises(error);
			};
			const fnOnMessage = async (objMessage) => {
				await this.processResponse(objMessage);
			};
			const fnOnExit = (nCode, nSignal) => {
				this.rejectAllPromises(new Error("Worker closed. Code: " + JSON.stringify(nCode) + ". Signal: " + JSON.stringify(nSignal)));

				this._worker.removeListener("exit", fnOnExit);
				this._worker.removeListener("error", fnOnError);

				if(!this._bBidirectionalWorkerMode)
				{
					this._worker.removeListener("message", fnOnMessage);
				}
			};

			this._worker.on("exit", fnOnExit);
			this._worker.on("error", fnOnError);

			if(!this._bBidirectionalWorkerMode)
			{
				this._worker.on("message", fnOnMessage);
			}
		}
	}
};
<|endoftext|>


<commit_before>// Cache test
<commit_msg>updated file<commit_after>// How long
// Cache test
<|endoftext|>


<commit_before>/**
 * The form that's visible when "Create <ItemName>" is clicked on either the
 * List screen or the Item screen
 */

import React from 'react';
import assign from 'object-assign';
import vkey from 'vkey';
import AlertMessages from './AlertMessages';
import { Fields } from 'FieldTypes';
import InvalidFieldType from './InvalidFieldType';
import { Button, Form, Modal } from 'elemental';

const CreateForm = React.createClass({
	displayName: 'CreateForm',
	propTypes: {
		err: React.PropTypes.object,
		isOpen: React.PropTypes.bool,
		list: React.PropTypes.object,
		onCancel: React.PropTypes.func,
		onCreate: React.PropTypes.func,
	},
	getDefaultProps () {
		return {
			err: null,
			isOpen: false,
		};
	},
	getInitialState () {
		// Set the field values to their default values when first rendering the
		// form. (If they have a default value, that is)
		var values = {};
		Object.keys(this.props.list.fields).forEach(key => {
			var field = this.props.list.fields[key];
			if (field.defaultValue) {
				values[field.path] = field.defaultValue;
			}
		});
		return {
			values: values,
			alerts: {},
		};
	},
	componentDidMount () {
		document.body.addEventListener('keyup', this.handleKeyPress, false);
	},
	componentWillUnmount () {
		document.body.removeEventListener('keyup', this.handleKeyPress, false);
	},
	handleKeyPress (evt) {
		if (vkey[evt.keyCode] === '<escape>') {
			this.props.onCancel();
		}
	},
	// Handle input change events
	handleChange (event) {
		var values = assign({}, this.state.values);
		values[event.path] = event.value;
		this.setState({
			values: values,
		});
	},
	// Set the props of a field
	getFieldProps (field) {
		var props = assign({}, field);
		props.value = this.state.values[field.path];
		props.values = this.state.values;
		props.onChange = this.handleChange;
		props.mode = 'create';
		props.key = field.path;
		return props;
	},
	// Create a new item when the form is submitted
	submitForm (event) {
		event.preventDefault();
		const createForm = event.target;
		const formData = new FormData(createForm);
		this.props.list.createItem(formData, (err, data) => {
			if (data) {
				if (this.props.onCreate) {
					this.props.onCreate(data);
				} else {
					// Clear form
					this.setState({
						values: {},
						alerts: {
							success: {
								success: 'Item created',
							},
						},
					});
				}
			} else {
				if (!err) {
					err = {
						error: 'connection error',
					};
				}
				// If we get a database error, show the database error message
				// instead of only saying "Database error"
				if (err.error === 'database error') {
					err.error = err.detail.errmsg;
				}
				this.setState({
					alerts: {
						error: err,
					},
				});
			}
		});
	},
	// Render the form itself
	renderForm () {
		if (!this.props.isOpen) return;

		var form = [];
		var list = this.props.list;
		var nameField = this.props.list.nameField;
		var focusWasSet;

		// If the name field is an initial one, we need to render a proper
		// input for it
		if (list.nameIsInitial) {
			var nameFieldProps = this.getFieldProps(nameField);
			nameFieldProps.autoFocus = focusWasSet = true;
			if (nameField.type === 'text') {
				nameFieldProps.className = 'item-name-field';
				nameFieldProps.placeholder = nameField.label;
				nameFieldProps.label = '';
			}
			form.push(React.createElement(Fields[nameField.type], nameFieldProps));
		}

		// Render inputs for all initial fields
		Object.keys(list.initialFields).forEach(key => {
			var field = list.fields[list.initialFields[key]];
			// If there's something weird passed in as field type, render the
			// invalid field type component
			if (typeof Fields[field.type] !== 'function') {
				form.push(React.createElement(InvalidFieldType, { type: field.type, path: field.path, key: field.path }));
				return;
			}
			// Get the props for the input field
			var fieldProps = this.getFieldProps(field);
			// If there was no focusRef set previously, set the current field to
			// be the one to be focussed. Generally the first input field, if
			// there's an initial name field that takes precedence.
			if (!focusWasSet) {
				fieldProps.autoFocus = focusWasSet = true;
			}
			form.push(React.createElement(Fields[field.type], fieldProps));
		});

		return (
			<Form
				type="horizontal"
				onSubmit={this.submitForm}
				className="create-form"
			>
				<Modal.Header
					text={'Create a new ' + list.singular}
					onClose={this.props.onCancel}
					showCloseButton
				/>
				<Modal.Body>
					<AlertMessages alerts={this.state.alerts} />
					{form}
				</Modal.Body>
				<Modal.Footer>
					<Button type="success" submit>Create</Button>
					<Button
						type="link-cancel"
						onClick={this.props.onCancel}
					>
						Cancel
					</Button>
				</Modal.Footer>
			</Form>
		);
	},
	render () {
		return (
			<Modal
				isOpen={this.props.isOpen}
				onCancel={this.props.onCancel}
				backdropClosesModal
			>
				{this.renderForm()}
			</Modal>
		);
	},
});

module.exports = CreateForm;
<commit_msg>Using the new getDefaultValue method in the Create form<commit_after>/**
 * The form that's visible when "Create <ItemName>" is clicked on either the
 * List screen or the Item screen
 */

import React from 'react';
import assign from 'object-assign';
import vkey from 'vkey';
import AlertMessages from './AlertMessages';
import { Fields } from 'FieldTypes';
import InvalidFieldType from './InvalidFieldType';
import { Button, Form, Modal } from 'elemental';

const CreateForm = React.createClass({
	displayName: 'CreateForm',
	propTypes: {
		err: React.PropTypes.object,
		isOpen: React.PropTypes.bool,
		list: React.PropTypes.object,
		onCancel: React.PropTypes.func,
		onCreate: React.PropTypes.func,
	},
	getDefaultProps () {
		return {
			err: null,
			isOpen: false,
		};
	},
	getInitialState () {
		// Set the field values to their default values when first rendering the
		// form. (If they have a default value, that is)
		var values = {};
		Object.keys(this.props.list.fields).forEach(key => {
			var field = this.props.list.fields[key];
			var FieldComponent = Fields[field.type];
			values[field.path] = FieldComponent.getDefaultValue(field);
		});
		return {
			values: values,
			alerts: {},
		};
	},
	componentDidMount () {
		document.body.addEventListener('keyup', this.handleKeyPress, false);
	},
	componentWillUnmount () {
		document.body.removeEventListener('keyup', this.handleKeyPress, false);
	},
	handleKeyPress (evt) {
		if (vkey[evt.keyCode] === '<escape>') {
			this.props.onCancel();
		}
	},
	// Handle input change events
	handleChange (event) {
		var values = assign({}, this.state.values);
		values[event.path] = event.value;
		this.setState({
			values: values,
		});
	},
	// Set the props of a field
	getFieldProps (field) {
		var props = assign({}, field);
		props.value = this.state.values[field.path];
		props.values = this.state.values;
		props.onChange = this.handleChange;
		props.mode = 'create';
		props.key = field.path;
		return props;
	},
	// Create a new item when the form is submitted
	submitForm (event) {
		event.preventDefault();
		const createForm = event.target;
		const formData = new FormData(createForm);
		this.props.list.createItem(formData, (err, data) => {
			if (data) {
				if (this.props.onCreate) {
					this.props.onCreate(data);
				} else {
					// Clear form
					this.setState({
						values: {},
						alerts: {
							success: {
								success: 'Item created',
							},
						},
					});
				}
			} else {
				if (!err) {
					err = {
						error: 'connection error',
					};
				}
				// If we get a database error, show the database error message
				// instead of only saying "Database error"
				if (err.error === 'database error') {
					err.error = err.detail.errmsg;
				}
				this.setState({
					alerts: {
						error: err,
					},
				});
			}
		});
	},
	// Render the form itself
	renderForm () {
		if (!this.props.isOpen) return;

		var form = [];
		var list = this.props.list;
		var nameField = this.props.list.nameField;
		var focusWasSet;

		// If the name field is an initial one, we need to render a proper
		// input for it
		if (list.nameIsInitial) {
			var nameFieldProps = this.getFieldProps(nameField);
			nameFieldProps.autoFocus = focusWasSet = true;
			if (nameField.type === 'text') {
				nameFieldProps.className = 'item-name-field';
				nameFieldProps.placeholder = nameField.label;
				nameFieldProps.label = '';
			}
			form.push(React.createElement(Fields[nameField.type], nameFieldProps));
		}

		// Render inputs for all initial fields
		Object.keys(list.initialFields).forEach(key => {
			var field = list.fields[list.initialFields[key]];
			// If there's something weird passed in as field type, render the
			// invalid field type component
			if (typeof Fields[field.type] !== 'function') {
				form.push(React.createElement(InvalidFieldType, { type: field.type, path: field.path, key: field.path }));
				return;
			}
			// Get the props for the input field
			var fieldProps = this.getFieldProps(field);
			// If there was no focusRef set previously, set the current field to
			// be the one to be focussed. Generally the first input field, if
			// there's an initial name field that takes precedence.
			if (!focusWasSet) {
				fieldProps.autoFocus = focusWasSet = true;
			}
			form.push(React.createElement(Fields[field.type], fieldProps));
		});

		return (
			<Form
				type="horizontal"
				onSubmit={this.submitForm}
				className="create-form"
			>
				<Modal.Header
					text={'Create a new ' + list.singular}
					onClose={this.props.onCancel}
					showCloseButton
				/>
				<Modal.Body>
					<AlertMessages alerts={this.state.alerts} />
					{form}
				</Modal.Body>
				<Modal.Footer>
					<Button type="success" submit>Create</Button>
					<Button
						type="link-cancel"
						onClick={this.props.onCancel}
					>
						Cancel
					</Button>
				</Modal.Footer>
			</Form>
		);
	},
	render () {
		return (
			<Modal
				isOpen={this.props.isOpen}
				onCancel={this.props.onCancel}
				backdropClosesModal
			>
				{this.renderForm()}
			</Modal>
		);
	},
});

module.exports = CreateForm;
<|endoftext|>


<commit_before>/** @license MIT License (c) copyright 2010-2016 original author or authors */
/** @author Brian Cavalier */
/** @author John Hann */

export default function Stream (source) {
  this.source = source
}

Stream.prototype.run = function (sink, source) {
  return this.source.run(sink, source)
}
<commit_msg>Fix naming (#419)<commit_after>/** @license MIT License (c) copyright 2010-2016 original author or authors */
/** @author Brian Cavalier */
/** @author John Hann */

export default function Stream (source) {
  this.source = source
}

Stream.prototype.run = function (sink, scheduler) {
  return this.source.run(sink, scheduler)
}
<|endoftext|>


<commit_before>import Canvas from './Canvas.js'

import FramePlayer from './FramePlayer.js'

import ContrastManager from './Managers/ContrastManager.js'
import PaletteManager from './Managers/PaletteManager.js'
import ResolutionManager from './Managers/ResolutionManager.js'
import ShaderManager from './Managers/ShaderManager.js'
import FrameSetManager from './Managers/FrameSetManager.js'

import React, { Component } from 'react'

import Hammer from 'hammerjs'

import StudioMenu from './Tools/StudioMenu.js'
import StudioToolbar from './Tools/StudioToolbar.js'

import { Position, Toaster, FocusStyleManager } from "@blueprintjs/core";

import '@blueprintjs/core/dist/blueprint.css'

const debugToaster = Toaster.create({
  className: "alert-toaster",
  position: Position.TOP_RIGHT,
});

/**
* Welcome to the Studio
*/
export default class Studio extends Component {

  constructor (props) {
    super(props)

    this.onUpdate = this.onUpdate.bind(this)
    this.updateDimensions = this.updateDimensions.bind(this)
    this.toggleDebug = this.toggleDebug.bind(this)
    this.toggleFullScreen = this.toggleFullScreen.bind(this)
    this.handleKeydown = this.handleKeydown.bind(this)
    this.toggleFill = this.toggleFill.bind(this)
    this.toggleStroke = this.toggleStroke.bind(this)

    this.onRender = this.onRender.bind(this)

    this.shaderManager = new ShaderManager(
                                ShaderManager.defaultItems(),
                                this.onUpdate)

    this.paletteManager = new PaletteManager(
                                PaletteManager.defaultItems(),
                                this.onUpdate)

    this.resolutionManager = new ResolutionManager(
                                    4*9, 4, 4*13, 2,
                                    this.onUpdate)

    this.contrastManager = new ContrastManager(
                                    70, 2, 100, 2,
                                    this.onUpdate)

    this.frameSetManager = new FrameSetManager(FrameSetManager.defaultItems())

    this.framePlayer = new FramePlayer(
                              this.frameSetManager.getByName("face").item.urls,
                              this.onUpdate)

    this.debugToaster = debugToaster

    this.saver = {}

    this.state = {
      debug: true,
      isPlaying: false,
      currentFrameIndex: this.framePlayer.currentFrameIndex,
      frames: this.framePlayer.frames,
      frameCount: this.framePlayer.frames.length,
      currentShaderIndex: this.shaderManager.currentIndex,
      currentPaletteIndex: this.paletteManager.currentIndex,
      fullScreen: false,
      resolution: this.resolutionManager.current(),
      contrast: this.contrastManager.current(),
      fill: true,
      stroke: true
    }
  }

  componentWillMount () {
    this.updateDimensions(this.state.fullScreen)
    FocusStyleManager.onlyShowFocusOnTabs()
  }

  componentDidMount () {
    window.addEventListener('keydown', this.handleKeydown, true)
    window.addEventListener("resize", this.updateDimensions, true)

    const mc = new Hammer(document, { preventDefault: true })

    mc.get('pinch').set({ enable: true })
    mc.get('swipe').set({ direction: Hammer.DIRECTION_ALL })

    mc.on("swipeleft", ev => this.shaderManager.moveToNextIndex())
      .on("swiperight", ev => this.shaderManager.moveToPreviousIndex())
      .on("swipedown", ev => this.paletteManager.moveToPreviousIndex())
      .on("swipeup", ev => this.paletteManager.moveToNextIndex())
      .on("pinchin", ev => this.resolutionManager.up())
      .on("pinchout", ev => this.resolutionManager.down())
      .on("press", ev => {
        this.toggleFill()
      })
      .on("tap", ev => {
        if (ev.tapCount === 2) {
          this.framePlayer.toggle()
        }
      })

  }

  componentWillUnmount () {
    window.removeEventListener("resize", this.updateDimensions, true)
    window.removeEventListener('keydown', this.handleKeydown, true)
  }

  onUpdate () {
    this.setState({
      isPlaying : this.framePlayer.isPlaying(),
      currentFrameIndex: this.framePlayer.currentFrameIndex,
      frameCount: this.framePlayer.frames.length,
      frames: this.framePlayer.frames,
      currentShaderIndex: this.shaderManager.currentIndex,
      currentPaletteIndex: this.paletteManager.currentIndex,
      resolution: this.resolutionManager.current(),
      contrast: this.contrastManager.current()
    })
  }

  getDimensions (fullScreen) {
    const w = window,
        d = document,
        documentElement = d.documentElement,
        body = d.getElementsByTagName('body')[0]

    let width = w.innerWidth || documentElement.clientWidth || body.clientWidth,
        height = w.innerHeight|| documentElement.clientHeight|| body.clientHeight

    if (!fullScreen) {
      width = Math.min(width, height)
      height = width
    }

    return { width: width, height: height }
  }

  updateDimensions () {
    const dims = this.getDimensions(this.state.fullScreen)

    if (this.state.debug
      && dims.width !== this.state.width
      && dims.height !== this.state.height) {
      //this.debugToaster.show({ message: `Canvas is ${dims.width}px x ${dims.height}px`, iconName: 'move' })
    }

    this.setState(dims)
  }

  toggleFill () {
    const result = !this.state.fill

    if (this.state.debug) {
      this.debugToaster.show({
        message: !result ? 'Fill off' : 'Fill on',
        iconName: !result ? 'circle' : 'full-circle'
      })
    }

    this.setState({fill: result})
  }

  toggleStroke () {
    const result = !this.state.stroke

    if (this.state.debug) {
      this.debugToaster.show({
        message: !result ? 'Stroke off' : 'Stroke on',
        iconName: !result ? 'circle' : 'full-circle'
      })
    }

    this.setState({stroke: result})
  }

  toggleDebug () {
    if (this.state.debug) {
      this.debugToaster.show({ message: "Press ⌘H to toggle menus on", iconName: 'help'})
    }

    this.setState({debug: !this.state.debug})
  }

  toggleFullScreen () {

    const result = !this.state.fullScreen
    const dims = this.getDimensions(result)
    dims.fullScreen = result

    this.setState(dims)

    if (this.state.debug) {
      this.debugToaster.show({
        message: result ? 'Fullscreen On' : 'Fullscreen Off',
        iconName: result ? 'fullscreen' : 'minimize'
      })

      this.debugToaster.show({
        message: `Canvas is ${dims.width}px x ${dims.height}px`,
        iconName: 'move'
      })
    }
  }

  onRender (timing) {
    if (this.state.debug) {
      console.log(`Time to process: ${timing.processTime}ms`)
      console.log(`Time to render: ${timing.renderTime}ms`)
    }
  }

  saveImage () {
    const canvas = document.getElementsByTagName('canvas')[0]

    const downloadLink = canvas.toDataURL('image/png')

    const link = document.createElement('a')

    link.href = downloadLink
    link.setAttribute('download', `studio-${this.shaderManager.current().name}-${this.paletteManager.current().name}-${this.framePlayer.currentFrameIndex+1}.png`)
    link.click()

    if (this.state.debug) {
      this.debugToaster.show({
        message: `Image saved`, iconName: 'download'
      })
    }
  }

  handleKeydown (ev) {
    if ((ev.metaKey || ev.ctrlKey) && ev.which === 72) {
      ev.preventDefault()
      this.toggleDebug()
    } else if ((ev.metaKey || ev.ctrlKey) && ev.which === 83) {
      ev.preventDefault()
      this.saveImage()
    } else if ((ev.metaKey || ev.ctrlKey) && ev.which === 70) {
      ev.preventDefault()
      this.toggleFullScreen()
    } else if ((ev.metaKey || ev.ctrlKey) && ev.which === 68) {
      ev.preventDefault()
      this.toggleFill()
    } else if ((ev.metaKey || ev.ctrlKey) && ev.which === 71) {
      ev.preventDefault()
      this.toggleStroke()
    } else if (ev.which === 32) {
      ev.preventDefault()
      this.framePlayer.toggle()
    } else if (ev.which === 39) {
      ev.preventDefault()
      if (ev.metaKey || ev.ctrlKey) {
        this.paletteManager.moveToNextIndex()
      } else {
        this.shaderManager.moveToNextIndex()
      }
    } else if (ev.which === 37) {
      ev.preventDefault()
      if (ev.metaKey || ev.ctrlKey) {
        this.paletteManager.moveToPreviousIndex()
      } else {
        this.shaderManager.moveToPreviousIndex()
      }
    } else if (ev.which === 38) {
      ev.preventDefault()
      if (ev.metaKey || ev.ctrlKey) {
        this.contrastManager.up()
      } else {
        this.resolutionManager.up()
      }
    } else if (ev.which === 40) {
      ev.preventDefault()
      if (ev.metaKey || ev.ctrlKey) {
        this.contrastManager.down()
      } else {
        this.resolutionManager.down()
      }
    }
  }

  render () {
    return (

        <Canvas
          width={this.state.width}
          height={this.state.height}
          debug={this.state.debug}
          currentFrame={this.framePlayer.currentFrame()}
          shader={this.shaderManager.current()}
          palette={this.paletteManager.current()}
          debugToaster={this.debugToaster}
          framePlayer={this.framePlayer}
          onRender={this.onRender}
          resolution={this.resolutionManager.current()}
          contrast={this.contrastManager.current()}
          fill={this.state.fill}
          stroke={this.state.stroke} />

        <StudioTools
          debug={this.state.debug}
          fullScreen={this.state.fullScreen}
          toggleFullScreen={this.toggleFullScreen}
          toggleDebug={this.toggleDebug}
          toggleFill={this.toggleFill}
          toggleStroke={this.toggleStroke}
          saveImage={this.saveImage}
          framePlayer={this.framePlayer}
          paletteManager={this.paletteManager}
          shaderManager={this.shaderManager}
          debugToaster={this.debugToaster}
          resolutionManager={this.resolutionManager}
          contrastManager={this.contrastManager} />

    )
  }
}

class StudioTools extends Component {
  render () {
    return !this.props.debug ? null : (

        <StudioMenu {...this.props} />
        <StudioToolbar {...this.props} />

    )
  }
}
<commit_msg>switch direction for swipe<commit_after>import Canvas from './Canvas.js'

import FramePlayer from './FramePlayer.js'

import ContrastManager from './Managers/ContrastManager.js'
import PaletteManager from './Managers/PaletteManager.js'
import ResolutionManager from './Managers/ResolutionManager.js'
import ShaderManager from './Managers/ShaderManager.js'
import FrameSetManager from './Managers/FrameSetManager.js'

import React, { Component } from 'react'

import Hammer from 'hammerjs'

import StudioMenu from './Tools/StudioMenu.js'
import StudioToolbar from './Tools/StudioToolbar.js'

import { Position, Toaster, FocusStyleManager } from "@blueprintjs/core";

import '@blueprintjs/core/dist/blueprint.css'

const debugToaster = Toaster.create({
  className: "alert-toaster",
  position: Position.TOP_RIGHT,
});

/**
* Welcome to the Studio
*/
export default class Studio extends Component {

  constructor (props) {
    super(props)

    this.onUpdate = this.onUpdate.bind(this)
    this.updateDimensions = this.updateDimensions.bind(this)
    this.toggleDebug = this.toggleDebug.bind(this)
    this.toggleFullScreen = this.toggleFullScreen.bind(this)
    this.handleKeydown = this.handleKeydown.bind(this)
    this.toggleFill = this.toggleFill.bind(this)
    this.toggleStroke = this.toggleStroke.bind(this)

    this.onRender = this.onRender.bind(this)

    this.shaderManager = new ShaderManager(
                                ShaderManager.defaultItems(),
                                this.onUpdate)

    this.paletteManager = new PaletteManager(
                                PaletteManager.defaultItems(),
                                this.onUpdate)

    this.resolutionManager = new ResolutionManager(
                                    4*9, 4, 4*13, 2,
                                    this.onUpdate)

    this.contrastManager = new ContrastManager(
                                    70, 2, 100, 2,
                                    this.onUpdate)

    this.frameSetManager = new FrameSetManager(FrameSetManager.defaultItems())

    this.framePlayer = new FramePlayer(
                              this.frameSetManager.getByName("face").item.urls,
                              this.onUpdate)

    this.debugToaster = debugToaster

    this.saver = {}

    this.state = {
      debug: true,
      isPlaying: false,
      currentFrameIndex: this.framePlayer.currentFrameIndex,
      frames: this.framePlayer.frames,
      frameCount: this.framePlayer.frames.length,
      currentShaderIndex: this.shaderManager.currentIndex,
      currentPaletteIndex: this.paletteManager.currentIndex,
      fullScreen: false,
      resolution: this.resolutionManager.current(),
      contrast: this.contrastManager.current(),
      fill: true,
      stroke: true
    }
  }

  componentWillMount () {
    this.updateDimensions(this.state.fullScreen)
    FocusStyleManager.onlyShowFocusOnTabs()
  }

  componentDidMount () {
    window.addEventListener('keydown', this.handleKeydown, true)
    window.addEventListener("resize", this.updateDimensions, true)

    const mc = new Hammer(document, { preventDefault: true })

    mc.get('pinch').set({ enable: true })
    mc.get('swipe').set({ direction: Hammer.DIRECTION_ALL })

    mc.on("swipeleft", ev => this.shaderManager.moveToNextIndex())
      .on("swiperight", ev => this.shaderManager.moveToPreviousIndex())
      .on("swipedown", ev => this.paletteManager.moveToNextIndex())
      .on("swipeup", ev => this.paletteManager.moveToPreviousIndex())
      .on("pinchin", ev => this.resolutionManager.up())
      .on("pinchout", ev => this.resolutionManager.down())
      .on("press", ev => {
        this.toggleFill()
      })
      .on("tap", ev => {
        if (ev.tapCount === 2) {
          this.framePlayer.toggle()
        }
      })

  }

  componentWillUnmount () {
    window.removeEventListener("resize", this.updateDimensions, true)
    window.removeEventListener('keydown', this.handleKeydown, true)
  }

  onUpdate () {
    this.setState({
      isPlaying : this.framePlayer.isPlaying(),
      currentFrameIndex: this.framePlayer.currentFrameIndex,
      frameCount: this.framePlayer.frames.length,
      frames: this.framePlayer.frames,
      currentShaderIndex: this.shaderManager.currentIndex,
      currentPaletteIndex: this.paletteManager.currentIndex,
      resolution: this.resolutionManager.current(),
      contrast: this.contrastManager.current()
    })
  }

  getDimensions (fullScreen) {
    const w = window,
        d = document,
        documentElement = d.documentElement,
        body = d.getElementsByTagName('body')[0]

    let width = w.innerWidth || documentElement.clientWidth || body.clientWidth,
        height = w.innerHeight|| documentElement.clientHeight|| body.clientHeight

    if (!fullScreen) {
      width = Math.min(width, height)
      height = width
    }

    return { width: width, height: height }
  }

  updateDimensions () {
    const dims = this.getDimensions(this.state.fullScreen)

    if (this.state.debug
      && dims.width !== this.state.width
      && dims.height !== this.state.height) {
      //this.debugToaster.show({ message: `Canvas is ${dims.width}px x ${dims.height}px`, iconName: 'move' })
    }

    this.setState(dims)
  }

  toggleFill () {
    const result = !this.state.fill

    if (this.state.debug) {
      this.debugToaster.show({
        message: !result ? 'Fill off' : 'Fill on',
        iconName: !result ? 'circle' : 'full-circle'
      })
    }

    this.setState({fill: result})
  }

  toggleStroke () {
    const result = !this.state.stroke

    if (this.state.debug) {
      this.debugToaster.show({
        message: !result ? 'Stroke off' : 'Stroke on',
        iconName: !result ? 'circle' : 'full-circle'
      })
    }

    this.setState({stroke: result})
  }

  toggleDebug () {
    if (this.state.debug) {
      this.debugToaster.show({ message: "Press ⌘H to toggle menus on", iconName: 'help'})
    }

    this.setState({debug: !this.state.debug})
  }

  toggleFullScreen () {

    const result = !this.state.fullScreen
    const dims = this.getDimensions(result)
    dims.fullScreen = result

    this.setState(dims)

    if (this.state.debug) {
      this.debugToaster.show({
        message: result ? 'Fullscreen On' : 'Fullscreen Off',
        iconName: result ? 'fullscreen' : 'minimize'
      })

      this.debugToaster.show({
        message: `Canvas is ${dims.width}px x ${dims.height}px`,
        iconName: 'move'
      })
    }
  }

  onRender (timing) {
    if (this.state.debug) {
      console.log(`Time to process: ${timing.processTime}ms`)
      console.log(`Time to render: ${timing.renderTime}ms`)
    }
  }

  saveImage () {
    const canvas = document.getElementsByTagName('canvas')[0]

    const downloadLink = canvas.toDataURL('image/png')

    const link = document.createElement('a')

    link.href = downloadLink
    link.setAttribute('download', `studio-${this.shaderManager.current().name}-${this.paletteManager.current().name}-${this.framePlayer.currentFrameIndex+1}.png`)
    link.click()

    if (this.state.debug) {
      this.debugToaster.show({
        message: `Image saved`, iconName: 'download'
      })
    }
  }

  handleKeydown (ev) {
    if ((ev.metaKey || ev.ctrlKey) && ev.which === 72) {
      ev.preventDefault()
      this.toggleDebug()
    } else if ((ev.metaKey || ev.ctrlKey) && ev.which === 83) {
      ev.preventDefault()
      this.saveImage()
    } else if ((ev.metaKey || ev.ctrlKey) && ev.which === 70) {
      ev.preventDefault()
      this.toggleFullScreen()
    } else if ((ev.metaKey || ev.ctrlKey) && ev.which === 68) {
      ev.preventDefault()
      this.toggleFill()
    } else if ((ev.metaKey || ev.ctrlKey) && ev.which === 71) {
      ev.preventDefault()
      this.toggleStroke()
    } else if (ev.which === 32) {
      ev.preventDefault()
      this.framePlayer.toggle()
    } else if (ev.which === 39) {
      ev.preventDefault()
      if (ev.metaKey || ev.ctrlKey) {
        this.paletteManager.moveToNextIndex()
      } else {
        this.shaderManager.moveToNextIndex()
      }
    } else if (ev.which === 37) {
      ev.preventDefault()
      if (ev.metaKey || ev.ctrlKey) {
        this.paletteManager.moveToPreviousIndex()
      } else {
        this.shaderManager.moveToPreviousIndex()
      }
    } else if (ev.which === 38) {
      ev.preventDefault()
      if (ev.metaKey || ev.ctrlKey) {
        this.contrastManager.up()
      } else {
        this.resolutionManager.up()
      }
    } else if (ev.which === 40) {
      ev.preventDefault()
      if (ev.metaKey || ev.ctrlKey) {
        this.contrastManager.down()
      } else {
        this.resolutionManager.down()
      }
    }
  }

  render () {
    return (

        <Canvas
          width={this.state.width}
          height={this.state.height}
          debug={this.state.debug}
          currentFrame={this.framePlayer.currentFrame()}
          shader={this.shaderManager.current()}
          palette={this.paletteManager.current()}
          debugToaster={this.debugToaster}
          framePlayer={this.framePlayer}
          onRender={this.onRender}
          resolution={this.resolutionManager.current()}
          contrast={this.contrastManager.current()}
          fill={this.state.fill}
          stroke={this.state.stroke} />

        <StudioTools
          debug={this.state.debug}
          fullScreen={this.state.fullScreen}
          toggleFullScreen={this.toggleFullScreen}
          toggleDebug={this.toggleDebug}
          toggleFill={this.toggleFill}
          toggleStroke={this.toggleStroke}
          saveImage={this.saveImage}
          framePlayer={this.framePlayer}
          paletteManager={this.paletteManager}
          shaderManager={this.shaderManager}
          debugToaster={this.debugToaster}
          resolutionManager={this.resolutionManager}
          contrastManager={this.contrastManager} />

    )
  }
}

class StudioTools extends Component {
  render () {
    return !this.props.debug ? null : (

        <StudioMenu {...this.props} />
        <StudioToolbar {...this.props} />

    )
  }
}
<|endoftext|>


<commit_before>import Page from './Page';
import { isValidSize, parseVal } from './utils/convertUnits';
import c from './utils/prefixClass';

class Styler {

  setSize(size) {
    isValidSize(size);
    this.size = size;
  }

  setMargin(margin) {
    isValidSize(margin);
    this.margin = margin;
  }

  setBleed(newBleed) {
    this.bleed = newBleed;
  }

  isSizeValid() {
    this.updateStylesheet();
    return Page.isSizeValid();
  }

  spreadSizeStyle() {
    const w = parseVal(this.size.width);
    return {
      height: this.size.height,
      width: `${w.val * 2}${w.unit}`,
    };
  }

  updateStylesheet() {
    let sheet;
    const existing = document.querySelector('#binderyPageSetup');
    if (existing) {
      sheet = existing;
    } else {
      sheet = document.createElement('style');
      sheet.id = 'binderyPageSetup';
    }
    const w = parseVal(this.size.width);

    sheet.innerHTML = `
${c('.show-crop')} ${c('.spread-wrapper')},
${c('.show-bleed-marks')} ${c('.spread-wrapper')} {
  margin: calc(${this.bleed} + 12pt) auto;
}
${c('.page-size')} {
  height: ${this.size.height};
  width: ${this.size.width};
}
${c('.page-size-rotated')} {
  height: ${this.size.width};
  width: ${this.size.height};
}
${c('.spread-size')} {
  height: ${this.size.height};
  width: ${w.val * 2}${w.unit};
}
${c('.spread-size-rotated')} {
  height: ${w.val * 2}${w.unit};
  width: ${this.size.height};
}
${c('.flowbox')},
${c('.footer')} {
  margin-left: ${this.margin.inner};
  margin-right: ${this.margin.outer};
}
${c('.left')} ${c('.flowbox')},
${c('.left')} ${c('.footer')} {
  margin-left: ${this.margin.outer};
  margin-right: ${this.margin.inner};
}

${c('.left')} ${c('.running-header')} {
  left: ${this.margin.outer};
}
${c('.right')} ${c('.running-header')} {
  right: ${this.margin.outer};
}

${c('.flowbox')} { margin-top: ${this.margin.top}; }
${c('.footer')}{ margin-bottom: ${this.margin.bottom}; }

${c('.bleed-left')},
${c('.bleed-right')},
${c('.crop-left')},
${c('.crop-right')},
${c('.crop-fold')} {
  top: calc( -12pt - ${this.bleed} );
  bottom: calc( -12pt - ${this.bleed} );
}

${c('.bleed-top')},
${c('.bleed-bottom')},
${c('.crop-top')},
${c('.crop-bottom')} {
  left: calc( -12pt - ${this.bleed} );
  right: calc( -12pt - ${this.bleed} );
}
${c('.bleed-left')}   { left: -${this.bleed}; }
${c('.bleed-right')}  { right: -${this.bleed}; }
${c('.bleed-top')}    { top: -${this.bleed}; }
${c('.bleed-bottom')} { bottom: -${this.bleed}; }

${c('.background')} {
  top: -${this.bleed};
  bottom: -${this.bleed};
  left: -${this.bleed};
  right: -${this.bleed};
}

${c('.spread')}${c('.right')} > ${c('.background')} {
  left: calc(-100% - ${this.bleed});
}
${c('.spread')}${c('.left')} > ${c('.background')} {
  right: calc(-100% - ${this.bleed});
}
    `;
    document.head.appendChild(sheet);
  }
}

export default Styler;
<commit_msg>Only add margin to spread-wrapper in sheet mode<commit_after>import Page from './Page';
import { isValidSize, parseVal } from './utils/convertUnits';
import c from './utils/prefixClass';

class Styler {

  setSize(size) {
    isValidSize(size);
    this.size = size;
  }

  setMargin(margin) {
    isValidSize(margin);
    this.margin = margin;
  }

  setBleed(newBleed) {
    this.bleed = newBleed;
  }

  isSizeValid() {
    this.updateStylesheet();
    return Page.isSizeValid();
  }

  spreadSizeStyle() {
    const w = parseVal(this.size.width);
    return {
      height: this.size.height,
      width: `${w.val * 2}${w.unit}`,
    };
  }

  updateStylesheet() {
    let sheet;
    const existing = document.querySelector('#binderyPageSetup');
    if (existing) {
      sheet = existing;
    } else {
      sheet = document.createElement('style');
      sheet.id = 'binderyPageSetup';
    }
    const w = parseVal(this.size.width);

    sheet.innerHTML = `
${c('.show-crop')} ${c('.print-page')} ${c('.spread-wrapper')},
${c('.show-bleed-marks')} ${c('.print-page')} ${c('.spread-wrapper')} {
  margin: calc(${this.bleed} + 12pt) auto;
}
${c('.page-size')} {
  height: ${this.size.height};
  width: ${this.size.width};
}
${c('.page-size-rotated')} {
  height: ${this.size.width};
  width: ${this.size.height};
}
${c('.spread-size')} {
  height: ${this.size.height};
  width: ${w.val * 2}${w.unit};
}
${c('.spread-size-rotated')} {
  height: ${w.val * 2}${w.unit};
  width: ${this.size.height};
}
${c('.flowbox')},
${c('.footer')} {
  margin-left: ${this.margin.inner};
  margin-right: ${this.margin.outer};
}
${c('.left')} ${c('.flowbox')},
${c('.left')} ${c('.footer')} {
  margin-left: ${this.margin.outer};
  margin-right: ${this.margin.inner};
}

${c('.left')} ${c('.running-header')} {
  left: ${this.margin.outer};
}
${c('.right')} ${c('.running-header')} {
  right: ${this.margin.outer};
}

${c('.flowbox')} { margin-top: ${this.margin.top}; }
${c('.footer')}{ margin-bottom: ${this.margin.bottom}; }

${c('.bleed-left')},
${c('.bleed-right')},
${c('.crop-left')},
${c('.crop-right')},
${c('.crop-fold')} {
  top: calc( -12pt - ${this.bleed} );
  bottom: calc( -12pt - ${this.bleed} );
}

${c('.bleed-top')},
${c('.bleed-bottom')},
${c('.crop-top')},
${c('.crop-bottom')} {
  left: calc( -12pt - ${this.bleed} );
  right: calc( -12pt - ${this.bleed} );
}
${c('.bleed-left')}   { left: -${this.bleed}; }
${c('.bleed-right')}  { right: -${this.bleed}; }
${c('.bleed-top')}    { top: -${this.bleed}; }
${c('.bleed-bottom')} { bottom: -${this.bleed}; }

${c('.background')} {
  top: -${this.bleed};
  bottom: -${this.bleed};
  left: -${this.bleed};
  right: -${this.bleed};
}

${c('.spread')}${c('.right')} > ${c('.background')} {
  left: calc(-100% - ${this.bleed});
}
${c('.spread')}${c('.left')} > ${c('.background')} {
  right: calc(-100% - ${this.bleed});
}
    `;
    document.head.appendChild(sheet);
  }
}

export default Styler;
<|endoftext|>


<commit_before>import React, { cloneElement } from 'react';
import classNames from 'classnames';

import ValidComponentChildren from './utils/ValidComponentChildren';
import createChainedFunction from './utils/createChainedFunction';
import BootstrapMixin from './BootstrapMixin';

const SubNav = React.createClass({
  mixins: [BootstrapMixin],

  propTypes: {
    onSelect: React.PropTypes.func,
    active: React.PropTypes.bool,
    activeHref: React.PropTypes.string,
    activeKey: React.PropTypes.any,
    disabled: React.PropTypes.bool,
    href: React.PropTypes.string,
    title: React.PropTypes.string,
    text: React.PropTypes.node,
    target: React.PropTypes.string
  },

  getDefaultProps() {
    return {
      bsClass: 'nav'
    };
  },

  handleClick(e) {
    if (this.props.onSelect) {
      e.preventDefault();

      if (!this.props.disabled) {
        this.props.onSelect(this.props.eventKey, this.props.href, this.props.target);
      }
    }
  },

  isActive() {
    return this.isChildActive(this);
  },

  isChildActive(child) {
    if (child.props.active) {
      return true;
    }

    if (this.props.activeKey != null && this.props.activeKey === child.props.eventKey) {
      return true;
    }

    if (this.props.activeHref != null && this.props.activeHref === child.props.href) {
      return true;
    }

    if (child.props.children) {
      let isActive = false;

      ValidComponentChildren.forEach(
        child.props.children,
        function (grandchild) {
          if (this.isChildActive(grandchild)) {
            isActive = true;
          }
        },
        this
      );

      return isActive;
    }

    return false;
  },

  getChildActiveProp(child) {
    if (child.props.active) {
      return true;
    }
    if (this.props.activeKey != null) {
      if (child.props.eventKey === this.props.activeKey) {
        return true;
      }
    }
    if (this.props.activeHref != null) {
      if (child.props.href === this.props.activeHref) {
        return true;
      }
    }

    return child.props.active;
  },

  render() {
    let classes = {
      'active': this.isActive(),
      'disabled': this.props.disabled
    };

    return (

          {this.props.text}

          {ValidComponentChildren.map(this.props.children, this.renderNavItem)}

    );
  },

  renderNavItem(child, index) {
    return cloneElement(
      child,
      {
        active: this.getChildActiveProp(child),
        onSelect: createChainedFunction(child.props.onSelect, this.props.onSelect),
        key: child.key ? child.key : index
      }
    );
  }
});

export default SubNav;
<commit_msg>Define type for `eventKey` property in SubNav.js<commit_after>import React, { cloneElement } from 'react';
import classNames from 'classnames';

import ValidComponentChildren from './utils/ValidComponentChildren';
import createChainedFunction from './utils/createChainedFunction';
import BootstrapMixin from './BootstrapMixin';

const SubNav = React.createClass({
  mixins: [BootstrapMixin],

  propTypes: {
    onSelect: React.PropTypes.func,
    active: React.PropTypes.bool,
    activeHref: React.PropTypes.string,
    activeKey: React.PropTypes.any,
    disabled: React.PropTypes.bool,
    eventKey: React.PropTypes.any,
    href: React.PropTypes.string,
    title: React.PropTypes.string,
    text: React.PropTypes.node,
    target: React.PropTypes.string
  },

  getDefaultProps() {
    return {
      bsClass: 'nav'
    };
  },

  handleClick(e) {
    if (this.props.onSelect) {
      e.preventDefault();

      if (!this.props.disabled) {
        this.props.onSelect(this.props.eventKey, this.props.href, this.props.target);
      }
    }
  },

  isActive() {
    return this.isChildActive(this);
  },

  isChildActive(child) {
    if (child.props.active) {
      return true;
    }

    if (this.props.activeKey != null && this.props.activeKey === child.props.eventKey) {
      return true;
    }

    if (this.props.activeHref != null && this.props.activeHref === child.props.href) {
      return true;
    }

    if (child.props.children) {
      let isActive = false;

      ValidComponentChildren.forEach(
        child.props.children,
        function (grandchild) {
          if (this.isChildActive(grandchild)) {
            isActive = true;
          }
        },
        this
      );

      return isActive;
    }

    return false;
  },

  getChildActiveProp(child) {
    if (child.props.active) {
      return true;
    }
    if (this.props.activeKey != null) {
      if (child.props.eventKey === this.props.activeKey) {
        return true;
      }
    }
    if (this.props.activeHref != null) {
      if (child.props.href === this.props.activeHref) {
        return true;
      }
    }

    return child.props.active;
  },

  render() {
    let classes = {
      'active': this.isActive(),
      'disabled': this.props.disabled
    };

    return (

          {this.props.text}

          {ValidComponentChildren.map(this.props.children, this.renderNavItem)}

    );
  },

  renderNavItem(child, index) {
    return cloneElement(
      child,
      {
        active: this.getChildActiveProp(child),
        onSelect: createChainedFunction(child.props.onSelect, this.props.onSelect),
        key: child.key ? child.key : index
      }
    );
  }
});

export default SubNav;
<|endoftext|>