<commit_before>/**
 * This module defines the p5.Shader class
 * @module Lights, Camera
 * @submodule Shaders
 * @for p5
 * @requires core
 */

var p5 = require('../core/core');

/**
 * Shader class for WEBGL Mode
 * @class p5.Shader
 * @constructor
 * @param {p5.RendererGL} renderer an instance of p5.RendererGL that
 * will provide the GL context for this new p5.Shader
 * @param {String} vertSrc source code for the vertex shader (as a string)
 * @param {String} fragSrc source code for the fragment shader (as a string)
 * @example
 *

 *
 * var varying = 'precision highp float; varying vec2 vPos;';
 * var vs =
 *   varying +
 *   'attribute vec3 aPosition;' +
 *   'void main() { vPos = (gl_Position = vec4(aPosition,1.0)).xy; }';
 * var fs =
 *   varying +
 *   'uniform vec2 p;' +
 *   'uniform float r;' +
 *   'const int I = 500;' +
 *   'void main() {' +
 *   '  vec2 c = p + vPos * r, z = c;' +
 *   '  float n = 0.0;' +
 *   '  for (int i = I; i > 0; i --) {' +
 *   '    if(z.x*z.x+z.y*z.y > 4.0) {' +
 *   '      n = float(i)/float(I);' +
 *   '      break;' +
 *   '    }' +
 *   '    z = vec2(z.x*z.x-z.y*z.y, 2.0*z.x*z.y) + c;' +
 *   '  }' +
 *   '  gl_FragColor = vec4(0.5-cos(n*17.0)/2.0,0.5-cos(n*13.0)/2.0,0.5-cos(n*23.0)/2.0,1.0);' +
 *   '}';
 *
 * var mandel;
 * function setup() {
 *   var renderer = createCanvas(100, 100, WEBGL);
 *   mandel = new p5.Shader(renderer, vs, fs);
 *   shader(mandel);
 *   mandel.setUniform('p', [-0.74364388703, 0.13182590421]);
 *   noStroke();
 * }
 *
 * function draw() {
 *   mandel.setUniform('r', 1.5 * exp(-6.5 * (1 + sin(millis() / 2000))));
 *   quad(-1, -1, 1, -1, 1, 1, -1, 1);
 * }
 *
 *

 */
p5.Shader = function(renderer, vertSrc, fragSrc) {
  // TODO: adapt this to not take ids, but rather,
  // to take the source for a vertex and fragment shader
  // to enable custom shaders at some later date
  this._renderer = renderer;
  this._vertSrc = vertSrc;
  this._fragSrc = fragSrc;
  this._vertShader = -1;
  this._fragShader = -1;
  this._glProgram = 0;
  this._loadedAttributes = false;
  this.attributes = {};
  this._loadedUniforms = false;
  this.uniforms = {};
  this._bound = false;
  this.samplers = [];

  return this;
};

/**
 * Creates, compiles, and links the shader based on its
 * sources for the vertex and fragment shaders (provided
 * to the constructor). Populates known attributes and
 * uniforms from the shader.
 * @method init
 * @chainable
 * @private
 */
p5.Shader.prototype.init = function() {
  if (this._glProgram === 0 /* or context is stale? */) {
    var gl = this._renderer.GL;

    // @todo: once custom shading is allowed,
    // friendly error messages should be used here to share
    // compiler and linker errors.

    //set up the shader by
    // 1. creating and getting a gl id for the shader program,
    // 2. compliling its vertex & fragment sources,
    // 3. linking the vertex and fragment shaders
    this._vertShader = gl.createShader(gl.VERTEX_SHADER);
    //load in our default vertex shader
    gl.shaderSource(this._vertShader, this._vertSrc);
    gl.compileShader(this._vertShader);
    // if our vertex shader failed compilation?
    if (!gl.getShaderParameter(this._vertShader, gl.COMPILE_STATUS)) {
      console.error(
        'Yikes! An error occurred compiling the vertex shader:' +
          gl.getShaderInfoLog(this._vertShader)
      );
      return null;
    }

    this._fragShader = gl.createShader(gl.FRAGMENT_SHADER);
    //load in our material frag shader
    gl.shaderSource(this._fragShader, this._fragSrc);
    gl.compileShader(this._fragShader);
    // if our frag shader failed compilation?
    if (!gl.getShaderParameter(this._fragShader, gl.COMPILE_STATUS)) {
      console.error(
        'Darn! An error occurred compiling the fragment shader:' +
          gl.getShaderInfoLog(this._fragShader)
      );
      return null;
    }

    this._glProgram = gl.createProgram();
    gl.attachShader(this._glProgram, this._vertShader);
    gl.attachShader(this._glProgram, this._fragShader);
    gl.linkProgram(this._glProgram);
    if (!gl.getProgramParameter(this._glProgram, gl.LINK_STATUS)) {
      console.error(
        'Snap! Error linking shader program: ' +
          gl.getProgramInfoLog(this._glProgram)
      );
    }

    this._loadAttributes();
    this._loadUniforms();
  }
  return this;
};

/**
 * Queries the active attributes for this shader and loads
 * their names and locations into the attributes array.
 * @method _loadAttributes
 * @private
 */
p5.Shader.prototype._loadAttributes = function() {
  if (this._loadedAttributes) {
    return;
  }

  this.attributes = {};

  var gl = this._renderer.GL;

  var numAttributes = gl.getProgramParameter(
    this._glProgram,
    gl.ACTIVE_ATTRIBUTES
  );
  for (var i = 0; i < numAttributes; ++i) {
    var attributeInfo = gl.getActiveAttrib(this._glProgram, i);
    var name = attributeInfo.name;
    var location = gl.getAttribLocation(this._glProgram, name);
    var attribute = {};
    attribute.name = name;
    attribute.location = location;
    attribute.type = attributeInfo.type;
    attribute.size = attributeInfo.size;
    this.attributes[name] = attribute;
  }

  this._loadedAttributes = true;
};

/**
 * Queries the active uniforms for this shader and loads
 * their names and locations into the uniforms array.
 * @method _loadUniforms
 * @private
 */
p5.Shader.prototype._loadUniforms = function() {
  if (this._loadedUniforms) {
    return;
  }

  var gl = this._renderer.GL;

  // Inspect shader and cache uniform info
  var numUniforms = gl.getProgramParameter(this._glProgram, gl.ACTIVE_UNIFORMS);

  var samplerIndex = 0;
  for (var i = 0; i < numUniforms; ++i) {
    var uniformInfo = gl.getActiveUniform(this._glProgram, i);
    var uniform = {};
    uniform.location = gl.getUniformLocation(this._glProgram, uniformInfo.name);
    uniform.size = uniformInfo.size;
    var uniformName = uniformInfo.name;
    //uniforms thats are arrays have their name returned as
    //someUniform[0] which is a bit silly so we trim it
    //off here. The size property tells us that its an array
    //so we dont lose any information by doing this
    if (uniformInfo.size > 1) {
      uniformName = uniformName.substring(0, uniformName.indexOf('[0]'));
    }
    uniform.name = uniformName;
    uniform.type = uniformInfo.type;
    if (uniform.type === gl.SAMPLER_2D) {
      uniform.samplerIndex = samplerIndex;
      samplerIndex++;
      this.samplers.push(uniform);
    }
    this.uniforms[uniformName] = uniform;
  }
  this._loadedUniforms = true;
};

p5.Shader.prototype.compile = function() {
  // TODO
};

/**
 * initializes (if needed) and binds the shader program.
 * @method bindShader
 * @private
 */
p5.Shader.prototype.bindShader = function() {
  this.init();
  if (!this._bound) {
    this.useProgram();
    this._bound = true;
    this.bindTextures();

    this._loadAttributes();
    this._loadUniforms();

    this._renderer._setDefaultCamera();
    this._setMatrixUniforms();
    if (this === this._renderer.curStrokeShader) {
      this._setViewportUniform();
    }
  }
};

/**
 * @method unbindShader
 * @chainable
 * @private
 */
p5.Shader.prototype.unbindShader = function() {
  if (this._bound) {
    this.unbindTextures();
    //this._renderer.GL.useProgram(0); ??
    this._bound = false;
  }
  return this;
};

p5.Shader.prototype.bindTextures = function() {
  var gl = this._renderer.GL;
  for (var i = 0; i < this.samplers.length; i++) {
    var uniform = this.samplers[i];
    var tex = uniform.texture;
    if (tex === undefined) {
      // user hasn't yet supplied a texture for this slot.
      // (or there may not be one--maybe just lighting),
      // so we supply a default texture instead.
      tex = this._renderer._getEmptyTexture();
    }
    gl.activeTexture(gl.TEXTURE0 + uniform.samplerIndex);
    tex.bindTexture();
    tex.update();
    gl.uniform1i(uniform.location, uniform.samplerIndex);
  }
};

p5.Shader.prototype.unbindTextures = function() {
  // TODO: migrate stuff from material.js here
  // - OR - have material.js define this function
};

p5.Shader.prototype._setMatrixUniforms = function() {
  this.setUniform('uProjectionMatrix', this._renderer.uPMatrix.mat4);
  this.setUniform('uModelViewMatrix', this._renderer.uMVMatrix.mat4);
  this.setUniform('uViewMatrix', this._renderer.cameraMatrix.mat4);
  if (this === this._renderer.curFillShader) {
    this._renderer.uNMatrix.inverseTranspose(this._renderer.uMVMatrix);
    this.setUniform('uNormalMatrix', this._renderer.uNMatrix.mat3);
  }
};

p5.Shader.prototype._setViewportUniform = function() {
  this.setUniform('uViewport', this._renderer._viewport);
};

/**
 * @method useProgram
 * @chainable
 * @private
 */
p5.Shader.prototype.useProgram = function() {
  var gl = this._renderer.GL;
  gl.useProgram(this._glProgram);
  return this;
};

/**
 * Wrapper around gl.uniform functions.
 * As we store uniform info in the shader we can use that
 * to do type checking on the supplied data and call
 * the appropriate function.
 * @method setUniform
 * @chainable
 * @param {String} uniformName the name of the uniform in the
 * shader program
 * @param {Object} data the data to be associated with that uniform; type
 * varies (could be a single numerical value, array, matrix, or
 * texture / sampler reference)
 */
p5.Shader.prototype.setUniform = function(uniformName, data) {
  //@todo update all current gl.uniformXX calls

  var uniform = this.uniforms[uniformName];
  if (!uniform) {
    //@todo warning?
    return;
  }
  var location = uniform.location;

  var gl = this._renderer.GL;
  // todo: is this safe to do here?
  // todo: store the values another way?
  this.useProgram();

  // TODO BIND?

  switch (uniform.type) {
    case gl.BOOL:
      if (data === true) {
        gl.uniform1i(location, 1);
      } else {
        gl.uniform1i(location, 0);
      }
      break;
    case gl.INT:
      if (uniform.size > 1) {
        gl.uniform1iv(location, data);
      } else {
        gl.uniform1i(location, data);
      }
      break;
    case gl.FLOAT:
      if (uniform.size > 1) {
        gl.uniform1fv(location, data);
      } else {
        gl.uniform1f(location, data);
      }
      break;
    case gl.FLOAT_MAT3:
      gl.uniformMatrix3fv(location, false, data);
      break;
    case gl.FLOAT_MAT4:
      gl.uniformMatrix4fv(location, false, data);
      break;
    case gl.FLOAT_VEC2:
      if (uniform.size > 1) {
        gl.uniform2fv(location, data);
      } else {
        gl.uniform2f(location, data[0], data[1]);
      }
      break;
    case gl.FLOAT_VEC3:
      if (uniform.size > 1) {
        gl.uniform3fv(location, data);
      } else {
        gl.uniform3f(location, data[0], data[1], data[2]);
      }
      break;
    case gl.FLOAT_VEC4:
      if (uniform.size > 1) {
        gl.uniform4fv(location, data);
      } else {
        gl.uniform4f(location, data[0], data[1], data[2], data[3]);
      }
      break;
    case gl.SAMPLER_2D:
      gl.activeTexture(gl.TEXTURE0 + uniform.samplerIndex);
      uniform.texture = this._renderer.getTexture(data);
      gl.uniform1i(uniform.location, uniform.samplerIndex);
      break;
    //@todo complete all types
  }
  return this;
};

/* NONE OF THIS IS FAST OR EFFICIENT BUT BEAR WITH ME
 *
 * these shader "type" query methods are used by various
 * facilities of the renderer to determine if changing
 * the shader type for the required action (for example,
 * do we need to load the default lighting shader if the
 * current shader cannot handle lighting?)
 *
 **/

p5.Shader.prototype.isLightShader = function() {
  return (
    this.uniforms.uUseLighting !== undefined ||
    this.uniforms.uAmbientLightCount !== undefined ||
    this.uniforms.uDirectionalLightCount !== undefined ||
    this.uniforms.uPointLightCount !== undefined ||
    this.uniforms.uAmbientColor !== undefined ||
    this.uniforms.uDirectionalColor !== undefined ||
    this.uniforms.uPointLightLocation !== undefined ||
    this.uniforms.uPointLightColor !== undefined ||
    this.uniforms.uLightingDirection !== undefined ||
    this.uniforms.uSpecular !== undefined
  );
};

p5.Shader.prototype.isTextureShader = function() {
  return this.samplerIndex > 0;
};

p5.Shader.prototype.isColorShader = function() {
  return (
    this.attributes.aVertexColor !== undefined ||
    this.uniforms.uMaterialColor !== undefined
  );
};

p5.Shader.prototype.isTexLightShader = function() {
  return this.isLightShader() && this.isTextureShader();
};

p5.Shader.prototype.isStrokeShader = function() {
  return this.uniforms.uStrokeWeight !== undefined;
};

/**
 * @method enableAttrib
 * @chainable
 * @private
 */
p5.Shader.prototype.enableAttrib = function(
  loc,
  size,
  type,
  normalized,
  stride,
  offset
) {
  var gl = this._renderer.GL;
  if (loc !== -1) {
    gl.enableVertexAttribArray(loc);
    gl.vertexAttribPointer(loc, size, type, normalized, stride, offset);
  }
  return this;
};

module.exports = p5.Shader;
<commit_msg>add `createShader` method, add comments to sample<commit_after>/**
 * This module defines the p5.Shader class
 * @module Lights, Camera
 * @submodule Shaders
 * @for p5
 * @requires core
 */

var p5 = require('../core/core');

/**
 * Shader class for WEBGL Mode
 * @class p5.Shader
 * @constructor
 * @param {p5.RendererGL} renderer an instance of p5.RendererGL that
 * will provide the GL context for this new p5.Shader
 * @param {String} vertSrc source code for the vertex shader (as a string)
 * @param {String} fragSrc source code for the fragment shader (as a string)
 */
p5.Shader = function(renderer, vertSrc, fragSrc) {
  // TODO: adapt this to not take ids, but rather,
  // to take the source for a vertex and fragment shader
  // to enable custom shaders at some later date
  this._renderer = renderer;
  this._vertSrc = vertSrc;
  this._fragSrc = fragSrc;
  this._vertShader = -1;
  this._fragShader = -1;
  this._glProgram = 0;
  this._loadedAttributes = false;
  this.attributes = {};
  this._loadedUniforms = false;
  this.uniforms = {};
  this._bound = false;
  this.samplers = [];

  return this;
};

/**
 * @method createShader
 * @for p5
 * @param {String} vertSrc source code for the vertex shader
 * @param {String} fragSrc source code for the fragment shader
 * @returns {p5.Shader}
 *
 * @example
 *

 *
 * // the 'varying's are shader between both vertex & fragment shaders
 * var varying = 'precision highp float; varying vec2 vPos;';
 *
 * // the vertex shader is called for each vertex
 * var vs =
 *   varying +
 *   'attribute vec3 aPosition;' +
 *   'void main() { vPos = (gl_Position = vec4(aPosition,1.0)).xy; }';
 *
 * // the fragment shader is called for each pixel
 * var fs =
 *   varying +
 *   'uniform vec2 p;' +
 *   'uniform float r;' +
 *   'const int I = 500;' +
 *   'void main() {' +
 *   '  vec2 c = p + vPos * r, z = c;' +
 *   '  float n = 0.0;' +
 *   '  for (int i = I; i > 0; i --) {' +
 *   '    if(z.x*z.x+z.y*z.y > 4.0) {' +
 *   '      n = float(i)/float(I);' +
 *   '      break;' +
 *   '    }' +
 *   '    z = vec2(z.x*z.x-z.y*z.y, 2.0*z.x*z.y) + c;' +
 *   '  }' +
 *   '  gl_FragColor = vec4(0.5-cos(n*17.0)/2.0,0.5-cos(n*13.0)/2.0,0.5-cos(n*23.0)/2.0,1.0);' +
 *   '}';
 *
 * var mandel;
 * function setup() {
 *   // create and initialize the shader
 *   mandel = createShader(vs, fs);
 *   shader(mandel);
 *   noStroke();
 *
 *   // 'p' is the center point of the Mandelbrot image
 *   mandel.setUniform('p', [-0.74364388703, 0.13182590421]);
 * }
 *
 * function draw() {
 *   // 'r' is the size of the image in Mandelbrot-space
 *   mandel.setUniform('r', 1.5 * exp(-6.5 * (1 + sin(millis() / 2000))));
 *   quad(-1, -1, 1, -1, 1, 1, -1, 1);
 * }
 *
 *

 *
 * @alt
 * zooming Mandelbrot set. a colorful, infinitely detailed fractal.
 */
p5.prototype.createShader = function(vs, fs) {
  return new p5.Shader(this._renderer, vs, fs);
};

/**
 * Creates, compiles, and links the shader based on its
 * sources for the vertex and fragment shaders (provided
 * to the constructor). Populates known attributes and
 * uniforms from the shader.
 * @method init
 * @for p5.Shader
 * @chainable
 * @private
 */
p5.Shader.prototype.init = function() {
  if (this._glProgram === 0 /* or context is stale? */) {
    var gl = this._renderer.GL;

    // @todo: once custom shading is allowed,
    // friendly error messages should be used here to share
    // compiler and linker errors.

    //set up the shader by
    // 1. creating and getting a gl id for the shader program,
    // 2. compliling its vertex & fragment sources,
    // 3. linking the vertex and fragment shaders
    this._vertShader = gl.createShader(gl.VERTEX_SHADER);
    //load in our default vertex shader
    gl.shaderSource(this._vertShader, this._vertSrc);
    gl.compileShader(this._vertShader);
    // if our vertex shader failed compilation?
    if (!gl.getShaderParameter(this._vertShader, gl.COMPILE_STATUS)) {
      console.error(
        'Yikes! An error occurred compiling the vertex shader:' +
          gl.getShaderInfoLog(this._vertShader)
      );
      return null;
    }

    this._fragShader = gl.createShader(gl.FRAGMENT_SHADER);
    //load in our material frag shader
    gl.shaderSource(this._fragShader, this._fragSrc);
    gl.compileShader(this._fragShader);
    // if our frag shader failed compilation?
    if (!gl.getShaderParameter(this._fragShader, gl.COMPILE_STATUS)) {
      console.error(
        'Darn! An error occurred compiling the fragment shader:' +
          gl.getShaderInfoLog(this._fragShader)
      );
      return null;
    }

    this._glProgram = gl.createProgram();
    gl.attachShader(this._glProgram, this._vertShader);
    gl.attachShader(this._glProgram, this._fragShader);
    gl.linkProgram(this._glProgram);
    if (!gl.getProgramParameter(this._glProgram, gl.LINK_STATUS)) {
      console.error(
        'Snap! Error linking shader program: ' +
          gl.getProgramInfoLog(this._glProgram)
      );
    }

    this._loadAttributes();
    this._loadUniforms();
  }
  return this;
};

/**
 * Queries the active attributes for this shader and loads
 * their names and locations into the attributes array.
 * @method _loadAttributes
 * @private
 */
p5.Shader.prototype._loadAttributes = function() {
  if (this._loadedAttributes) {
    return;
  }

  this.attributes = {};

  var gl = this._renderer.GL;

  var numAttributes = gl.getProgramParameter(
    this._glProgram,
    gl.ACTIVE_ATTRIBUTES
  );
  for (var i = 0; i < numAttributes; ++i) {
    var attributeInfo = gl.getActiveAttrib(this._glProgram, i);
    var name = attributeInfo.name;
    var location = gl.getAttribLocation(this._glProgram, name);
    var attribute = {};
    attribute.name = name;
    attribute.location = location;
    attribute.type = attributeInfo.type;
    attribute.size = attributeInfo.size;
    this.attributes[name] = attribute;
  }

  this._loadedAttributes = true;
};

/**
 * Queries the active uniforms for this shader and loads
 * their names and locations into the uniforms array.
 * @method _loadUniforms
 * @private
 */
p5.Shader.prototype._loadUniforms = function() {
  if (this._loadedUniforms) {
    return;
  }

  var gl = this._renderer.GL;

  // Inspect shader and cache uniform info
  var numUniforms = gl.getProgramParameter(this._glProgram, gl.ACTIVE_UNIFORMS);

  var samplerIndex = 0;
  for (var i = 0; i < numUniforms; ++i) {
    var uniformInfo = gl.getActiveUniform(this._glProgram, i);
    var uniform = {};
    uniform.location = gl.getUniformLocation(this._glProgram, uniformInfo.name);
    uniform.size = uniformInfo.size;
    var uniformName = uniformInfo.name;
    //uniforms thats are arrays have their name returned as
    //someUniform[0] which is a bit silly so we trim it
    //off here. The size property tells us that its an array
    //so we dont lose any information by doing this
    if (uniformInfo.size > 1) {
      uniformName = uniformName.substring(0, uniformName.indexOf('[0]'));
    }
    uniform.name = uniformName;
    uniform.type = uniformInfo.type;
    if (uniform.type === gl.SAMPLER_2D) {
      uniform.samplerIndex = samplerIndex;
      samplerIndex++;
      this.samplers.push(uniform);
    }
    this.uniforms[uniformName] = uniform;
  }
  this._loadedUniforms = true;
};

p5.Shader.prototype.compile = function() {
  // TODO
};

/**
 * initializes (if needed) and binds the shader program.
 * @method bindShader
 * @private
 */
p5.Shader.prototype.bindShader = function() {
  this.init();
  if (!this._bound) {
    this.useProgram();
    this._bound = true;
    this.bindTextures();

    this._loadAttributes();
    this._loadUniforms();

    this._renderer._setDefaultCamera();
    this._setMatrixUniforms();
    if (this === this._renderer.curStrokeShader) {
      this._setViewportUniform();
    }
  }
};

/**
 * @method unbindShader
 * @chainable
 * @private
 */
p5.Shader.prototype.unbindShader = function() {
  if (this._bound) {
    this.unbindTextures();
    //this._renderer.GL.useProgram(0); ??
    this._bound = false;
  }
  return this;
};

p5.Shader.prototype.bindTextures = function() {
  var gl = this._renderer.GL;
  for (var i = 0; i < this.samplers.length; i++) {
    var uniform = this.samplers[i];
    var tex = uniform.texture;
    if (tex === undefined) {
      // user hasn't yet supplied a texture for this slot.
      // (or there may not be one--maybe just lighting),
      // so we supply a default texture instead.
      tex = this._renderer._getEmptyTexture();
    }
    gl.activeTexture(gl.TEXTURE0 + uniform.samplerIndex);
    tex.bindTexture();
    tex.update();
    gl.uniform1i(uniform.location, uniform.samplerIndex);
  }
};

p5.Shader.prototype.unbindTextures = function() {
  // TODO: migrate stuff from material.js here
  // - OR - have material.js define this function
};

p5.Shader.prototype._setMatrixUniforms = function() {
  this.setUniform('uProjectionMatrix', this._renderer.uPMatrix.mat4);
  this.setUniform('uModelViewMatrix', this._renderer.uMVMatrix.mat4);
  this.setUniform('uViewMatrix', this._renderer.cameraMatrix.mat4);
  if (this === this._renderer.curFillShader) {
    this._renderer.uNMatrix.inverseTranspose(this._renderer.uMVMatrix);
    this.setUniform('uNormalMatrix', this._renderer.uNMatrix.mat3);
  }
};

p5.Shader.prototype._setViewportUniform = function() {
  this.setUniform('uViewport', this._renderer._viewport);
};

/**
 * @method useProgram
 * @chainable
 * @private
 */
p5.Shader.prototype.useProgram = function() {
  var gl = this._renderer.GL;
  gl.useProgram(this._glProgram);
  return this;
};

/**
 * Wrapper around gl.uniform functions.
 * As we store uniform info in the shader we can use that
 * to do type checking on the supplied data and call
 * the appropriate function.
 * @method setUniform
 * @chainable
 * @param {String} uniformName the name of the uniform in the
 * shader program
 * @param {Object} data the data to be associated with that uniform; type
 * varies (could be a single numerical value, array, matrix, or
 * texture / sampler reference)
 */
p5.Shader.prototype.setUniform = function(uniformName, data) {
  //@todo update all current gl.uniformXX calls

  var uniform = this.uniforms[uniformName];
  if (!uniform) {
    //@todo warning?
    return;
  }
  var location = uniform.location;

  var gl = this._renderer.GL;
  // todo: is this safe to do here?
  // todo: store the values another way?
  this.useProgram();

  // TODO BIND?

  switch (uniform.type) {
    case gl.BOOL:
      if (data === true) {
        gl.uniform1i(location, 1);
      } else {
        gl.uniform1i(location, 0);
      }
      break;
    case gl.INT:
      if (uniform.size > 1) {
        gl.uniform1iv(location, data);
      } else {
        gl.uniform1i(location, data);
      }
      break;
    case gl.FLOAT:
      if (uniform.size > 1) {
        gl.uniform1fv(location, data);
      } else {
        gl.uniform1f(location, data);
      }
      break;
    case gl.FLOAT_MAT3:
      gl.uniformMatrix3fv(location, false, data);
      break;
    case gl.FLOAT_MAT4:
      gl.uniformMatrix4fv(location, false, data);
      break;
    case gl.FLOAT_VEC2:
      if (uniform.size > 1) {
        gl.uniform2fv(location, data);
      } else {
        gl.uniform2f(location, data[0], data[1]);
      }
      break;
    case gl.FLOAT_VEC3:
      if (uniform.size > 1) {
        gl.uniform3fv(location, data);
      } else {
        gl.uniform3f(location, data[0], data[1], data[2]);
      }
      break;
    case gl.FLOAT_VEC4:
      if (uniform.size > 1) {
        gl.uniform4fv(location, data);
      } else {
        gl.uniform4f(location, data[0], data[1], data[2], data[3]);
      }
      break;
    case gl.SAMPLER_2D:
      gl.activeTexture(gl.TEXTURE0 + uniform.samplerIndex);
      uniform.texture = this._renderer.getTexture(data);
      gl.uniform1i(uniform.location, uniform.samplerIndex);
      break;
    //@todo complete all types
  }
  return this;
};

/* NONE OF THIS IS FAST OR EFFICIENT BUT BEAR WITH ME
 *
 * these shader "type" query methods are used by various
 * facilities of the renderer to determine if changing
 * the shader type for the required action (for example,
 * do we need to load the default lighting shader if the
 * current shader cannot handle lighting?)
 *
 **/

p5.Shader.prototype.isLightShader = function() {
  return (
    this.uniforms.uUseLighting !== undefined ||
    this.uniforms.uAmbientLightCount !== undefined ||
    this.uniforms.uDirectionalLightCount !== undefined ||
    this.uniforms.uPointLightCount !== undefined ||
    this.uniforms.uAmbientColor !== undefined ||
    this.uniforms.uDirectionalColor !== undefined ||
    this.uniforms.uPointLightLocation !== undefined ||
    this.uniforms.uPointLightColor !== undefined ||
    this.uniforms.uLightingDirection !== undefined ||
    this.uniforms.uSpecular !== undefined
  );
};

p5.Shader.prototype.isTextureShader = function() {
  return this.samplerIndex > 0;
};

p5.Shader.prototype.isColorShader = function() {
  return (
    this.attributes.aVertexColor !== undefined ||
    this.uniforms.uMaterialColor !== undefined
  );
};

p5.Shader.prototype.isTexLightShader = function() {
  return this.isLightShader() && this.isTextureShader();
};

p5.Shader.prototype.isStrokeShader = function() {
  return this.uniforms.uStrokeWeight !== undefined;
};

/**
 * @method enableAttrib
 * @chainable
 * @private
 */
p5.Shader.prototype.enableAttrib = function(
  loc,
  size,
  type,
  normalized,
  stride,
  offset
) {
  var gl = this._renderer.GL;
  if (loc !== -1) {
    gl.enableVertexAttribArray(loc);
    gl.vertexAttribPointer(loc, size, type, normalized, stride, offset);
  }
  return this;
};

module.exports = p5.Shader;
<|endoftext|>


<commit_before>// Backbone.CrossDomainModel 0.1.0
//
// (c) 2013 Victor Quinn
// Licensed under the MIT license.

(function (root, factory) {
   if (typeof define === "function" && define.amd) {
      // AMD. Register as an anonymous module.
      define(["underscore","backbone"], function(_, Backbone) {
        // Use global variables if the locals are undefined.
        return factory(_ || root._, Backbone || root.Backbone);
      });
   } else {
      // RequireJS isn't being used. Assume underscore and backbone are loaded in <script> tags
      factory(_, Backbone);
   }
}(this, function(_, Backbone) {

    // Helper function to determine the request url given model and options objects
    function requestUrl(model, options) {
        var requestUrl = null;
        // First try the options object
        try {
            requestUrl = options.url;
        } catch(x) {}

        // Then try the model's url
        if (!requestUrl) {
            try {
                requestUrl = _.isFunction(model.url) ? model.url() : model.url;
            } catch(x) {}
        }

        return requestUrl;
    }

    // Helper function to determine whether protocols differ.
    function protocolsDiffer(thisProtocol, requestProtocol) {
        if (thisProtocol === ':' || requestProtocol === ":") {
            return false;
        }

        else if (thisProtocol === requestProtocol) {
            return false;
        }

        return true;
    }

    // Map from CRUD to HTTP for our default `Backbone.sync` implementation.

    var methodMap = {
        'create': 'POST',
        'update': 'PUT',
        'patch':  'PATCH',
        'delete': 'DELETE',
        'read':   'GET'
    };

    Backbone.vanillaSync = Backbone.sync;

    // Override 'Backbone.sync' to default to CrossDomainModel sync.
    // the original 'Backbone.sync' is still available in 'Backbone.vanillaSync'
    Backbone.sync = function(method, model, options) {

        // See if we need to use the XDomainRequest object for IE. If the request is on the
        // same domain, we can fall back on the normal Backbone.ajax handling.
        var useXDomainRequest = false;

        // See https://gist.github.com/jlong/2428561
        var thisDomainParser = document.createElement('a');
        thisDomainParser.href = document.URL;

        var requestDomainParser = document.createElement('a');
        requestDomainParser.href = requestUrl(model, options);

        if (requestDomainParser.host !== "" && (thisDomainParser.host !== requestDomainParser.host)) {
            useXDomainRequest = true;
        }

        // This currently catches IE10 as well which supports XMLHttpRequest so it should
        // probably only trap IE < 10.
        if (useXDomainRequest && window.XDomainRequest) {

            // See this article for more details on all the silly nuances: http://vq.io/14DJ1Tv

            // Check if protocols differ, if so throw an error so the app devs can handle.
            if (protocolsDiffer(thisDomainParser.protocol, requestDomainParser.protocol)) {
                throw new Error('Backbone.CrossDomain only works for same protocol requests (HTTP -> HTTP, HTTPS -> HTTPS) cannot mix.');
            }

            // Basically Backbone.sync rewritten to use XDomainRequest object
            var type = methodMap[method];

            // Default options, unless specified.
            _.defaults(options || (options = {}), {
                emulateHTTP: Backbone.emulateHTTP,
                emulateJSON: Backbone.emulateJSON
            });

            // XDomainRequest only works with POST. So DELETE/PUT/PATCH can't work here.

            // Note: Conscious decision to throw error rather than try to munge the request and
            // do something like force "options.emulateHTTP = true" because we want developers
            // to notice they're trying to do something illegal with this request and it may
            // require server-side changes for compatibility.
            if (!options.emulateHTTP && (method === 'update' || method === 'patch' || method === 'delete')) {
                throw new Error('Backbone.CrossDomain cannot use PUT, PATCH, DELETE with XDomainRequest (IE) and emulateHTTP=false');
            }

            // Default JSON-request options.
            var params = {type: type, dataType: 'json'};

            // Ensure that we have a URL.
            if (!options.url) {
                params.url = _.result(model, 'url') || urlError();
            }

            // TODO: XDomainRequest only accepts text/plain Content-Type header

            // TODO: XDomainRequest doesn't like other headers

            // Ensure that we have the appropriate request data.
            if (options.data == null && model && (method === 'create' || method === 'update' || method === 'patch')) {
                params.contentType = 'application/json';
                params.data = JSON.stringify(options.attrs || model.toJSON(options));
            }

            // For older servers, emulate JSON by encoding the request into an HTML-form.
            if (options.emulateJSON) {
                params.contentType = 'application/x-www-form-urlencoded';
                params.data = params.data ? {model: params.data} : {};
            }

            // For older servers, emulate HTTP by mimicking the HTTP method with `_method`
            // And an `X-HTTP-Method-Override` header.
            if (options.emulateHTTP && (type === 'PUT' || type === 'DELETE' || type === 'PATCH')) {
                params.type = 'POST';
                if (options.emulateJSON) params.data._method = type;
                var beforeSend = options.beforeSend;
                options.beforeSend = function(xhr) {
                    xhr.setRequestHeader('X-HTTP-Method-Override', type);
                    if (beforeSend) return beforeSend.apply(this, arguments);
                };
            }

            // Don't process data on a non-GET request.
            if (params.type !== 'GET' && !options.emulateJSON) {
                params.processData = false;
            }

            var xdr = options.xhr = new XDomainRequest();

            var success = options.success;
            xdr.onload = function(resp) {
                resp = JSON.parse(xdr.responseText);
                if (resp) success(model, resp, options);
                model.trigger('sync', model, resp, options);
            }

            var error = options.error;
            xdr.onerror = function(xdr) {
                if (error) error(model, xdr, options);
                model.trigger('error', model, xdr, options);
            };

            // Make the request using XDomainRequest

            xdr.open(params.type, params.url);
            xdr.send(params.data);

            model.trigger('request', model, xdr, options);
            return xdr;
        }
        else {
            return Backbone.vanillaSync.apply(this, arguments);
        }
    };

    return Backbone;
}));
<commit_msg>Now POST working with XDomainRequest<commit_after>// Backbone.CrossDomainModel 0.1.0
//
// (c) 2013 Victor Quinn
// Licensed under the MIT license.

(function (root, factory) {
   if (typeof define === "function" && define.amd) {
      // AMD. Register as an anonymous module.
      define(["underscore","backbone"], function(_, Backbone) {
        // Use global variables if the locals are undefined.
        return factory(_ || root._, Backbone || root.Backbone);
      });
   } else {
      // RequireJS isn't being used. Assume underscore and backbone are loaded in <script> tags
      factory(_, Backbone);
   }
}(this, function(_, Backbone) {

    // Helper function to determine the request url given model and options objects
    function requestUrl(model, options) {
        var requestUrl = null;
        // First try the options object
        try {
            requestUrl = options.url;
        } catch(x) {}

        // Then try the model's url
        if (!requestUrl) {
            try {
                requestUrl = _.isFunction(model.url) ? model.url() : model.url;
            } catch(x) {}
        }

        return requestUrl;
    }

    // Helper function to determine whether protocols differ.
    function protocolsDiffer(thisProtocol, requestProtocol) {
        if (thisProtocol === ':' || requestProtocol === ":") {
            return false;
        }

        else if (thisProtocol === requestProtocol) {
            return false;
        }

        return true;
    }

    // Map from CRUD to HTTP for our default `Backbone.sync` implementation.

    var methodMap = {
        'create': 'POST',
        'update': 'PUT',
        'patch':  'PATCH',
        'delete': 'DELETE',
        'read':   'GET'
    };

    Backbone.vanillaSync = Backbone.sync;

    // Override 'Backbone.sync' to default to CrossDomainModel sync.
    // the original 'Backbone.sync' is still available in 'Backbone.vanillaSync'
    Backbone.sync = function(method, model, options) {

        // See if we need to use the XDomainRequest object for IE. If the request is on the
        // same domain, we can fall back on the normal Backbone.ajax handling.
        var useXDomainRequest = false;

        // See https://gist.github.com/jlong/2428561
        var thisDomainParser = document.createElement('a');
        thisDomainParser.href = document.URL;

        var requestDomainParser = document.createElement('a');
        requestDomainParser.href = requestUrl(model, options);

        if (requestDomainParser.host !== "" && (thisDomainParser.host !== requestDomainParser.host)) {
            useXDomainRequest = true;
        }

        // This currently catches IE10 as well which supports XMLHttpRequest so it should
        // probably only trap IE < 10.
        if (useXDomainRequest && window.XDomainRequest) {

            // See this article for more details on all the silly nuances: http://vq.io/14DJ1Tv

            // Check if protocols differ, if so throw an error so the app devs can handle.
            if (protocolsDiffer(thisDomainParser.protocol, requestDomainParser.protocol)) {
                throw new Error('Backbone.CrossDomain only works for same protocol requests (HTTP -> HTTP, HTTPS -> HTTPS) cannot mix.');
            }

            // Basically Backbone.sync rewritten to use XDomainRequest object
            var type = methodMap[method];

            // Default options, unless specified.
            _.defaults(options || (options = {}), {
                emulateHTTP: Backbone.emulateHTTP,
                emulateJSON: Backbone.emulateJSON
            });

            // XDomainRequest only works with POST. So DELETE/PUT/PATCH can't work here.

            // Note: Conscious decision to throw error rather than try to munge the request and
            // do something like force "options.emulateHTTP = true" because we want developers
            // to notice they're trying to do something illegal with this request and it may
            // require server-side changes for compatibility.
            if (!options.emulateHTTP && (method === 'update' || method === 'patch' || method === 'delete')) {
                throw new Error('Backbone.CrossDomain cannot use PUT, PATCH, DELETE with XDomainRequest (IE) and emulateHTTP=false');
            }

            // Default JSON-request options.
            var params = {type: type, dataType: 'json'};

            // Ensure that we have a URL.
            if (!options.url) {
                params.url = _.result(model, 'url') || urlError();
            }

            // TODO: XDomainRequest only accepts text/plain Content-Type header

            // TODO: XDomainRequest doesn't like other headers

            // Ensure that we have the appropriate request data.
            if (options.data == null && model && (method === 'create' || method === 'update' || method === 'patch')) {
                params.data = JSON.stringify(options.attrs || model.toJSON(options));
            }

            // For older servers, emulate JSON by encoding the request into an HTML-form.
            if (options.emulateJSON) {
                params.data = params.data ? {model: params.data} : {};
            }

            // For older servers, emulate HTTP by mimicking the HTTP method with `_method`
            // And an `X-HTTP-Method-Override` header.

            if (options.emulateHTTP && (type === 'PUT' || type === 'DELETE' || type === 'PATCH')) {
                params.type = 'POST';
                if (options.emulateJSON) params.data._method = type;
                var beforeSend = options.beforeSend;
                options.beforeSend = function(xhr) {
                    xhr.setRequestHeader('X-HTTP-Method-Override', type);
                    if (beforeSend) return beforeSend.apply(this, arguments);
                };
            }

            // Don't process data on a non-GET request.
            if (params.type !== 'GET' && !options.emulateJSON) {
                params.processData = false;
            }

            // Need to send this along as key/value pairs, can't send JSON blob
            if (params.type === 'POST') {
                params.data = Backbone.$.param(JSON.parse(params.data));
            }

            var xdr = options.xhr = new XDomainRequest();

            var success = options.success;
            xdr.onload = function(resp) {
                resp = JSON.parse(xdr.responseText);
                if (resp) success(model, resp, options);
                model.trigger('sync', model, resp, options);
            }

            var error = options.error;
            xdr.onerror = function(xdr) {
                if (error) error(model, xdr, options);
                model.trigger('error', model, xdr, options);
            };

            // Make the request using XDomainRequest
            xdr.open(params.type, params.url);
            xdr.send(params.data);

            model.trigger('request', model, xdr, options);
            return xdr;
        }
        else {
            return Backbone.vanillaSync.apply(this, arguments);
        }
    };

    return Backbone;
}));
<|endoftext|>


<commit_before>var anysort = require('./');

var sorted;
var unsorted = [
	'path/to/foo.js',
	'path/to/bar.js',
	'bar.js',
	'path/anyjs/baz.js',
	'path/anyjs/aaz.js',
	'path/to/file.js',
	'path/anyjs/caz.js'
];

var matchers = [
	'path/to/file.js',
	'path/anyjs/**/*.js',
	/foo.js$/,
	function (string) {
		return string.indexOf('bar') !== -1 && string.length > 10
	}
];

// `.slice()` prevents `unsorted` from being mutated when using `.sort()`
sorted = unsorted.slice().sort(anysort(matchers));
console.log(sorted);

sorted = unsorted.slice().sort(function (a, b){
	return anysort(a, b, matchers);
});
console.log(sorted);

sorted = anysort.splice(unsorted, matchers);
console.log(sorted);

sorted = anysort.splice(unsorted, matchers).sorted;
console.log(sorted);

var before = /to/;
var after = ['path/anyjs/baz.js', 'path/anyjs/aaz.js'];
sorted = anysort.grouped(unsorted, [before, 'unmatched', after]);
console.log(sorted);

var exclusions = /anyjs/;
sorted = anysort.grouped(unsorted, [exclusions, matchers], [2, 1]);
console.log(sorted);
<commit_msg>Pretty readme script output<commit_after>var assert = require('assert');
var inspect = require('util').inspect;
var i = function (val) {return inspect(val, {colors: true})};
var a = function (val) {return i(val).replace('[','[\n ').replace(' ]','\n]')};

var anysort = require('./');
console.log("var anysort = require('anysort');\n");

var sorted;
var unsorted = [
  'path/to/foo.js',
  'path/to/bar.js',
  'bar.js',
  'path/anyjs/baz.js',
  'path/anyjs/aaz.js',
  'path/to/file.js',
  'path/anyjs/caz.js'
];
console.log('var unsorted =', a(unsorted), ';\n');
var matchers = [
  'path/to/file.js',
  'path/anyjs/**/*.js',
  /foo.js$/,
  function (string) {
    return string.indexOf('bar') !== -1 && string.length > 10;
  }
];
console.log('var matchers =',
  a(matchers).replace('[Function]', matchers[3].toString()), ';\n');

// `.slice()` prevents `unsorted` from being mutated when using `.sort()`
sorted = unsorted.slice().sort(anysort(matchers));
assert.deepEqual(sorted, unsorted.slice().sort(function (a, b){
  return anysort(a, b, matchers);
}));
console.log('// the following two are equivalent');
console.log('unsorted.sort(anysort(matchers));');
console.log('unsorted.sort(function (a, b){');
console.log(' return anysort(a, b, matchers);');
console.log('});');
console.log(i(sorted), '\n');
/*
[ 'path/to/file.js',
  'path/anyjs/aaz.js',
  'path/anyjs/baz.js',
  'path/anyjs/caz.js',
  'path/to/foo.js',
  'path/to/bar.js',
  'bar.js' ]
*/

sorted = anysort.splice(unsorted, matchers);
console.log('anysort.splice(unsorted, matchers);');
console.log(i(sorted), '\n');
/*
{ matched:
   [ 'path/to/file.js',
     'path/anyjs/aaz.js',
     'path/anyjs/baz.js',
     'path/anyjs/caz.js',
     'path/to/foo.js',
     'path/to/bar.js' ],
  unmatched: [ 'bar.js' ],
  sorted:
   [ 'path/to/file.js',
     'path/anyjs/aaz.js',
     'path/anyjs/baz.js',
     'path/anyjs/caz.js',
     'path/to/foo.js',
     'path/to/bar.js',
     'bar.js' ] }
*/

sorted = anysort.splice(unsorted, matchers).sorted;
console.log('// quick access to just the sorted array');
console.log('anysort.splice(unsorted, matchers).sorted;');
console.log(i(sorted), '\n');
/*
[ 'path/to/file.js',
  'path/anyjs/aaz.js',
  'path/anyjs/baz.js',
  'path/anyjs/caz.js',
  'path/to/foo.js',
  'path/to/bar.js',
  'bar.js' ]
*/

var before = /to/;
var after = ['path/anyjs/baz.js', 'path/anyjs/aaz.js'];
sorted = anysort.grouped(unsorted, [before, 'unmatched', after]);
console.log("var before = /to/;");
console.log("var after = ['path/anyjs/baz.js', 'path/anyjs/aaz.js'];");
console.log("anysort.grouped(unsorted, [before, 'unmatched', after]);");
console.log(i(sorted), '\n');
/*
[ 'path/to/bar.js',
  'path/to/file.js',
  'path/to/foo.js',
  'bar.js',
  'path/anyjs/caz.js',
  'path/anyjs/baz.js',
  'path/anyjs/aaz.js' ]
*/

var exclusions = /anyjs/;
sorted = anysort.grouped(unsorted, [exclusions, matchers], [2, 1]);
console.log('var exclusions = /anyjs/;');
console.log('// 2 is the index for unmatched list members');
console.log('anysort.grouped(unsorted, [exclusions, matchers], [2, 1]);');
console.log(i(sorted), '\n');
/*
[ 'bar.js',
  'path/to/file.js',
  'path/to/foo.js',
  'path/to/bar.js' ]
*/
<|endoftext|>


<commit_before>var Character = Class.extend({
    position: { x: 25, y: 155},
    lastPosition: { x: 0, y: 0 },
    velocity: { x: 0, y: 0 },
    facingEast: true,

    movementSpeed: 1,
    jumpSpeed: 10,

    canJump: true,
    canFire: true,

    size: { w: 15, h: 20 },
    txBody: new Image(),
    txBodyWhite: null,
    txCorpse: null,
    txCorpseSize: { w: 25, h: 9 },

    isVulnerable: true,
    hitBoxMargin: 3,
    isHurting: false,
    hurtCounter: 0,
    healthNow: 0,
    healthMax: 100,
    dead: false,

    init: function() {
        this.movementSpeed = 1;
        this.jumpSpeed = 10;
        this.canJump = true;
        this.canFire = true;
        this.facingEast = true;
        this.velocity = { x: 0, y: 0 };
        this.size = { w: 15, h: 20 };
        this.hitBoxMargin = 3;

        this.isVulnerable = true;
        this.isHurting = false;
        this.hurtCounter = 0;
        this.healthMax = 100;
        this.healthNow = this.healthMax;
        this.dead = false;

        this.txBody = new Image();
        this.txBody.onload = this.generateHurtSprite.bind(this);

        this.txBodyWhite = this.txBody; // avoid breakage, generateHurtSprite() will update this later

        this.txCorpse = new Image();
        this.txCorpse.src = 'assets/textures/enemy_dead.png';
    },

    generateHurtSprite: function() {
        var canvas = document.createElement('canvas');
        var ctx = canvas.getContext('2d');

        canvas.width = this.size.w;
        canvas.height = this.size.h;

        ctx.drawImage(this.txBody, 0, 0, this.size.w, this.size.h);

        var spriteData = ctx.getImageData(0, 0, this.size.w, this.size.h);
        var pixelData = spriteData.data;

        var hurtColor = [255, 255, 255]; // RGB format

        // Iterate the pixels in the first frame of the sprite we just loaded.
        // Each pixel has 4 bytes of information: Red, Green, Blue and Alpha.
        for (var i = 0; i < pixelData.length; i += 4) {
            pixelData[i] = hurtColor[0];
            pixelData[i + 1] = hurtColor[1];
            pixelData[i + 2] = hurtColor[2];
            // Pixels that were not previously set should still have a zero alpha (invisible).
            // We don't change alpha (the 4th value) but leave it as is.
            // End result: the sprite, where pixels were already visible, will have our color.
        }

        spriteData.data = pixelData;
        ctx.putImageData(spriteData, 0, 0);

        this.txBodyWhite = canvas;
    },

    drawExtras: function(ctx) {
        // To be implemented by children where appropriate
    },

    draw: function(ctx) {
        if (this.dead) {
            ctx.drawImage(this.txCorpse, 0, 0, this.txCorpseSize.w, this.txCorpseSize.h, this.position.x, this.position.y - (this.txCorpseSize.h - this.size.h), this.txCorpseSize.w, this.txCorpseSize.h);
            return;
        }

        ctx.save();

        if (!this.facingEast) {
            ctx.translate(this.position.x + 16, this.position.y);
            ctx.scale(-1, 1);
        } else {
            ctx.translate(this.position.x, this.position.y);
        }

        ctx.drawImage(this.isHurting ? this.txBodyWhite : this.txBody, 0, 0, this.size.w, this.size.h, 0, 0, this.size.w, this.size.h);

        this.drawExtras(ctx);

        ctx.restore();

        if (window.DEBUG_PROJECTILES && !this.dead) {
            var sq = this.getRect();

            ctx.beginPath();
            ctx.rect(sq.left, sq.top, sq.width, sq.height);
            ctx.lineWidth = 1;
            ctx.strokeStyle = 'red';
            ctx.stroke();
        }
    },

    hurt: function(projectile, damage) {
        if (!this.isVulnerable || this.dead) {
            return;
        }

        if (damage > 0) {
            Sfx.hurt();

            this.isHurting = true;
            this.hurtCounter = 2;
            this.healthNow -= damage;

            if (this.healthNow <= 0) {
                this.die();
            }
        }

        this.knockBack(1, projectile.velocity.x < 0);
    },

    jump: function() {
        this.velocity.y = -this.jumpSpeed;
        this.canJump = false;

        if (this === Map.player) {
            Sfx.jump();
        }
    },

    die: function() {
        this.dead = true;
        this.healthNow = 0;
    },

    knockBack: function(knockbackVelocity, toEast) {
        knockbackVelocity += Math.random();

        if (toEast) {
            knockbackVelocity = -knockbackVelocity;
        }

        this.velocity.x = knockbackVelocity;
    },

    update: function() {
        this.lastPosition = { x: this.position.x, y: this.position.y };

        /*** Movement processing ***/
        this.position.x += this.velocity.x;
        this.position.y += this.velocity.y;

        // Downward gravity
        this.velocity.y += Map.gravity;

        // Sideways gravity (for knockbacks etc)
        if (this.velocity.x > 0) {
            this.velocity.x -= Map.gravity / 2;

            if (this.velocity.x < 0) {
                this.velocity.x = 0;
            }
        } else if (this.velocity.x < 0) {
            this.velocity.x += Map.gravity / 2;

            if (this.velocity.x > 0) {
                this.velocity.x = 0;
            }
        }

        /*** Collision detection ***/
        var bounds = this.getRect();

        if (bounds.bottom > Renderer.canvas.height) {
            this.velocity.y = 0;
            this.position.y = Renderer.canvas.height - bounds.height;
            this.canJump = true; // restore jumping powers if we have landed safely
        }

        /*** Hurt animation ***/
        if (this.isHurting && this.hurtCounter <= 0) {
            this.isHurting = false;
        }
        else if (this.hurtCounter > 0) {
            this.hurtCounter--;
        }
    },

    getRect: function() {
        return {
            top: this.position.y,
            left: this.position.x + this.hitBoxMargin,
            height: this.size.h,
            width: this.size.w - (this.hitBoxMargin * 2),
            bottom: this.position.y + this.size.h,
            right: this.position.x + this.size.w - (this.hitBoxMargin * 2)
        };
    }
});<commit_msg>Character rendering: round the rendering positions to whole pixels to prevent "fuzzing"<commit_after>var Character = Class.extend({
    position: { x: 25, y: 155},
    lastPosition: { x: 0, y: 0 },
    velocity: { x: 0, y: 0 },
    facingEast: true,

    movementSpeed: 1,
    jumpSpeed: 10,

    canJump: true,
    canFire: true,

    size: { w: 15, h: 20 },
    txBody: new Image(),
    txBodyWhite: null,
    txCorpse: null,
    txCorpseSize: { w: 25, h: 9 },

    isVulnerable: true,
    hitBoxMargin: 3,
    isHurting: false,
    hurtCounter: 0,
    healthNow: 0,
    healthMax: 100,
    dead: false,

    init: function() {
        this.movementSpeed = 1;
        this.jumpSpeed = 10;
        this.canJump = true;
        this.canFire = true;
        this.facingEast = true;
        this.velocity = { x: 0, y: 0 };
        this.size = { w: 15, h: 20 };
        this.hitBoxMargin = 3;

        this.isVulnerable = true;
        this.isHurting = false;
        this.hurtCounter = 0;
        this.healthMax = 100;
        this.healthNow = this.healthMax;
        this.dead = false;

        this.txBody = new Image();
        this.txBody.onload = this.generateHurtSprite.bind(this);

        this.txBodyWhite = this.txBody; // avoid breakage, generateHurtSprite() will update this later

        this.txCorpse = new Image();
        this.txCorpse.src = 'assets/textures/enemy_dead.png';
    },

    generateHurtSprite: function() {
        var canvas = document.createElement('canvas');
        var ctx = canvas.getContext('2d');

        canvas.width = this.size.w;
        canvas.height = this.size.h;

        ctx.drawImage(this.txBody, 0, 0, this.size.w, this.size.h);

        var spriteData = ctx.getImageData(0, 0, this.size.w, this.size.h);
        var pixelData = spriteData.data;

        var hurtColor = [255, 255, 255]; // RGB format

        // Iterate the pixels in the first frame of the sprite we just loaded.
        // Each pixel has 4 bytes of information: Red, Green, Blue and Alpha.
        for (var i = 0; i < pixelData.length; i += 4) {
            pixelData[i] = hurtColor[0];
            pixelData[i + 1] = hurtColor[1];
            pixelData[i + 2] = hurtColor[2];
            // Pixels that were not previously set should still have a zero alpha (invisible).
            // We don't change alpha (the 4th value) but leave it as is.
            // End result: the sprite, where pixels were already visible, will have our color.
        }

        spriteData.data = pixelData;
        ctx.putImageData(spriteData, 0, 0);

        this.txBodyWhite = canvas;
    },

    drawExtras: function(ctx) {
        // To be implemented by children where appropriate
    },

    draw: function(ctx) {
        if (this.dead) {
            ctx.drawImage(this.txCorpse, 0, 0, this.txCorpseSize.w, this.txCorpseSize.h, this.position.x, this.position.y - (this.txCorpseSize.h - this.size.h), this.txCorpseSize.w, this.txCorpseSize.h);
            return;
        }

        ctx.save();

        if (!this.facingEast) {
            ctx.translate(Math.round(this.position.x + 16), Math.round(this.position.y));
            ctx.scale(-1, 1);
        } else {
            ctx.translate(Math.round(this.position.x), Math.round(this.position.y));
        }

        ctx.drawImage(this.isHurting ? this.txBodyWhite : this.txBody, 0, 0, this.size.w, this.size.h, 0, 0, this.size.w, this.size.h);

        this.drawExtras(ctx);

        ctx.restore();

        if (window.DEBUG_PROJECTILES && !this.dead) {
            var sq = this.getRect();

            ctx.beginPath();
            ctx.rect(sq.left, sq.top, sq.width, sq.height);
            ctx.lineWidth = 1;
            ctx.strokeStyle = 'red';
            ctx.stroke();
        }
    },

    hurt: function(projectile, damage) {
        if (!this.isVulnerable || this.dead) {
            return;
        }

        if (damage > 0) {
            Sfx.hurt();

            this.isHurting = true;
            this.hurtCounter = 2;
            this.healthNow -= damage;

            if (this.healthNow <= 0) {
                this.die();
            }
        }

        this.knockBack(1, projectile.velocity.x < 0);
    },

    jump: function() {
        this.velocity.y = -this.jumpSpeed;
        this.canJump = false;

        if (this === Map.player) {
            Sfx.jump();
        }
    },

    die: function() {
        this.dead = true;
        this.healthNow = 0;
    },

    knockBack: function(knockbackVelocity, toEast) {
        knockbackVelocity += Math.random();

        if (toEast) {
            knockbackVelocity = -knockbackVelocity;
        }

        this.velocity.x = knockbackVelocity;
    },

    update: function() {
        this.lastPosition = { x: this.position.x, y: this.position.y };

        /*** Movement processing ***/
        this.position.x += this.velocity.x;
        this.position.y += this.velocity.y;

        // Downward gravity
        this.velocity.y += Map.gravity;

        // Sideways gravity (for knockbacks etc)
        if (this.velocity.x > 0) {
            this.velocity.x -= Map.gravity / 2;

            if (this.velocity.x < 0) {
                this.velocity.x = 0;
            }
        } else if (this.velocity.x < 0) {
            this.velocity.x += Map.gravity / 2;

            if (this.velocity.x > 0) {
                this.velocity.x = 0;
            }
        }

        /*** Collision detection ***/
        var bounds = this.getRect();

        if (bounds.bottom > Renderer.canvas.height) {
            this.velocity.y = 0;
            this.position.y = Renderer.canvas.height - bounds.height;
            this.canJump = true; // restore jumping powers if we have landed safely
        }

        /*** Hurt animation ***/
        if (this.isHurting && this.hurtCounter <= 0) {
            this.isHurting = false;
        }
        else if (this.hurtCounter > 0) {
            this.hurtCounter--;
        }
    },

    getRect: function() {
        return {
            top: this.position.y,
            left: this.position.x + this.hitBoxMargin,
            height: this.size.h,
            width: this.size.w - (this.hitBoxMargin * 2),
            bottom: this.position.y + this.size.h,
            right: this.position.x + this.size.w - (this.hitBoxMargin * 2)
        };
    }
});<|endoftext|>


<commit_before>var React = require('react');
var ReactDOM = require('react-dom');
var utilities = require("../../../node_modules/simple-react-utilities/js/utilities.js");

import cardData from '../json/monsters.json';
import filterOptions from '../json/filter-configs.json';
import Filter from '../../../node_modules/card-filters/src/www/js/filter.js';
import { CardBook } from '../../../node_modules/card-filters/src/www/js/cards.js'
import { Card } from '../../../node_modules/card-filters/src/www/js/components/card.js'
import { FilterButtonGroup } from '../../../node_modules/card-filters/src/www/js/components/filter-button-group.js';
import { FilterButton } from '../../../node_modules/card-filters/src/www/js/components/filter-button.js';
import { TextInput } from '../../../node_modules/simple-react-forms/form-fields/text-input.js';
import { ShowHideButton } from '../../../node_modules/card-filters/src/www/js/components/show-hide-button.js';
import { SubmitButton } from '../../../node_modules/simple-react-forms/form-fields/submit-button.js';

class MonsterBook extends React.Component {
  /**
   * constructor for CardBook
   * @param  {object} props [element properties/attributes passed in at initialization]
   */
  constructor(props) {
    super(props);

    this.searchFilter = this.searchFilter.bind(this);
    this.renderCards = this.renderCards.bind(this);
  }

  searchFilter(activate,deactivate) {
    var filter = new Filter(cardData);
    var _this = this;

    var searchOptions = {
      filterName : "level",
      filterFunc : filter.by.search,
      onActivate : activate,
      onDeactivate : deactivate
    }

    function update(e) {
      if (typeof activate === "function") {
        activate('search',{filterFunc : filter.by.search, filterArgs : [false, e.target.value]}); // TODO: this boolean indicates description search
      }
    }

    function clearInput() {
      let inputElem = document.querySelector('.form-control');
      inputElem.value = "";
      activate('search',{filterFunc : filter.by.search, filterArgs : [false, ""]}); // TODO: this boolean indicates description search
    }

    return

            <TextInput className="filterByText" label="Search" type="search" name="filterByText" onChange={update} />

x

  }

  crFilters(activate,deactivate) {
    var filter = new Filter(cardData);
    var buttonOptions = {
      filterName : "CR",
      filterFunc : filter.by.value,
      onActivate : activate,
      onDeactivate : deactivate
    }

    return

CR

              <FilterButtonGroup cardData={cardData} buttonOptions={buttonOptions} filters={filterOptions.cr} />

  }
  /**
   * [typeFilters description]
   * @param  {[type]} activate
   * @param  {[type]} deactivate [description]
   * @return {[type]}            [description]
   */
  typeFilters(activate,deactivate) {
    var filter = new Filter(cardData);

    function update(e) {
      let activeBtn = document.querySelector(".active.type-filter-btn");
      let activeFilterProp = activeBtn.getAttribute('data-filter-prop');
      let activeFilterValue = activeBtn.getAttribute('data-filter-value');

      if (e.target.className.indexOf("active") === -1) {
        activeBtn.className = activeBtn.className.replace(/active/g,'');
        e.target.className += " active";

        activate('type', {
          filterArgs : [e.target.getAttribute('data-filter-prop'), e.target.getAttribute('data-filter-value')],
          filterFunc : filter.by.match
        });

        deactivate('type', {
          filterArgs : [activeFilterProp, activeFilterValue],
          filterFunc : filter.by.match,
          exclude : true
        });
      }

    }

    function showAll(e) {
      let activeBtn = document.querySelector(".active.type-filter-btn");
      let activeFilterProp = activeBtn.getAttribute('data-filter-prop');
      let activeFilterValue = activeBtn.getAttribute('data-filter-value');

      if (e.target.className.indexOf("active") === -1) {
        activeBtn.className = activeBtn.className.replace(/active/g,'');
        e.target.className += " active";

        activate('type', {
          filterArgs : [e.target.getAttribute('data-filter-prop'), e.target.getAttribute('data-filter-value')],
          filterFunc : filter.by.all
        });

        deactivate('type', {
          filterArgs : [activeFilterProp, activeFilterValue],
          filterFunc : filter.by.match,
          exclude : true
        });
      }
    }

    function getClassName(index) {
      const activeClass = index === 0 ? ' active' : '';
      return "btn type-filter-btn" + activeClass;
    }

    var buttons = function() {
      return filterOptions.type.map((button, index) => {
        return (

            {button.label}

        )
      });
    }

    return

                {buttons()}

Show all types (slow)

  }

  sourceFilters(activate,deactivate) {
    var filter = new Filter(cardData);
    var buttonOptions = {
      filterName : "Source",
      filterFunc : filter.by.match,
      onActivate : activate,
      onDeactivate : deactivate
    }

    return

Source

              <FilterButtonGroup cardData={cardData} buttonOptions={buttonOptions} filters={filterOptions.source} />

  }

  renderCards(cardsData, saveFunc) {
    var i = 0;
    var cardsArr = cardsData || [];

    function cardId(card) {
      return card.name.toLowerCase()
                      .replace(/\s/g,'-')
                      .replace(/'/g,'')
                      .replace(/\//g,'-');
    }

    function abilityScoreModifier(score) {
      let modifier = Math.floor((score - 10)/2);
      return modifier > -1 ? '+'+modifier : modifier;
    }

    function creatureSpecialAbilities(creature) {
      if(Array.isArray(creature.special_abilities)) {
        return creature.special_abilities.map((ability) => {
          return (

              {ability.name}.

          )
        });
      }
    }

    function creatureActions(creature) {
      if(Array.isArray(creature.actions)) {
        return creature.actions.map((action) => {
          return (

              {action.name}.

          )
        });
      }
    }

    function saveCard(e) {
      const cardObject = utilities.getObjectByName(cardsData,e.target.getAttribute('data-card-name'));
      e.target.className += " saved"

      saveFunc(cardObject);
    }

    function searchString(creatureName) {

      return "https://www.google.com/search?safe=active&tbm=isch&q="+creatureName.toLowerCase().replace(/\s/g,'+');;
    }

    let cards =

      {cardsArr.map((creature) => {
        const index = Math.floor(Math.random() * 1001)
        return

{creature.name} [IMG]
Save

          <ShowHideButton target={"."+cardId(creature)+index+" .card-content"} showText="+" hideText="-" />
          <ShowHideButton target={"."+cardId(creature)+index+" .card-content"} showText="+" hideText="-" startClosed="true" />

{creature.size} {creature.type}, {creature.alignment}

Armor class {creature.ac}

Hit Points {creature.hp}

Speed {creature.speed}

                  STR
                  DEX
                  CON
                  INT
                  WIS
                  CHA

                  {creature.str} ({abilityScoreModifier(creature.str)})
                  {creature.dex} ({abilityScoreModifier(creature.dex)})
                  {creature.con} ({abilityScoreModifier(creature.con)})
                  {creature.int} ({abilityScoreModifier(creature.int)})
                  {creature.wis} ({abilityScoreModifier(creature.wis)})
                  {creature.cha} ({abilityScoreModifier(creature.cha)})

Vulnerabilities {creature.vulnerable}

Damage Immunities {creature.immune}

Condition Immunities {creature.conditionImmune}

Senses {creature.senses}
     Perception {creature.passive}

Languages {creature.languages}

Challenge {creature.cr}

              {creatureSpecialAbilities(creature)}

Actions

              {creatureActions(creature)}

{creature.source}

})}

;

    if (!cardsData || cardsData.length === 0) {
      cards =

No cards matching the selected filters

    }

    return cards;
  }

  /**
   * puts everything in the DOM
   */
  render() {
    const filter = new Filter(cardData);
    const startData = filter.by.match(['type','aberration'],cardData);
    const startFilter = {
      'type' : {
        'filterArgs' : [['type','aberration']],
        'filterFunc' : filter.by.match
      }
    }

    function uniqueData () {
      let uniqueData = [];
      let isUnique = true;
      let i = 0, j = 0, l = 0, k = 0;

      for (i = 0, l = cardData.length; i < l; i++) {
        isUnique = true;

        for (j = 0, k = uniqueData.length; j < k; j++) {
          if (cardData[i].name === uniqueData[j].name) {
            isUnique = false;
          }
        }

        if (isUnique) {
          uniqueData.push(cardData[i])
        }

      }

      uniqueData = utilities.sortObjectsByProp(uniqueData, 'name');

     return
 {cardData.length}

      {uniqueData.length}

      {JSON.stringify(uniqueData)}

    }

    function uniqueProps (prop) {
      let uniqueProps = [];
      let isUnique = true;
      let i = 0, j = 0, l = 0, k = 0;

      for (i = 0, l = cardData.length; i < l; i++) {
        isUnique = true;

        for (j = 0, k = uniqueProps.length; j < k; j++) {
          if (cardData[i].source === uniqueProps[j].source) {
            isUnique = false;
          }
        }

        if (isUnique) {
          uniqueProps.push(cardData[i].source)
        }

      }

     return
{uniqueProps.map((prop) => {
      return
{prop}

     })}
;
    }

    return

              <CardBook
                label="Monsters"
                cardData={cardData}
                startData={startData}
                startFilter={startFilter}
                searchFilter={this.searchFilter}
                navigation={this.typeFilters}
                filters={[this.crFilters, this.sourceFilters]}
                renderCards={this.renderCards}
                advancedFilters={[/*this.componentFilters,this.optionsFilters,this.schoolFilters,this.durationFilters,this.castingTimeFilters,this.rangeFilters,this.sourceFilters*/]}
              />

;
  }
}

ReactDOM.render(<MonsterBook />, document.querySelector('main'));
<commit_msg>conditionally show properties on card<commit_after>var React = require('react');
var ReactDOM = require('react-dom');
var utilities = require("../../../node_modules/simple-react-utilities/js/utilities.js");

import cardData from '../json/monsters.json';
import filterOptions from '../json/filter-configs.json';
import Filter from '../../../node_modules/card-filters/src/www/js/filter.js';
import { CardBook } from '../../../node_modules/card-filters/src/www/js/cards.js'
import { Card } from '../../../node_modules/card-filters/src/www/js/components/card.js'
import { FilterButtonGroup } from '../../../node_modules/card-filters/src/www/js/components/filter-button-group.js';
import { FilterButton } from '../../../node_modules/card-filters/src/www/js/components/filter-button.js';
import { TextInput } from '../../../node_modules/simple-react-forms/form-fields/text-input.js';
import { ShowHideButton } from '../../../node_modules/card-filters/src/www/js/components/show-hide-button.js';
import { SubmitButton } from '../../../node_modules/simple-react-forms/form-fields/submit-button.js';

class MonsterBook extends React.Component {
  /**
   * constructor for CardBook
   * @param  {object} props [element properties/attributes passed in at initialization]
   */
  constructor(props) {
    super(props);

    this.searchFilter = this.searchFilter.bind(this);
    this.renderCards = this.renderCards.bind(this);
  }

  searchFilter(activate,deactivate) {
    var filter = new Filter(cardData);
    var _this = this;

    var searchOptions = {
      filterName : "level",
      filterFunc : filter.by.search,
      onActivate : activate,
      onDeactivate : deactivate
    }

    function update(e) {
      if (typeof activate === "function") {
        activate('search',{filterFunc : filter.by.search, filterArgs : [false, e.target.value]}); // TODO: this boolean indicates description search
      }
    }

    function clearInput() {
      let inputElem = document.querySelector('.form-control');
      inputElem.value = "";
      activate('search',{filterFunc : filter.by.search, filterArgs : [false, ""]}); // TODO: this boolean indicates description search
    }

    return

            <TextInput className="filterByText" label="Search" type="search" name="filterByText" onChange={update} />

x

  }

  crFilters(activate,deactivate) {
    var filter = new Filter(cardData);
    var buttonOptions = {
      filterName : "CR",
      filterFunc : filter.by.value,
      onActivate : activate,
      onDeactivate : deactivate
    }

    return

CR

              <FilterButtonGroup cardData={cardData} buttonOptions={buttonOptions} filters={filterOptions.cr} />

  }
  /**
   * [typeFilters description]
   * @param  {[type]} activate
   * @param  {[type]} deactivate [description]
   * @return {[type]}            [description]
   */
  typeFilters(activate,deactivate) {
    var filter = new Filter(cardData);

    function update(e) {
      let activeBtn = document.querySelector(".active.type-filter-btn");
      let activeFilterProp = activeBtn.getAttribute('data-filter-prop');
      let activeFilterValue = activeBtn.getAttribute('data-filter-value');

      if (e.target.className.indexOf("active") === -1) {
        activeBtn.className = activeBtn.className.replace(/active/g,'');
        e.target.className += " active";

        activate('type', {
          filterArgs : [e.target.getAttribute('data-filter-prop'), e.target.getAttribute('data-filter-value')],
          filterFunc : filter.by.match
        });

        deactivate('type', {
          filterArgs : [activeFilterProp, activeFilterValue],
          filterFunc : filter.by.match,
          exclude : true
        });
      }

    }

    function showAll(e) {
      let activeBtn = document.querySelector(".active.type-filter-btn");
      let activeFilterProp = activeBtn.getAttribute('data-filter-prop');
      let activeFilterValue = activeBtn.getAttribute('data-filter-value');

      if (e.target.className.indexOf("active") === -1) {
        activeBtn.className = activeBtn.className.replace(/active/g,'');
        e.target.className += " active";

        activate('type', {
          filterArgs : [e.target.getAttribute('data-filter-prop'), e.target.getAttribute('data-filter-value')],
          filterFunc : filter.by.all
        });

        deactivate('type', {
          filterArgs : [activeFilterProp, activeFilterValue],
          filterFunc : filter.by.match,
          exclude : true
        });
      }
    }

    function getClassName(index) {
      const activeClass = index === 0 ? ' active' : '';
      return "btn type-filter-btn" + activeClass;
    }

    var buttons = function() {
      return filterOptions.type.map((button, index) => {
        return (

            {button.label}

        )
      });
    }

    return

                {buttons()}

Show all types (slow)

  }

  sourceFilters(activate,deactivate) {
    var filter = new Filter(cardData);
    var buttonOptions = {
      filterName : "Source",
      filterFunc : filter.by.match,
      onActivate : activate,
      onDeactivate : deactivate
    }

    return

Source

              <FilterButtonGroup cardData={cardData} buttonOptions={buttonOptions} filters={filterOptions.source} />

  }

  renderCards(cardsData, saveFunc) {
    var i = 0;
    var cardsArr = cardsData || [];

    function cardId(card) {
      return card.name.toLowerCase()
                      .replace(/\s/g,'-')
                      .replace(/'/g,'')
                      .replace(/\//g,'-');
    }

    function abilityScoreModifier(score) {
      let modifier = Math.floor((score - 10)/2);
      return modifier > -1 ? '+'+modifier : modifier;
    }

    function creatureSpecialAbilities(creature) {
      if(Array.isArray(creature.special_abilities)) {
        return creature.special_abilities.map((ability) => {
          return (

              {ability.name}.

          )
        });
      }
    }

    function creatureActions(creature) {
      if(Array.isArray(creature.actions)) {
        return creature.actions.map((action) => {
          return (

              {action.name}.

          )
        });
      }
    }

    function saveCard(e) {
      const cardObject = utilities.getObjectByName(cardsData,e.target.getAttribute('data-card-name'));
      e.target.className += " saved"

      saveFunc(cardObject);
    }

    function searchString(creatureName) {

      return "https://www.google.com/search?safe=active&tbm=isch&q="+creatureName.toLowerCase().replace(/\s/g,'+');;
    }

    function displayProperty(label, value) {
      if (value) {
        return
{label} {value}

      }
    }

    let cards =

      {cardsArr.map((creature) => {
        const index = Math.floor(Math.random() * 1001)
        return

{creature.name} [IMG]
Save

          <ShowHideButton target={"."+cardId(creature)+index+" .card-content"} showText="+" hideText="-" />
          <ShowHideButton target={"."+cardId(creature)+index+" .card-content"} showText="+" hideText="-" startClosed="true" />

{creature.size} {creature.type}, {creature.alignment}

Armor class {creature.ac}

Hit Points {creature.hp}

Speed {creature.speed}

                  STR
                  DEX
                  CON
                  INT
                  WIS
                  CHA

                  {creature.str} ({abilityScoreModifier(creature.str)})
                  {creature.dex} ({abilityScoreModifier(creature.dex)})
                  {creature.con} ({abilityScoreModifier(creature.con)})
                  {creature.int} ({abilityScoreModifier(creature.int)})
                  {creature.wis} ({abilityScoreModifier(creature.wis)})
                  {creature.cha} ({abilityScoreModifier(creature.cha)})

              {displayProperty("Vulnerabilities",creature.vulnerable)}
              {displayProperty("Damage Immunities",creature.immune)}
              {displayProperty("Condition Immunities",creature.conditionImmune)}
              {displayProperty("Senses",creature.senses)}

    Perception {creature.passive}

              {displayProperty("Languages",creature.languages)}
              {displayProperty("Challenge",creature.cr)}

              {creatureSpecialAbilities(creature)}

Actions

              {creatureActions(creature)}

{creature.source}

})}

;

    if (!cardsData || cardsData.length === 0) {
      cards =

No cards matching the selected filters

    }

    return cards;
  }

  /**
   * puts everything in the DOM
   */
  render() {
    const filter = new Filter(cardData);
    const startData = filter.by.match(['type','aberration'],cardData);
    const startFilter = {
      'type' : {
        'filterArgs' : [['type','aberration']],
        'filterFunc' : filter.by.match
      }
    }

    function uniqueData () {
      let uniqueData = [];
      let isUnique = true;
      let i = 0, j = 0, l = 0, k = 0;

      for (i = 0, l = cardData.length; i < l; i++) {
        isUnique = true;

        for (j = 0, k = uniqueData.length; j < k; j++) {
          if (cardData[i].name === uniqueData[j].name) {
            isUnique = false;
          }
        }

        if (isUnique) {
          uniqueData.push(cardData[i])
        }

      }

      uniqueData = utilities.sortObjectsByProp(uniqueData, 'name');

     return
 {cardData.length}

      {uniqueData.length}

      {JSON.stringify(uniqueData)}

    }

    function uniqueProps (prop) {
      let uniqueProps = [];
      let isUnique = true;
      let i = 0, j = 0, l = 0, k = 0;

      for (i = 0, l = cardData.length; i < l; i++) {
        isUnique = true;

        for (j = 0, k = uniqueProps.length; j < k; j++) {
          if (cardData[i].source === uniqueProps[j].source) {
            isUnique = false;
          }
        }

        if (isUnique) {
          uniqueProps.push(cardData[i].source)
        }

      }

     return
{uniqueProps.map((prop) => {
      return
{prop}

     })}
;
    }

    return

              <CardBook
                label="Monsters"
                cardData={cardData}
                startData={startData}
                startFilter={startFilter}
                searchFilter={this.searchFilter}
                navigation={this.typeFilters}
                filters={[this.crFilters, this.sourceFilters]}
                renderCards={this.renderCards}
                advancedFilters={[/*this.componentFilters,this.optionsFilters,this.schoolFilters,this.durationFilters,this.castingTimeFilters,this.rangeFilters,this.sourceFilters*/]}
              />

;
  }
}

ReactDOM.render(<MonsterBook />, document.querySelector('main'));
<|endoftext|>


<commit_before>import _ from 'messages'
import CopyToClipboard from 'react-copy-to-clipboard'
import forEach from 'lodash/forEach'
import isEmpty from 'lodash/isEmpty'
import map from 'lodash/map'
import React, { Component } from 'react'
import sortBy from 'lodash/sortBy'
import Tags from 'tags'
import xo from 'xo'
import { createSelector } from 'reselect'
import { FormattedRelative } from 'react-intl'
import { Row, Col } from 'grid'
import { Tab, Tabs, TabList, TabPanel } from 'react-tabs'
import {
  connectStore,
  createCollectionSelector,
  Debug,
  formatSize,
  normalizeXenToolsStatus,
  osFamily
} from 'utils'
import {
  CpuSparkLines,
  MemorySparkLines,
  VifSparkLines,
  XvdSparkLines
} from 'xo-sparklines'

import VmActionBar from './action-bar'

// ===================================================================

@connectStore(() => {
  const getVifs = createSelector(
    createCollectionSelector(
      createSelector(
        (_, vm) => vm.VIFs,
        (objects) => objects,
        (vifIds, objects) => map(vifIds, (id) => objects[id])
      )
    ),
    (vifs) => sortBy(vifs, 'device')
  )
  const getNetworkByVifs = createCollectionSelector(
    createSelector(
      (objects) => objects,
      (_, vifs) => vifs,
      (objects, vifs) => {
        const networkByVifs = {}
        forEach(vifs, (vif) => {
          networkByVifs[vif.id] = objects[vif.$network]
        })
        return networkByVifs
      }
    )
  )

  return (state, props) => {
    const { objects } = state
    const { id } = props.params

    const vm = objects[id]
    if (!vm) {
      return {}
    }

    const vifs = getVifs(objects, vm)

    return {
      container: objects[vm.$container],
      networkByVifs: getNetworkByVifs(objects, vifs),
      pool: objects[vm.$pool],
      vifs,
      vm
    }
  }
})
export default class Vm extends Component {
  componentWillMount () {
    const vmId = this.props.params.id
    const loop = () => {
      xo.call('vm.stats', { id: vmId }).then((newStats) => {
        this.setState({
          stats: newStats.stats
        })
      })

      this.timeout = setTimeout(loop, 5000)
    }

    loop()
  }

  componentWillUnmount () {
    clearTimeout(this.timeout)
  }

  render () {
    const {
      container,
      pool,
      vifs,
      networkByVifs,
      vm
    } = this.props

    const { stats } = this.state || {}

    if (!vm) {
      return
Loading…

    }

    return

      <Row>
        <Col size={6}>

            {vm.name_label}
             - {container.name_label} ({pool.name_label})

{vm.name_description}

        </Col>
        <Col size={6}>

            <VmActionBar vm={vm} handlers={this.props}/>

        </Col>
      </Row>
      <Tabs>
        <TabList>
          <Tab>{_('generalTabName')}</Tab>
          <Tab>{_('statsTabName')}</Tab>
          <Tab>{_('consoleTabName')}</Tab>
          <Tab>{_('disksTabName', { disks: vm.$VBDs.length })}</Tab>
          <Tab>{_('networkTabName')}</Tab>
          <Tab>{_('snapshotsTabName')}</Tab>
          <Tab>{_('logsTabName')}</Tab>
          <Tab>{_('advancedTabName')}</Tab>
        </TabList>
        <TabPanel>
          { /* TODO: use CSS style */ }

          <Row className='text-xs-center'>
            <Col size={3}>

{vm.CPUs.number}x

              {stats && stats.cpus && <CpuSparkLines data={stats.cpus} />}
            </Col>
            <Col size={3}>
              { /* TODO: compute nicely RAM units */ }

{formatSize(vm.memory.size)}

              {stats && <MemorySparkLines data={stats} />}
            </Col>
            <Col size={3}>
              { /* TODO: compute total disk usage */ }

{vm.$VBDs.length}x

              {stats && stats.xvds && <XvdSparkLines data={stats.xvds} />}
            </Col>
            <Col size={3}>

{vm.VIFs.length}x

              {stats && stats.vifs && <VifSparkLines data={stats.vifs} />}
            </Col>
          </Row>
          { /* TODO: use CSS style */ }

          {vm.xenTools
            ? <Row className='text-xs-center'>
              <Col size={3}>
                {vm.power_state === 'Running'
                  ?

{_('started')} <FormattedRelative value={vm.startTime * 1000}/>

                  : null
                }
              </Col>
              <Col size={3}>

                  {vm.virtualizationMode === 'pv'
                    ? _('paraVirtualizedMode')
                    : _('hardwareVirtualizedMode')
                  }

              </Col>
              <Col size={3}>
                { /* TODO: tooltip and better icon usage */ }

              </Col>
              <Col size={3}>

                  {vm.addresses['0/ip']
                    ? vm.addresses['0/ip']
                    : _('noIpv4Record')
                  }

              </Col>
            </Row>
            : <Row className='text-xs-center'>
              <Col size={12}>{_('noToolsDetected')}.</Col>
            </Row>
          }
          { /* TODO: use CSS style */ }

          <Row>
            <Col size={12}>

                <Tags labels={vm.tags}/>

            </Col>
          </Row>
        </TabPanel>
        <TabPanel>
          <Debug value={this.state} />
        </TabPanel>
        <TabPanel className='text-xs-center'>
          <Row className='text-xs-center'>
            <Col size={3}>

                {stats && <CpuSparkLines data={stats.cpus} />}

            </Col>
            <Col size={3}>
              { /* TODO: compute nicely RAM units */ }

                {stats && <MemorySparkLines data={stats} />}

            </Col>
            <Col size={3}>

                {stats && <XvdSparkLines data={stats.xvds} />}

            </Col>
            <Col size={3}>

                {stats && <VifSparkLines data={stats.vifs} />}

            </Col>
          </Row>
          <Row>
            <Col size={5}>
              { /* TODO: insert real ISO selector, CtrlAltSuppr button and Clipboard */ }

                <select className='form-control'>
                  <option>-- CD Drive (empty) --</option>
                  <option>Debian-8.iso</option>
                  <option>Windows7.iso</option>
                </select>

                  <button className='btn btn-secondary'>

                  </button>

            </Col>
            <Col size={5}>

                <input type='text' className='form-control'></input>

                  <button className='btn btn-secondary'>
                     {_('copyToClipboardLabel')}
                  </button>

            </Col>
            <Col size={2}>
              <button className='btn btn-secondary'> {_('ctrlAltDelButtonLabel')}</button>
            </Col>
          </Row>
          { /* TODO: use CSS style to replace BR tag */ }

          { /* TODO: insert real noVNC console*/ }

          { /* TODO: use CSS style to replace BR tag */ }

 {_('tipLabel')} {_('tipConsoleLabel')}

        </TabPanel>
        <TabPanel>
          <Debug value={vm} />
        </TabPanel>
        <TabPanel>
          <Row>
            <Col size={12}>
              <button className='btn btn-lg btn-primary btn-tab pull-xs-right'>{_('vifCreateDeviceButton')}</button>

              {!isEmpty(vifs)
                ?

                        {_('vifDeviceLabel')}
                        {_('vifMacLabel')}
                        {_('vifMtuLabel')}
                        {_('vifNetworkLabel')}
                        {_('vifStatusLabel')}

                      {map(vifs, (vif) =>

                          VIF #{vif.device}

{vif.MAC}

                          {vif.MTU}
                          {networkByVifs[vif.id].name_label}

                            {vif.attached
                              ?
                                  {_('vifStatusConnected')}

                              :
                                  {_('vifStatusDisconnected')}

                            }

                      )}

                  {vm.addresses && !isEmpty(vm.addresses)
                    ?

{_('vifIpAddresses')}

                      {map(vm.addresses, (address) => {address})}

                    : _('noIpRecord')
                  }

                :
{_('vifNoInterface')}

              }
            </Col>
          </Row>
        </TabPanel>
        <TabPanel>

Snapshot stuff

        </TabPanel>
        <TabPanel>

Log stuff

        </TabPanel>
        <TabPanel>
          <Row>
            <Col size={12}>

{_('uuid')}

                  {vm.uuid}
                  <CopyToClipboard text={vm.uuid}>
                    <button className='btn btn-sm btn-secondary btn-copy-to-clipboard'>

                    </button>
                  </CopyToClipboard>

{_('virtualizationMode')}

                  {vm.virtualizationMode === 'pv'
                    ? _('paraVirtualizedMode')
                    : _('hardwareVirtualizedMode')
                  }

{_('cpuWeightLabel')}

                {vm.cpuWeight
                  ?
{vm.cpuWeight}

                  :
{_('defaultCpuWeight')}

                }

                {vm.PV_args
                  ? [

{_('pvArgsLabel')}
,

{vm.PV_args}

                  ]
                  : null
                }

{_('xenToolsStatus')}

{_('xenToolsStatusValue', { status: normalizeXenToolsStatus(vm.xenTools) })}

{_('osName')}

{vm.os_version ? vm.os_version.name : _('unknownOsName')}

{_('osKernel')}

{vm.os_version ? vm.os_version.uname : _('unknownOsKernel')}

{_('autoPowerOn')}

{vm.auto_poweron ? _('enabledAutoPowerOn') : _('disabledAutoPowerOn')}

{_('ha')}

{vm.high_availability ? _('enabledHa') : _('disabledHa')}

{_('originalTemplate')}

{vm.other.base_template_name ? vm.other.base_template_name : _('unknownOriginalTemplate')}

            </Col>
          </Row>
        </TabPanel>
      </Tabs>

  }
}
<commit_msg>start to work on snapshot view<commit_after>import _ from 'messages'
import CopyToClipboard from 'react-copy-to-clipboard'
import forEach from 'lodash/forEach'
import isEmpty from 'lodash/isEmpty'
import map from 'lodash/map'
import React, { Component } from 'react'
import sortBy from 'lodash/sortBy'
import reverse from 'lodash/reverse'
import Tags from 'tags'
import xo from 'xo'
import { createSelector } from 'reselect'
import { FormattedRelative } from 'react-intl'
import { Row, Col } from 'grid'
import { Tab, Tabs, TabList, TabPanel } from 'react-tabs'
import {
  connectStore,
  createCollectionSelector,
  Debug,
  formatSize,
  normalizeXenToolsStatus,
  osFamily
} from 'utils'
import {
  CpuSparkLines,
  MemorySparkLines,
  VifSparkLines,
  XvdSparkLines
} from 'xo-sparklines'

import VmActionBar from './action-bar'

// ===================================================================

@connectStore(() => {
  const getSnapshots = createSelector(
    createCollectionSelector(
      createSelector(
        (_, vm) => vm.snapshots,
        (objects) => objects,
        (snapshotIds, objects) => map(snapshotIds, (id) => objects[id])
      )
    ),
    (snapshots) => reverse(sortBy(snapshots, 'snapshot_time'))
  )
  const getVifs = createSelector(
    createCollectionSelector(
      createSelector(
        (_, vm) => vm.VIFs,
        (objects) => objects,
        (vifIds, objects) => map(vifIds, (id) => objects[id])
      )
    ),
    (vifs) => sortBy(vifs, 'device')
  )
  const getNetworkByVifs = createCollectionSelector(
    createSelector(
      (objects) => objects,
      (_, vifs) => vifs,
      (objects, vifs) => {
        const networkByVifs = {}
        forEach(vifs, (vif) => {
          networkByVifs[vif.id] = objects[vif.$network]
        })
        return networkByVifs
      }
    )
  )

  return (state, props) => {
    const { objects } = state
    const { id } = props.params

    const vm = objects[id]
    if (!vm) {
      return {}
    }

    const vifs = getVifs(objects, vm)

    return {
      container: objects[vm.$container],
      networkByVifs: getNetworkByVifs(objects, vifs),
      pool: objects[vm.$pool],
      snapshots: getSnapshots(objects, vm),
      vifs,
      vm
    }
  }
})
export default class Vm extends Component {
  componentWillMount () {
    const vmId = this.props.params.id
    const loop = () => {
      xo.call('vm.stats', { id: vmId }).then((newStats) => {
        this.setState({
          stats: newStats.stats
        })
      })

      this.timeout = setTimeout(loop, 5000)
    }

    loop()
  }

  componentWillUnmount () {
    clearTimeout(this.timeout)
  }

  render () {
    const {
      container,
      networkByVifs,
      pool,
      snapshots,
      vifs,
      vm
    } = this.props

    const { stats } = this.state || {}

    if (!vm) {
      return
Loading…

    }

    return

      <Row>
        <Col size={6}>

            {vm.name_label}
             - {container.name_label} ({pool.name_label})

{vm.name_description}

        </Col>
        <Col size={6}>

            <VmActionBar vm={vm} handlers={this.props}/>

        </Col>
      </Row>
      <Tabs>
        <TabList>
          <Tab>{_('generalTabName')}</Tab>
          <Tab>{_('statsTabName')}</Tab>
          <Tab>{_('consoleTabName')}</Tab>
          <Tab>{_('disksTabName', { disks: vm.$VBDs.length })}</Tab>
          <Tab>{_('networkTabName')}</Tab>
          <Tab>{_('snapshotsTabName')}</Tab>
          <Tab>{_('logsTabName')}</Tab>
          <Tab>{_('advancedTabName')}</Tab>
        </TabList>
        <TabPanel>
          { /* TODO: use CSS style */ }

          <Row className='text-xs-center'>
            <Col size={3}>

{vm.CPUs.number}x

              {stats && stats.cpus && <CpuSparkLines data={stats.cpus} />}
            </Col>
            <Col size={3}>

{formatSize(vm.memory.size)}

              {stats && <MemorySparkLines data={stats} />}
            </Col>
            <Col size={3}>
              { /* TODO: compute total disk usage */ }

{vm.$VBDs.length}x

              {stats && stats.xvds && <XvdSparkLines data={stats.xvds} />}
            </Col>
            <Col size={3}>

{vm.VIFs.length}x

              {stats && stats.vifs && <VifSparkLines data={stats.vifs} />}
            </Col>
          </Row>
          { /* TODO: use CSS style */ }

          {vm.xenTools
            ? <Row className='text-xs-center'>
              <Col size={3}>
                {vm.power_state === 'Running'
                  ?

{_('started')} <FormattedRelative value={vm.startTime * 1000}/>

                  : null
                }
              </Col>
              <Col size={3}>

                  {vm.virtualizationMode === 'pv'
                    ? _('paraVirtualizedMode')
                    : _('hardwareVirtualizedMode')
                  }

              </Col>
              <Col size={3}>
                { /* TODO: tooltip and better icon usage */ }

              </Col>
              <Col size={3}>

                  {vm.addresses['0/ip']
                    ? vm.addresses['0/ip']
                    : _('noIpv4Record')
                  }

              </Col>
            </Row>
            : <Row className='text-xs-center'>
              <Col size={12}>{_('noToolsDetected')}.</Col>
            </Row>
          }
          { /* TODO: use CSS style */ }

          <Row>
            <Col size={12}>

                <Tags labels={vm.tags}/>

            </Col>
          </Row>
        </TabPanel>
        <TabPanel>
          <Debug value={this.state} />
        </TabPanel>
        <TabPanel className='text-xs-center'>
          <Row className='text-xs-center'>
            <Col size={3}>

                {stats && <CpuSparkLines data={stats.cpus} />}

            </Col>
            <Col size={3}>

                {stats && <MemorySparkLines data={stats} />}

            </Col>
            <Col size={3}>

                {stats && <XvdSparkLines data={stats.xvds} />}

            </Col>
            <Col size={3}>

                {stats && <VifSparkLines data={stats.vifs} />}

            </Col>
          </Row>
          <Row>
            <Col size={5}>
              { /* TODO: insert real ISO selector, CtrlAltSuppr button and Clipboard */ }

                <select className='form-control'>
                  <option>-- CD Drive (empty) --</option>
                  <option>Debian-8.iso</option>
                  <option>Windows7.iso</option>
                </select>

                  <button className='btn btn-secondary'>

                  </button>

            </Col>
            <Col size={5}>

                <input type='text' className='form-control'></input>

                  <button className='btn btn-secondary'>
                     {_('copyToClipboardLabel')}
                  </button>

            </Col>
            <Col size={2}>
              <button className='btn btn-secondary'> {_('ctrlAltDelButtonLabel')}</button>
            </Col>
          </Row>
          { /* TODO: use CSS style to replace BR tag */ }

          { /* TODO: insert real noVNC console*/ }

          { /* TODO: use CSS style to replace BR tag */ }

 {_('tipLabel')} {_('tipConsoleLabel')}

        </TabPanel>
        <TabPanel>
          <Debug value={vm} />
        </TabPanel>
        <TabPanel>
          <Row>
            <Col size={12}>
              <button className='btn btn-lg btn-primary btn-tab pull-xs-right'>{_('vifCreateDeviceButton')}</button>

              {!isEmpty(vifs)
                ?

                        {_('vifDeviceLabel')}
                        {_('vifMacLabel')}
                        {_('vifMtuLabel')}
                        {_('vifNetworkLabel')}
                        {_('vifStatusLabel')}

                      {map(vifs, (vif) =>

                          VIF #{vif.device}

{vif.MAC}

                          {vif.MTU}
                          {networkByVifs[vif.id].name_label}

                            {vif.attached
                              ?
                                  {_('vifStatusConnected')}

                              :
                                  {_('vifStatusDisconnected')}

                            }

                      )}

                  {vm.addresses && !isEmpty(vm.addresses)
                    ?

{_('vifIpAddresses')}

                      {map(vm.addresses, (address) => {address})}

                    : _('noIpRecord')
                  }

                :
{_('vifNoInterface')}

              }
            </Col>
          </Row>
        </TabPanel>
        <TabPanel>
          <Row>
            <Col size={12}>
            {isEmpty(vm.snapshots)
              ? [
No existing snapshots
,
Create a new one
]
              : map(snapshots, (snapshot) => {snapshot.name_label} {snapshot.snapshot_time}  )
            }
            </Col>
          </Row>
        </TabPanel>
        <TabPanel>

Log stuff

        </TabPanel>
        <TabPanel>
          <Row>
            <Col size={12}>

{_('uuid')}

                  {vm.uuid}
                  <CopyToClipboard text={vm.uuid}>
                    <button className='btn btn-sm btn-secondary btn-copy-to-clipboard'>

                    </button>
                  </CopyToClipboard>

{_('virtualizationMode')}

                  {vm.virtualizationMode === 'pv'
                    ? _('paraVirtualizedMode')
                    : _('hardwareVirtualizedMode')
                  }

{_('cpuWeightLabel')}

                {vm.cpuWeight
                  ?
{vm.cpuWeight}

                  :
{_('defaultCpuWeight')}

                }

                {vm.PV_args
                  ? [

{_('pvArgsLabel')}
,

{vm.PV_args}

                  ]
                  : null
                }

{_('xenToolsStatus')}

{_('xenToolsStatusValue', { status: normalizeXenToolsStatus(vm.xenTools) })}

{_('osName')}

{vm.os_version ? vm.os_version.name : _('unknownOsName')}

{_('osKernel')}

{vm.os_version ? vm.os_version.uname : _('unknownOsKernel')}

{_('autoPowerOn')}

{vm.auto_poweron ? _('enabledAutoPowerOn') : _('disabledAutoPowerOn')}

{_('ha')}

{vm.high_availability ? _('enabledHa') : _('disabledHa')}

{_('originalTemplate')}

{vm.other.base_template_name ? vm.other.base_template_name : _('unknownOriginalTemplate')}

            </Col>
          </Row>
        </TabPanel>
      </Tabs>

  }
}
<|endoftext|>