<commit_before>// Copyright (c) 2015 - 2017 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

import {COORDINATE_SYSTEM, PROJECTION_MODE} from '../../lib/constants';

// We are generating these from the js code in constants.js
const COORDINATE_SYSTEM_GLSL_CONSTANTS = Object.keys(COORDINATE_SYSTEM)
  .map(key => `const int COORDINATE_SYSTEM_${key} = ${COORDINATE_SYSTEM[key]};`)
  .join('');
const PROJECTION_MODE_GLSL_CONSTANTS = Object.keys(PROJECTION_MODE)
  .map(key => `const int PROJECTION_MODE_${key} = ${PROJECTION_MODE[key]};`)
  .join('');

export default `\
${COORDINATE_SYSTEM_GLSL_CONSTANTS}
${PROJECTION_MODE_GLSL_CONSTANTS}

uniform int project_uCoordinateSystem;
uniform int project_uProjectionMode;
uniform float project_uScale;
uniform bool project_uWrapLongitude;
uniform float project_uAntimeridian;
uniform vec3 project_uCommonUnitsPerMeter;
uniform vec3 project_uCommonUnitsPerWorldUnit;
uniform vec3 project_uCommonUnitsPerWorldUnit2;
uniform vec4 project_uCenter;
uniform mat4 project_uModelMatrix;
uniform mat4 project_uViewProjectionMatrix;
uniform vec2 project_uViewportSize;
uniform float project_uDevicePixelRatio;
uniform float project_uFocalDistance;
uniform vec3 project_uCameraPosition;
uniform vec3 project_uCoordinateOrigin;

const float TILE_SIZE = 512.0;
const float PI = 3.1415926536;
const float WORLD_SCALE = TILE_SIZE / (PI * 2.0);
const vec3 ZERO_64_LOW = vec3(0.0);
const float EARTH_RADIUS = 6370972.0; // meters
const float GLOBE_RADIUS = 256.0;

//
// Scaling offsets - scales meters to "world distance"
// Note the scalar version of project_size is for scaling the z component only
//
float project_size(float meters) {
  return meters * project_uCommonUnitsPerMeter.z;
}

vec2 project_size(vec2 meters) {
  return meters * project_uCommonUnitsPerMeter.xy;
}

vec3 project_size(vec3 meters) {
  return meters * project_uCommonUnitsPerMeter;
}

vec4 project_size(vec4 meters) {
  return vec4(meters.xyz * project_uCommonUnitsPerMeter, meters.w);
}

//
// Projecting normal - transform deltas from current coordinate system to
// normals in the worldspace
//
vec3 project_normal(vec3 vector) {
  // Apply model matrix
  vec4 normal_modelspace = project_uModelMatrix * vec4(vector, 0.0);
  return normalize(normal_modelspace.xyz * project_uCommonUnitsPerMeter);
}

vec4 project_offset_(vec4 offset) {
  float dy = offset.y;
  if (project_uCoordinateSystem == COORDINATE_SYSTEM_LNGLAT) {
    dy = clamp(dy, -1., 1.);
  }
  vec3 commonUnitsPerWorldUnit = project_uCommonUnitsPerWorldUnit + project_uCommonUnitsPerWorldUnit2 * dy;
  return vec4(offset.xyz * commonUnitsPerWorldUnit, offset.w);
}

//
// Projecting positions - non-linear projection: lnglats => unit tile [0-1, 0-1]
//
vec2 project_mercator_(vec2 lnglat) {
  float x = lnglat.x;
  if (project_uWrapLongitude) {
    x = mod(x - project_uAntimeridian, 360.0) + project_uAntimeridian;
  }
  return vec2(
    radians(x) + PI,
    PI + log(tan_fp32(PI * 0.25 + radians(lnglat.y) * 0.5))
  );
}

vec3 project_globe_(vec3 lnglatz) {
  float lambda = radians(lnglatz.x);
  float phi = radians(lnglatz.y);
  float cosPhi = cos(phi);
  float D = (lnglatz.z / EARTH_RADIUS + 1.0) * GLOBE_RADIUS;

  return vec3(
    sin(lambda) * cosPhi,
    -cos(lambda) * cosPhi,
    sin(phi)
  ) * D;
}

//
// Projects positions (defined by project_uCoordinateSystem) to common space (defined by project_uProjectionMode)
//
vec4 project_position(vec4 position, vec3 position64Low) {
  vec4 position_world = project_uModelMatrix * position;

  // Work around for a Mac+NVIDIA bug https://github.com/visgl/deck.gl/issues/4145
  if (project_uProjectionMode == PROJECTION_MODE_WEB_MERCATOR) {
    if (project_uCoordinateSystem == COORDINATE_SYSTEM_LNGLAT) {
      return vec4(
        project_mercator_(position_world.xy) * WORLD_SCALE,
        project_size(position_world.z),
        position_world.w
      );
    }
    if (project_uCoordinateSystem == COORDINATE_SYSTEM_CARTESIAN) {
      position_world.xyz += project_uCoordinateOrigin;
    }
  }
  if (project_uProjectionMode == PROJECTION_MODE_GLOBE) {
    if (project_uCoordinateSystem == COORDINATE_SYSTEM_LNGLAT) {
      return vec4(
        project_globe_(position_world.xyz),
        position_world.w
      );
    }
  }
  if (project_uProjectionMode == PROJECTION_MODE_IDENTITY ||
    (project_uProjectionMode == PROJECTION_MODE_WEB_MERCATOR_AUTO_OFFSET &&
    (project_uCoordinateSystem == COORDINATE_SYSTEM_LNGLAT ||
     project_uCoordinateSystem == COORDINATE_SYSTEM_CARTESIAN))) {
    // Subtract high part of 64 bit value. Convert remainder to float32, preserving precision.
    position_world.xyz -= project_uCoordinateOrigin;
  }

  // Translation is already added to the high parts
  return project_offset_(position_world + project_uModelMatrix * vec4(position64Low, 0.0));
}

vec4 project_position(vec4 position) {
  return project_position(position, ZERO_64_LOW);
}

vec3 project_position(vec3 position, vec3 position64Low) {
  vec4 projected_position = project_position(vec4(position, 1.0), position64Low);
  return projected_position.xyz;
}

vec3 project_position(vec3 position) {
  vec4 projected_position = project_position(vec4(position, 1.0), ZERO_64_LOW);
  return projected_position.xyz;
}

vec2 project_position(vec2 position) {
  vec4 projected_position = project_position(vec4(position, 0.0, 1.0), ZERO_64_LOW);
  return projected_position.xy;
}

vec4 project_common_position_to_clipspace(vec4 position, mat4 viewProjectionMatrix, vec4 center) {
  return viewProjectionMatrix * position + center;
}

//
// Projects from common space coordinates to clip space.
// Uses project_uViewProjectionMatrix
//
vec4 project_common_position_to_clipspace(vec4 position) {
  return project_common_position_to_clipspace(position, project_uViewProjectionMatrix, project_uCenter);
}

// Returns a clip space offset that corresponds to a given number of screen pixels
vec2 project_pixel_size_to_clipspace(vec2 pixels) {
  vec2 offset = pixels / project_uViewportSize * project_uDevicePixelRatio * 2.0;
  return offset * project_uFocalDistance;
}

float project_size_to_pixel(float meters) {
  return project_size(meters) * project_uScale;
}
float project_pixel_size(float pixels) {
  return pixels / project_uScale;
}
vec2 project_pixel_size(vec2 pixels) {
  return pixels / project_uScale;
}

// Get rotation matrix that aligns the z axis with the given up vector
// Find 3 unit vectors ux, uy, uz that are perpendicular to each other and uz == up
mat3 project_get_orientation_matrix(vec3 up) {
  vec3 uz = normalize(up);
  // Tangent on XY plane
  vec3 ux = abs(uz.z) == 1.0 ? vec3(1.0, 0.0, 0.0) : normalize(vec3(uz.y, -uz.x, 0));
  vec3 uy = cross(uz, ux);
  return mat3(ux, uy, uz);
}

bool project_needs_rotation(vec3 commonPosition, out mat3 transform) {
  if (project_uProjectionMode == PROJECTION_MODE_GLOBE) {
    transform = project_get_orientation_matrix(commonPosition);
    return true;
  }
  return false;
}
`;
<commit_msg>Shader support for extreme latitude coordinates (#5081)<commit_after>// Copyright (c) 2015 - 2017 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

import {COORDINATE_SYSTEM, PROJECTION_MODE} from '../../lib/constants';

// We are generating these from the js code in constants.js
const COORDINATE_SYSTEM_GLSL_CONSTANTS = Object.keys(COORDINATE_SYSTEM)
  .map(key => `const int COORDINATE_SYSTEM_${key} = ${COORDINATE_SYSTEM[key]};`)
  .join('');
const PROJECTION_MODE_GLSL_CONSTANTS = Object.keys(PROJECTION_MODE)
  .map(key => `const int PROJECTION_MODE_${key} = ${PROJECTION_MODE[key]};`)
  .join('');

export default `\
${COORDINATE_SYSTEM_GLSL_CONSTANTS}
${PROJECTION_MODE_GLSL_CONSTANTS}

uniform int project_uCoordinateSystem;
uniform int project_uProjectionMode;
uniform float project_uScale;
uniform bool project_uWrapLongitude;
uniform float project_uAntimeridian;
uniform vec3 project_uCommonUnitsPerMeter;
uniform vec3 project_uCommonUnitsPerWorldUnit;
uniform vec3 project_uCommonUnitsPerWorldUnit2;
uniform vec4 project_uCenter;
uniform mat4 project_uModelMatrix;
uniform mat4 project_uViewProjectionMatrix;
uniform vec2 project_uViewportSize;
uniform float project_uDevicePixelRatio;
uniform float project_uFocalDistance;
uniform vec3 project_uCameraPosition;
uniform vec3 project_uCoordinateOrigin;

const float TILE_SIZE = 512.0;
const float PI = 3.1415926536;
const float WORLD_SCALE = TILE_SIZE / (PI * 2.0);
const vec3 ZERO_64_LOW = vec3(0.0);
const float EARTH_RADIUS = 6370972.0; // meters
const float GLOBE_RADIUS = 256.0;

//
// Scaling offsets - scales meters to "world distance"
// Note the scalar version of project_size is for scaling the z component only
//
float project_size(float meters) {
  return meters * project_uCommonUnitsPerMeter.z;
}

vec2 project_size(vec2 meters) {
  return meters * project_uCommonUnitsPerMeter.xy;
}

vec3 project_size(vec3 meters) {
  return meters * project_uCommonUnitsPerMeter;
}

vec4 project_size(vec4 meters) {
  return vec4(meters.xyz * project_uCommonUnitsPerMeter, meters.w);
}

//
// Projecting normal - transform deltas from current coordinate system to
// normals in the worldspace
//
vec3 project_normal(vec3 vector) {
  // Apply model matrix
  vec4 normal_modelspace = project_uModelMatrix * vec4(vector, 0.0);
  return normalize(normal_modelspace.xyz * project_uCommonUnitsPerMeter);
}

vec4 project_offset_(vec4 offset) {
  float dy = offset.y;
  if (project_uCoordinateSystem == COORDINATE_SYSTEM_LNGLAT) {
    dy = clamp(dy, -1., 1.);
  }
  vec3 commonUnitsPerWorldUnit = project_uCommonUnitsPerWorldUnit + project_uCommonUnitsPerWorldUnit2 * dy;
  return vec4(offset.xyz * commonUnitsPerWorldUnit, offset.w);
}

//
// Projecting positions - non-linear projection: lnglats => unit tile [0-1, 0-1]
//
vec2 project_mercator_(vec2 lnglat) {
  float x = lnglat.x;
  if (project_uWrapLongitude) {
    x = mod(x - project_uAntimeridian, 360.0) + project_uAntimeridian;
  }
  float y = clamp(lnglat.y, -89.9, 89.9);
  return vec2(
    radians(x) + PI,
    PI + log(tan_fp32(PI * 0.25 + radians(y) * 0.5))
  );
}

vec3 project_globe_(vec3 lnglatz) {
  float lambda = radians(lnglatz.x);
  float phi = radians(lnglatz.y);
  float cosPhi = cos(phi);
  float D = (lnglatz.z / EARTH_RADIUS + 1.0) * GLOBE_RADIUS;

  return vec3(
    sin(lambda) * cosPhi,
    -cos(lambda) * cosPhi,
    sin(phi)
  ) * D;
}

//
// Projects positions (defined by project_uCoordinateSystem) to common space (defined by project_uProjectionMode)
//
vec4 project_position(vec4 position, vec3 position64Low) {
  vec4 position_world = project_uModelMatrix * position;

  // Work around for a Mac+NVIDIA bug https://github.com/visgl/deck.gl/issues/4145
  if (project_uProjectionMode == PROJECTION_MODE_WEB_MERCATOR) {
    if (project_uCoordinateSystem == COORDINATE_SYSTEM_LNGLAT) {
      return vec4(
        project_mercator_(position_world.xy) * WORLD_SCALE,
        project_size(position_world.z),
        position_world.w
      );
    }
    if (project_uCoordinateSystem == COORDINATE_SYSTEM_CARTESIAN) {
      position_world.xyz += project_uCoordinateOrigin;
    }
  }
  if (project_uProjectionMode == PROJECTION_MODE_GLOBE) {
    if (project_uCoordinateSystem == COORDINATE_SYSTEM_LNGLAT) {
      return vec4(
        project_globe_(position_world.xyz),
        position_world.w
      );
    }
  }
  if (project_uProjectionMode == PROJECTION_MODE_IDENTITY ||
    (project_uProjectionMode == PROJECTION_MODE_WEB_MERCATOR_AUTO_OFFSET &&
    (project_uCoordinateSystem == COORDINATE_SYSTEM_LNGLAT ||
     project_uCoordinateSystem == COORDINATE_SYSTEM_CARTESIAN))) {
    // Subtract high part of 64 bit value. Convert remainder to float32, preserving precision.
    position_world.xyz -= project_uCoordinateOrigin;
  }

  // Translation is already added to the high parts
  return project_offset_(position_world + project_uModelMatrix * vec4(position64Low, 0.0));
}

vec4 project_position(vec4 position) {
  return project_position(position, ZERO_64_LOW);
}

vec3 project_position(vec3 position, vec3 position64Low) {
  vec4 projected_position = project_position(vec4(position, 1.0), position64Low);
  return projected_position.xyz;
}

vec3 project_position(vec3 position) {
  vec4 projected_position = project_position(vec4(position, 1.0), ZERO_64_LOW);
  return projected_position.xyz;
}

vec2 project_position(vec2 position) {
  vec4 projected_position = project_position(vec4(position, 0.0, 1.0), ZERO_64_LOW);
  return projected_position.xy;
}

vec4 project_common_position_to_clipspace(vec4 position, mat4 viewProjectionMatrix, vec4 center) {
  return viewProjectionMatrix * position + center;
}

//
// Projects from common space coordinates to clip space.
// Uses project_uViewProjectionMatrix
//
vec4 project_common_position_to_clipspace(vec4 position) {
  return project_common_position_to_clipspace(position, project_uViewProjectionMatrix, project_uCenter);
}

// Returns a clip space offset that corresponds to a given number of screen pixels
vec2 project_pixel_size_to_clipspace(vec2 pixels) {
  vec2 offset = pixels / project_uViewportSize * project_uDevicePixelRatio * 2.0;
  return offset * project_uFocalDistance;
}

float project_size_to_pixel(float meters) {
  return project_size(meters) * project_uScale;
}
float project_pixel_size(float pixels) {
  return pixels / project_uScale;
}
vec2 project_pixel_size(vec2 pixels) {
  return pixels / project_uScale;
}

// Get rotation matrix that aligns the z axis with the given up vector
// Find 3 unit vectors ux, uy, uz that are perpendicular to each other and uz == up
mat3 project_get_orientation_matrix(vec3 up) {
  vec3 uz = normalize(up);
  // Tangent on XY plane
  vec3 ux = abs(uz.z) == 1.0 ? vec3(1.0, 0.0, 0.0) : normalize(vec3(uz.y, -uz.x, 0));
  vec3 uy = cross(uz, ux);
  return mat3(ux, uy, uz);
}

bool project_needs_rotation(vec3 commonPosition, out mat3 transform) {
  if (project_uProjectionMode == PROJECTION_MODE_GLOBE) {
    transform = project_get_orientation_matrix(commonPosition);
    return true;
  }
  return false;
}
`;
<|endoftext|>


<commit_before>/*global UserTimingCompression*/
/**
 * The UserTiming plugin to collect metrics from the W3C
 * [UserTiming]{@link http://www.w3.org/TR/user-timing/} API.
 *
 * This plugin is dependent on the
 * [UserTimingCompression library]{@link https://github.com/nicjansma/usertiming-compression.js}.
 * `UserTimingCompression` must be loaded before this plugin's `init()` is called.
 *
 * This plugin collects all marks and measures that were added since
 * navigation start or since the last beacon fired for the current navigation.
 *
 * For information on how to include this plugin, see the {@tutorial building} tutorial.
 *
 * ## Beacon Parameters
 *
 * This plugin adds the following parameters to the beacon:
 *
 * * `usertiming`: Compressed ResourceTiming data
 *
 * The value is a compressed string using
 * [UserTimingCompression library]{@link https://github.com/nicjansma/usertiming-compression.js}.
 * A decompression function is also available in the library.
 *
 * Timing data is rounded to the nearest millisecond.
 *
 * ## Example
 *
 *     // mark current timestamp as mark1
 *     performance.mark('mark1');
 *     // mark current timestamp as mark1
 *     performance.mark('mark2');
 *     // measure1 will be the delta between mark1 and mark2 timestamps
 *     performance.measure('measure1', 'mark1', 'mark2');
 *     //measure2 will be the delta between the mark2 timestamp and the current time
 *     performance.measure('measure2', 'mark2');
 *
 * The compressed data added to the beacon will look similar to the following:
 *
 *     usertiming=~(m~(ark~(1~'2s~2~'5k)~easure~(1~'2s_2s~2~'5k_5k)))
 *
 * Decompressing the above value will give us the original data for the marks
 * and measures collected:
 *
 *     [{"name":"mark1","startTime":100,"duration":0,"entryType":"mark"},
 *     {"name":"measure1","startTime":100,"duration":100,"entryType":"measure"},
 *     {"name":"mark2","startTime":200,"duration":0,"entryType":"mark"},
 *     {"name":"measure2","startTime":200,"duration":200,"entryType":"measure"}]
 *
 * ## Compatibility
 *
 * Many browsers [support](http://caniuse.com/#feat=user-timing) the UserTiming
 * API, e.g.:
 *
 * * Chrome 25+
 * * Edge
 * * Firefox 38+
 * * IE 10+
 * * Opera 15+
 *
 * See Nic Jansma's [usertiming.js]{@link https://github.com/nicjansma/usertiming.js}
 * polyfill library to add UserTiming API support for browsers that don't
 * implement it natively.
 *
 * @see {@link http://www.w3.org/TR/user-timing/}
 * @class BOOMR.plugins.UserTiming
 */
(function() {
	BOOMR = window.BOOMR || {};
	BOOMR.plugins = BOOMR.plugins || {};

	if (BOOMR.plugins.UserTiming) {
		return;
	}

	var impl = {
		// Whether or not this plugin is complete.
		complete: false,

		// Whether or not this plugin is initialized
		initialized: false,

		// Whether or not UserTiming is supported by this browser
		supported: false,

		// Options
		options: {"from": 0, "window": BOOMR.window},

		/*
		 * Calls the UserTimingCompression library to get the compressed UserTiming
		 * data that occurred since the last call.
		 *
		 * @returns {string} Compressed UserTiming data
		 */
		getUserTiming: function() {
			var timings, res,
			    now = BOOMR.hrNow(),  // Get a timestamp to compare with performance marks and measures.
			    utc = window.UserTimingCompression || BOOMR.window.UserTimingCompression;

			timings = utc.getCompressedUserTiming(impl.options);
			res = utc.compressForUri(timings);
			this.options.from = now;

			return res;
		},

		/**
		 * Callback for `before_beacon` boomerang event.
		 *
		 * Adds the `usertiming` param to the beacon.
		 */
		addEntriesToBeacon: function() {
			var r;

			if (this.complete) {
				return;
			}

			BOOMR.removeVar("usertiming");
			r = this.getUserTiming();
			if (r) {
				BOOMR.addVar({
					"usertiming": r
				});
			}

			this.complete = true;
		},

		/**
		 * Callback for `beacon` boomerang event.
		 *
		 * Clears the `usertiming` beacon param.
		 */
		clearMetrics: function(vars) {
			if (vars.hasOwnProperty("usertiming")) {
				BOOMR.removeVar("usertiming");
			}
			this.complete = false;
		},

		/**
		 * Subscribe to boomerang events that will handle the `usertiming`
		 * beacon param.
		 */
		subscribe: function() {
			BOOMR.subscribe("before_beacon", this.addEntriesToBeacon, null, this);
			BOOMR.subscribe("beacon", this.clearMetrics, null, this);
		},

		/**
		 * Callback for boomerang page_ready event.
		 *
		 * At page_ready, all javascript should be loaded. We'll call `checkSupport`
		 * again to see if a polyfill for UserTiming is available.
		 */
		pageReady: function() {
			if (this.checkSupport()) {
				this.subscribe();
			}
		},

		/**
		 * Checks if the browser supports the UserTiming API and that the
		 * UserTimingCompression library is available.
		 *
		 * @returns {boolean} true if supported, false if not
		 */
		checkSupport: function() {
			if (this.supported) {
				return true;
			}

			// Check that the required UserTimingCompression library is available
			var utc = window.UserTimingCompression || BOOMR.window.UserTimingCompression;
			if (typeof utc === "undefined") {
				BOOMR.warn("UserTimingCompression library not found", "usertiming");
				return false;
			}

			var p = BOOMR.getPerformance();

			// Check that we have getEntriesByType
			if (p && typeof p.getEntriesByType === "function") {
				var marks = p.getEntriesByType("mark");
				var measures = p.getEntriesByType("measure");

				// Check that the results of getEntriesByType for marks and measures are Arrays
				// Some polyfill libraries may incorrectly implement this
				if (BOOMR.utils.isArray(marks) && BOOMR.utils.isArray(measures)) {
					BOOMR.info("Client supports UserTiming API", "usertiming");
					this.supported = true;
					return true;
				}
			}

			return false;
		}
	};

	BOOMR.plugins.UserTiming = {
		/**
		 * Initializes the plugin.
		 *
		 * @returns {@link BOOMR.plugins.UserTiming} The UserTiming plugin for chaining
		 * @memberof BOOMR.plugins.UserTiming
		 */
		init: function(config) {
			if (impl.initialized) {
				return this;
			}

			if (impl.checkSupport()) {
				impl.subscribe();
			}
			else {
				// UserTiming isn't supported by the browser or the UserTimingCompression
				// library isn't loaded. Let's check again when the page is
				// ready to see if a polyfill was loaded.
				BOOMR.subscribe("page_ready", impl.pageReady, null, impl);
			}

			impl.initialized = true;
			return this;
		},

		/**
		 * Whether or not this plugin is complete
		 *
		 * @returns {boolean} `true` if the plugin is complete
		 * @memberof BOOMR.plugins.UserTiming
		 */
		is_complete: function() {
			return true;
		},

		/**
		 * Whether or not UserTiming is supported in this browser.
		 *
		 * @returns {boolean} `true` if UserTiming is supported.
		 * @memberof BOOMR.plugins.UserTiming
		 */
		is_supported: function() {
			return impl.initialized && impl.supported;
		}
	};
}());
<commit_msg>UserTiming: Fix typo in doc comments<commit_after>/*global UserTimingCompression*/
/**
 * The UserTiming plugin to collect metrics from the W3C
 * [UserTiming]{@link http://www.w3.org/TR/user-timing/} API.
 *
 * This plugin is dependent on the
 * [UserTimingCompression library]{@link https://github.com/nicjansma/usertiming-compression.js}.
 * `UserTimingCompression` must be loaded before this plugin's `init()` is called.
 *
 * This plugin collects all marks and measures that were added since
 * navigation start or since the last beacon fired for the current navigation.
 *
 * For information on how to include this plugin, see the {@tutorial building} tutorial.
 *
 * ## Beacon Parameters
 *
 * This plugin adds the following parameters to the beacon:
 *
 * * `usertiming`: Compressed ResourceTiming data
 *
 * The value is a compressed string using
 * [UserTimingCompression library]{@link https://github.com/nicjansma/usertiming-compression.js}.
 * A decompression function is also available in the library.
 *
 * Timing data is rounded to the nearest millisecond.
 *
 * ## Example
 *
 *     // mark current timestamp as mark1
 *     performance.mark('mark1');
 *     // mark current timestamp as mark2
 *     performance.mark('mark2');
 *     // measure1 will be the delta between mark1 and mark2 timestamps
 *     performance.measure('measure1', 'mark1', 'mark2');
 *     //measure2 will be the delta between the mark2 timestamp and the current time
 *     performance.measure('measure2', 'mark2');
 *
 * The compressed data added to the beacon will look similar to the following:
 *
 *     usertiming=~(m~(ark~(1~'2s~2~'5k)~easure~(1~'2s_2s~2~'5k_5k)))
 *
 * Decompressing the above value will give us the original data for the marks
 * and measures collected:
 *
 *     [{"name":"mark1","startTime":100,"duration":0,"entryType":"mark"},
 *     {"name":"measure1","startTime":100,"duration":100,"entryType":"measure"},
 *     {"name":"mark2","startTime":200,"duration":0,"entryType":"mark"},
 *     {"name":"measure2","startTime":200,"duration":200,"entryType":"measure"}]
 *
 * ## Compatibility
 *
 * Many browsers [support](http://caniuse.com/#feat=user-timing) the UserTiming
 * API, e.g.:
 *
 * * Chrome 25+
 * * Edge
 * * Firefox 38+
 * * IE 10+
 * * Opera 15+
 *
 * See Nic Jansma's [usertiming.js]{@link https://github.com/nicjansma/usertiming.js}
 * polyfill library to add UserTiming API support for browsers that don't
 * implement it natively.
 *
 * @see {@link http://www.w3.org/TR/user-timing/}
 * @class BOOMR.plugins.UserTiming
 */
(function() {
	BOOMR = window.BOOMR || {};
	BOOMR.plugins = BOOMR.plugins || {};

	if (BOOMR.plugins.UserTiming) {
		return;
	}

	var impl = {
		// Whether or not this plugin is complete.
		complete: false,

		// Whether or not this plugin is initialized
		initialized: false,

		// Whether or not UserTiming is supported by this browser
		supported: false,

		// Options
		options: {"from": 0, "window": BOOMR.window},

		/*
		 * Calls the UserTimingCompression library to get the compressed UserTiming
		 * data that occurred since the last call.
		 *
		 * @returns {string} Compressed UserTiming data
		 */
		getUserTiming: function() {
			var timings, res,
			    now = BOOMR.hrNow(),  // Get a timestamp to compare with performance marks and measures.
			    utc = window.UserTimingCompression || BOOMR.window.UserTimingCompression;

			timings = utc.getCompressedUserTiming(impl.options);
			res = utc.compressForUri(timings);
			this.options.from = now;

			return res;
		},

		/**
		 * Callback for `before_beacon` boomerang event.
		 *
		 * Adds the `usertiming` param to the beacon.
		 */
		addEntriesToBeacon: function() {
			var r;

			if (this.complete) {
				return;
			}

			BOOMR.removeVar("usertiming");
			r = this.getUserTiming();
			if (r) {
				BOOMR.addVar({
					"usertiming": r
				});
			}

			this.complete = true;
		},

		/**
		 * Callback for `beacon` boomerang event.
		 *
		 * Clears the `usertiming` beacon param.
		 */
		clearMetrics: function(vars) {
			if (vars.hasOwnProperty("usertiming")) {
				BOOMR.removeVar("usertiming");
			}
			this.complete = false;
		},

		/**
		 * Subscribe to boomerang events that will handle the `usertiming`
		 * beacon param.
		 */
		subscribe: function() {
			BOOMR.subscribe("before_beacon", this.addEntriesToBeacon, null, this);
			BOOMR.subscribe("beacon", this.clearMetrics, null, this);
		},

		/**
		 * Callback for boomerang page_ready event.
		 *
		 * At page_ready, all javascript should be loaded. We'll call `checkSupport`
		 * again to see if a polyfill for UserTiming is available.
		 */
		pageReady: function() {
			if (this.checkSupport()) {
				this.subscribe();
			}
		},

		/**
		 * Checks if the browser supports the UserTiming API and that the
		 * UserTimingCompression library is available.
		 *
		 * @returns {boolean} true if supported, false if not
		 */
		checkSupport: function() {
			if (this.supported) {
				return true;
			}

			// Check that the required UserTimingCompression library is available
			var utc = window.UserTimingCompression || BOOMR.window.UserTimingCompression;
			if (typeof utc === "undefined") {
				BOOMR.warn("UserTimingCompression library not found", "usertiming");
				return false;
			}

			var p = BOOMR.getPerformance();

			// Check that we have getEntriesByType
			if (p && typeof p.getEntriesByType === "function") {
				var marks = p.getEntriesByType("mark");
				var measures = p.getEntriesByType("measure");

				// Check that the results of getEntriesByType for marks and measures are Arrays
				// Some polyfill libraries may incorrectly implement this
				if (BOOMR.utils.isArray(marks) && BOOMR.utils.isArray(measures)) {
					BOOMR.info("Client supports UserTiming API", "usertiming");
					this.supported = true;
					return true;
				}
			}

			return false;
		}
	};

	BOOMR.plugins.UserTiming = {
		/**
		 * Initializes the plugin.
		 *
		 * @returns {@link BOOMR.plugins.UserTiming} The UserTiming plugin for chaining
		 * @memberof BOOMR.plugins.UserTiming
		 */
		init: function(config) {
			if (impl.initialized) {
				return this;
			}

			if (impl.checkSupport()) {
				impl.subscribe();
			}
			else {
				// UserTiming isn't supported by the browser or the UserTimingCompression
				// library isn't loaded. Let's check again when the page is
				// ready to see if a polyfill was loaded.
				BOOMR.subscribe("page_ready", impl.pageReady, null, impl);
			}

			impl.initialized = true;
			return this;
		},

		/**
		 * Whether or not this plugin is complete
		 *
		 * @returns {boolean} `true` if the plugin is complete
		 * @memberof BOOMR.plugins.UserTiming
		 */
		is_complete: function() {
			return true;
		},

		/**
		 * Whether or not UserTiming is supported in this browser.
		 *
		 * @returns {boolean} `true` if UserTiming is supported.
		 * @memberof BOOMR.plugins.UserTiming
		 */
		is_supported: function() {
			return impl.initialized && impl.supported;
		}
	};
}());
<|endoftext|>


<commit_before>/* base.js
 * Backbone.js base model functionality.
 */
define(
  [
    'backbone',
    'globals'
  ],
  function (Backbone, globals) {

    var Base = Backbone.Model.extend({
      defaults: {
        'model_name': 'base'
      },

      urlRoot: globals.API_ROOT,

      url: function () {
        var creds = this.getCreds();
        var url = this.urlRoot
          + '/provider/' + creds.provider_id
          + '/identity/' + creds.identity_id
          + '/' + this.defaults.model_name;

        if (this.get('id') !== undefined) {
          url += this.get('id');
        }

        return url;
      },

      /*
       * Here, were override the get method to allow lazy-loading of computed
       * attributes
       */
      get: function (attr) {
        if (typeof this.computed !== "undefined" && typeof this.computed[attr] === 'function')
          return this.computed[attr].call(this);
        return Backbone.Model.prototype.get.call(this, attr);
      }
    });

    return Base;

  });
<commit_msg>add back forward slash divider<commit_after>/* base.js
 * Backbone.js base model functionality.
 */
define(
  [
    'backbone',
    'globals'
  ],
  function (Backbone, globals) {

    var Base = Backbone.Model.extend({
      defaults: {
        'model_name': 'base'
      },

      urlRoot: globals.API_ROOT,

      url: function () {
        var creds = this.getCreds();
        var url = this.urlRoot
          + '/provider/' + creds.provider_id
          + '/identity/' + creds.identity_id
          + '/' + this.defaults.model_name + '/';

        if (this.get('id') !== undefined) {
          url += this.get('id');
        }

        return url;
      },

      /*
       * Here, were override the get method to allow lazy-loading of computed
       * attributes
       */
      get: function (attr) {
        if (typeof this.computed !== "undefined" && typeof this.computed[attr] === 'function')
          return this.computed[attr].call(this);
        return Backbone.Model.prototype.get.call(this, attr);
      }
    });

    return Base;

  });
<|endoftext|>


<commit_before>angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])

.constant('uibDropdownConfig', {
  appendToOpenClass: 'uib-dropdown-open',
  openClass: 'open'
})

.service('uibDropdownService', ['$document', '$rootScope', function($document, $rootScope) {
  var openScope = null;

  this.open = function(dropdownScope) {
    if (!openScope) {
      $document.on('click', closeDropdown);
      $document.on('keydown', keybindFilter);
    }

    if (openScope && openScope !== dropdownScope) {
      openScope.isOpen = false;
    }

    openScope = dropdownScope;
  };

  this.close = function(dropdownScope) {
    if (openScope === dropdownScope) {
      openScope = null;
      $document.off('click', closeDropdown);
      $document.off('keydown', keybindFilter);
    }
  };

  var closeDropdown = function(evt) {
    // This method may still be called during the same mouse event that
    // unbound this event handler. So check openScope before proceeding.
    if (!openScope) { return; }

    if (evt && openScope.getAutoClose() === 'disabled') { return; }

    if (evt && evt.which === 3) { return; }

    var toggleElement = openScope.getToggleElement();
    if (evt && toggleElement && toggleElement[0].contains(evt.target)) {
      return;
    }

    var dropdownElement = openScope.getDropdownElement();
    if (evt && openScope.getAutoClose() === 'outsideClick' &&
      dropdownElement && dropdownElement[0].contains(evt.target)) {
      return;
    }

    openScope.isOpen = false;

    if (!$rootScope.$$phase) {
      openScope.$apply();
    }
  };

  var keybindFilter = function(evt) {
    if (evt.which === 27) {
      openScope.focusToggleElement();
      closeDropdown();
    } else if (openScope.isKeynavEnabled() && [38, 40].indexOf(evt.which) !== -1 && openScope.isOpen) {
      evt.preventDefault();
      evt.stopPropagation();
      openScope.focusDropdownEntry(evt.which);
    }
  };
}])

.controller('UibDropdownController', ['$scope', '$element', '$attrs', '$parse', 'uibDropdownConfig', 'uibDropdownService', '$animate', '$uibPosition', '$document', '$compile', '$templateRequest', function($scope, $element, $attrs, $parse, dropdownConfig, uibDropdownService, $animate, $position, $document, $compile, $templateRequest) {
  var self = this,
    scope = $scope.$new(), // create a child scope so we are not polluting original one
    templateScope,
    appendToOpenClass = dropdownConfig.appendToOpenClass,
    openClass = dropdownConfig.openClass,
    getIsOpen,
    setIsOpen = angular.noop,
    toggleInvoker = $attrs.onToggle ? $parse($attrs.onToggle) : angular.noop,
    appendToBody = false,
    appendTo = null,
    keynavEnabled = false,
    selectedOption = null,
    body = $document.find('body');

  $element.addClass('dropdown');

  this.init = function() {
    if ($attrs.isOpen) {
      getIsOpen = $parse($attrs.isOpen);
      setIsOpen = getIsOpen.assign;

      $scope.$watch(getIsOpen, function(value) {
        scope.isOpen = !!value;
      });
    }

    if (angular.isDefined($attrs.dropdownAppendTo)) {
      var appendToEl = $parse($attrs.dropdownAppendTo)(scope);
      if (appendToEl) {
        appendTo = angular.element(appendToEl);
      }
    }

    appendToBody = angular.isDefined($attrs.dropdownAppendToBody);
    keynavEnabled = angular.isDefined($attrs.keyboardNav);

    if (appendToBody && !appendTo) {
      appendTo = body;
    }

    if (appendTo && self.dropdownMenu) {
      appendTo.append(self.dropdownMenu);
      $element.on('$destroy', function handleDestroyEvent() {
        self.dropdownMenu.remove();
      });
    }
  };

  this.toggle = function(open) {
    return scope.isOpen = arguments.length ? !!open : !scope.isOpen;
  };

  // Allow other directives to watch status
  this.isOpen = function() {
    return scope.isOpen;
  };

  scope.getToggleElement = function() {
    return self.toggleElement;
  };

  scope.getAutoClose = function() {
    return $attrs.autoClose || 'always'; //or 'outsideClick' or 'disabled'
  };

  scope.getElement = function() {
    return $element;
  };

  scope.isKeynavEnabled = function() {
    return keynavEnabled;
  };

  scope.focusDropdownEntry = function(keyCode) {
    var elems = self.dropdownMenu ? //If append to body is used.
      angular.element(self.dropdownMenu).find('a') :
      $element.find('ul').eq(0).find('a');

    switch (keyCode) {
      case 40: {
        if (!angular.isNumber(self.selectedOption)) {
          self.selectedOption = 0;
        } else {
          self.selectedOption = self.selectedOption === elems.length - 1 ?
            self.selectedOption :
            self.selectedOption + 1;
        }
        break;
      }
      case 38: {
        if (!angular.isNumber(self.selectedOption)) {
          self.selectedOption = elems.length - 1;
        } else {
          self.selectedOption = self.selectedOption === 0 ?
            0 : self.selectedOption - 1;
        }
        break;
      }
    }
    elems[self.selectedOption].focus();
  };

  scope.getDropdownElement = function() {
    return self.dropdownMenu;
  };

  scope.focusToggleElement = function() {
    if (self.toggleElement) {
      self.toggleElement[0].focus();
    }
  };

  scope.$watch('isOpen', function(isOpen, wasOpen) {
    if (appendTo && self.dropdownMenu) {
      var pos = $position.positionElements($element, self.dropdownMenu, 'bottom-left', true),
        css,
        rightalign;

      css = {
        top: pos.top + 'px',
        display: isOpen ? 'block' : 'none'
      };

      rightalign = self.dropdownMenu.hasClass('dropdown-menu-right');
      if (!rightalign) {
        css.left = pos.left + 'px';
        css.right = 'auto';
      } else {
        css.left = 'auto';
        css.right = window.innerWidth -
          (pos.left + $element.prop('offsetWidth')) + 'px';
      }

      // Need to adjust our positioning to be relative to the appendTo container
      // if it's not the body element
      if (!appendToBody) {
        var appendOffset = $position.offset(appendTo);

        css.top = pos.top - appendOffset.top + 'px';

        if (!rightalign) {
          css.left = pos.left - appendOffset.left + 'px';
        } else {
          css.right = window.innerWidth -
            (pos.left - appendOffset.left + $element.prop('offsetWidth')) + 'px';
        }
      }

      self.dropdownMenu.css(css);
    }

    var openContainer = appendTo ? appendTo : $element;
    var hasOpenClass = openContainer.hasClass(appendTo ? appendToOpenClass : openClass);

    if (hasOpenClass === !isOpen) {
      $animate[isOpen ? 'addClass' : 'removeClass'](openContainer, appendTo ? appendToOpenClass : openClass).then(function() {
        if (angular.isDefined(isOpen) && isOpen !== wasOpen) {
          toggleInvoker($scope, { open: !!isOpen });
        }
      });
    }

    if (isOpen) {
      if (self.dropdownMenuTemplateUrl) {
        $templateRequest(self.dropdownMenuTemplateUrl).then(function(tplContent) {
          templateScope = scope.$new();
          $compile(tplContent.trim())(templateScope, function(dropdownElement) {
            var newEl = dropdownElement;
            self.dropdownMenu.replaceWith(newEl);
            self.dropdownMenu = newEl;
          });
        });
      }

      scope.focusToggleElement();
      uibDropdownService.open(scope);
    } else {
      if (self.dropdownMenuTemplateUrl) {
        if (templateScope) {
          templateScope.$destroy();
        }
        var newEl = angular.element('

');
        self.dropdownMenu.replaceWith(newEl);
        self.dropdownMenu = newEl;
      }

      uibDropdownService.close(scope);
      self.selectedOption = null;
    }

    if (angular.isFunction(setIsOpen)) {
      setIsOpen($scope, isOpen);
    }
  });

  $scope.$on('$locationChangeSuccess', function() {
    if (scope.getAutoClose() !== 'disabled') {
      scope.isOpen = false;
    }
  });
}])

.directive('uibDropdown', function() {
  return {
    controller: 'UibDropdownController',
    link: function(scope, element, attrs, dropdownCtrl) {
      dropdownCtrl.init();
    }
  };
})

.directive('uibDropdownMenu', function() {
  return {
    restrict: 'A',
    require: '?^uibDropdown',
    link: function(scope, element, attrs, dropdownCtrl) {
      if (!dropdownCtrl || angular.isDefined(attrs.dropdownNested)) {
        return;
      }

      element.addClass('dropdown-menu');

      var tplUrl = attrs.templateUrl;
      if (tplUrl) {
        dropdownCtrl.dropdownMenuTemplateUrl = tplUrl;
      }

      if (!dropdownCtrl.dropdownMenu) {
        dropdownCtrl.dropdownMenu = element;
      }
    }
  };
})

.directive('uibDropdownToggle', function() {
  return {
    require: '?^uibDropdown',
    link: function(scope, element, attrs, dropdownCtrl) {
      if (!dropdownCtrl) {
        return;
      }

      element.addClass('dropdown-toggle');

      dropdownCtrl.toggleElement = element;

      var toggleDropdown = function(event) {
        event.preventDefault();

        if (!element.hasClass('disabled') && !attrs.disabled) {
          scope.$apply(function() {
            dropdownCtrl.toggle();
          });
        }
      };

      element.bind('click', toggleDropdown);

      // WAI-ARIA
      element.attr({ 'aria-haspopup': true, 'aria-expanded': false });
      scope.$watch(dropdownCtrl.isOpen, function(isOpen) {
        element.attr('aria-expanded', !!isOpen);
      });

      scope.$on('$destroy', function() {
        element.unbind('click', toggleDropdown);
      });
    }
  };
});
<commit_msg>fix(dropdown): correctly update isOpen<commit_after>angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])

.constant('uibDropdownConfig', {
  appendToOpenClass: 'uib-dropdown-open',
  openClass: 'open'
})

.service('uibDropdownService', ['$document', '$rootScope', function($document, $rootScope) {
  var openScope = null;

  this.open = function(dropdownScope) {
    if (!openScope) {
      $document.on('click', closeDropdown);
      $document.on('keydown', keybindFilter);
    }

    if (openScope && openScope !== dropdownScope) {
      openScope.isOpen = false;
    }

    openScope = dropdownScope;
  };

  this.close = function(dropdownScope) {
    if (openScope === dropdownScope) {
      openScope = null;
      $document.off('click', closeDropdown);
      $document.off('keydown', keybindFilter);
    }
  };

  var closeDropdown = function(evt) {
    // This method may still be called during the same mouse event that
    // unbound this event handler. So check openScope before proceeding.
    if (!openScope) { return; }

    if (evt && openScope.getAutoClose() === 'disabled') { return; }

    if (evt && evt.which === 3) { return; }

    var toggleElement = openScope.getToggleElement();
    if (evt && toggleElement && toggleElement[0].contains(evt.target)) {
      return;
    }

    var dropdownElement = openScope.getDropdownElement();
    if (evt && openScope.getAutoClose() === 'outsideClick' &&
      dropdownElement && dropdownElement[0].contains(evt.target)) {
      return;
    }

    openScope.isOpen = false;

    if (!$rootScope.$$phase) {
      openScope.$apply();
    }
  };

  var keybindFilter = function(evt) {
    if (evt.which === 27) {
      openScope.focusToggleElement();
      closeDropdown();
    } else if (openScope.isKeynavEnabled() && [38, 40].indexOf(evt.which) !== -1 && openScope.isOpen) {
      evt.preventDefault();
      evt.stopPropagation();
      openScope.focusDropdownEntry(evt.which);
    }
  };
}])

.controller('UibDropdownController', ['$scope', '$element', '$attrs', '$parse', 'uibDropdownConfig', 'uibDropdownService', '$animate', '$uibPosition', '$document', '$compile', '$templateRequest', function($scope, $element, $attrs, $parse, dropdownConfig, uibDropdownService, $animate, $position, $document, $compile, $templateRequest) {
  var self = this,
    scope = $scope.$new(), // create a child scope so we are not polluting original one
    templateScope,
    appendToOpenClass = dropdownConfig.appendToOpenClass,
    openClass = dropdownConfig.openClass,
    getIsOpen,
    setIsOpen = angular.noop,
    toggleInvoker = $attrs.onToggle ? $parse($attrs.onToggle) : angular.noop,
    appendToBody = false,
    appendTo = null,
    keynavEnabled = false,
    selectedOption = null,
    body = $document.find('body');

  $element.addClass('dropdown');

  this.init = function() {
    if ($attrs.isOpen) {
      getIsOpen = $parse($attrs.isOpen);
      setIsOpen = getIsOpen.assign;

      $scope.$watch(getIsOpen, function(value) {
        scope.isOpen = !!value;
      });
    }

    if (angular.isDefined($attrs.dropdownAppendTo)) {
      var appendToEl = $parse($attrs.dropdownAppendTo)(scope);
      if (appendToEl) {
        appendTo = angular.element(appendToEl);
      }
    }

    appendToBody = angular.isDefined($attrs.dropdownAppendToBody);
    keynavEnabled = angular.isDefined($attrs.keyboardNav);

    if (appendToBody && !appendTo) {
      appendTo = body;
    }

    if (appendTo && self.dropdownMenu) {
      appendTo.append(self.dropdownMenu);
      $element.on('$destroy', function handleDestroyEvent() {
        self.dropdownMenu.remove();
      });
    }
  };

  this.toggle = function(open) {
    scope.isOpen = arguments.length ? !!open : !scope.isOpen;
    if (angular.isFunction(setIsOpen)) {
      setIsOpen(scope, scope.isOpen);
    }

    return scope.isOpen;
  };

  // Allow other directives to watch status
  this.isOpen = function() {
    return scope.isOpen;
  };

  scope.getToggleElement = function() {
    return self.toggleElement;
  };

  scope.getAutoClose = function() {
    return $attrs.autoClose || 'always'; //or 'outsideClick' or 'disabled'
  };

  scope.getElement = function() {
    return $element;
  };

  scope.isKeynavEnabled = function() {
    return keynavEnabled;
  };

  scope.focusDropdownEntry = function(keyCode) {
    var elems = self.dropdownMenu ? //If append to body is used.
      angular.element(self.dropdownMenu).find('a') :
      $element.find('ul').eq(0).find('a');

    switch (keyCode) {
      case 40: {
        if (!angular.isNumber(self.selectedOption)) {
          self.selectedOption = 0;
        } else {
          self.selectedOption = self.selectedOption === elems.length - 1 ?
            self.selectedOption :
            self.selectedOption + 1;
        }
        break;
      }
      case 38: {
        if (!angular.isNumber(self.selectedOption)) {
          self.selectedOption = elems.length - 1;
        } else {
          self.selectedOption = self.selectedOption === 0 ?
            0 : self.selectedOption - 1;
        }
        break;
      }
    }
    elems[self.selectedOption].focus();
  };

  scope.getDropdownElement = function() {
    return self.dropdownMenu;
  };

  scope.focusToggleElement = function() {
    if (self.toggleElement) {
      self.toggleElement[0].focus();
    }
  };

  scope.$watch('isOpen', function(isOpen, wasOpen) {
    if (appendTo && self.dropdownMenu) {
      var pos = $position.positionElements($element, self.dropdownMenu, 'bottom-left', true),
        css,
        rightalign;

      css = {
        top: pos.top + 'px',
        display: isOpen ? 'block' : 'none'
      };

      rightalign = self.dropdownMenu.hasClass('dropdown-menu-right');
      if (!rightalign) {
        css.left = pos.left + 'px';
        css.right = 'auto';
      } else {
        css.left = 'auto';
        css.right = window.innerWidth -
          (pos.left + $element.prop('offsetWidth')) + 'px';
      }

      // Need to adjust our positioning to be relative to the appendTo container
      // if it's not the body element
      if (!appendToBody) {
        var appendOffset = $position.offset(appendTo);

        css.top = pos.top - appendOffset.top + 'px';

        if (!rightalign) {
          css.left = pos.left - appendOffset.left + 'px';
        } else {
          css.right = window.innerWidth -
            (pos.left - appendOffset.left + $element.prop('offsetWidth')) + 'px';
        }
      }

      self.dropdownMenu.css(css);
    }

    var openContainer = appendTo ? appendTo : $element;
    var hasOpenClass = openContainer.hasClass(appendTo ? appendToOpenClass : openClass);

    if (hasOpenClass === !isOpen) {
      $animate[isOpen ? 'addClass' : 'removeClass'](openContainer, appendTo ? appendToOpenClass : openClass).then(function() {
        if (angular.isDefined(isOpen) && isOpen !== wasOpen) {
          toggleInvoker($scope, { open: !!isOpen });
        }
      });
    }

    if (isOpen) {
      if (self.dropdownMenuTemplateUrl) {
        $templateRequest(self.dropdownMenuTemplateUrl).then(function(tplContent) {
          templateScope = scope.$new();
          $compile(tplContent.trim())(templateScope, function(dropdownElement) {
            var newEl = dropdownElement;
            self.dropdownMenu.replaceWith(newEl);
            self.dropdownMenu = newEl;
          });
        });
      }

      scope.focusToggleElement();
      uibDropdownService.open(scope);
    } else {
      if (self.dropdownMenuTemplateUrl) {
        if (templateScope) {
          templateScope.$destroy();
        }
        var newEl = angular.element('

');
        self.dropdownMenu.replaceWith(newEl);
        self.dropdownMenu = newEl;
      }

      uibDropdownService.close(scope);
      self.selectedOption = null;
    }

    if (angular.isFunction(setIsOpen)) {
      setIsOpen($scope, isOpen);
    }
  });

  $scope.$on('$locationChangeSuccess', function() {
    if (scope.getAutoClose() !== 'disabled') {
      scope.isOpen = false;
    }
  });
}])

.directive('uibDropdown', function() {
  return {
    controller: 'UibDropdownController',
    link: function(scope, element, attrs, dropdownCtrl) {
      dropdownCtrl.init();
    }
  };
})

.directive('uibDropdownMenu', function() {
  return {
    restrict: 'A',
    require: '?^uibDropdown',
    link: function(scope, element, attrs, dropdownCtrl) {
      if (!dropdownCtrl || angular.isDefined(attrs.dropdownNested)) {
        return;
      }

      element.addClass('dropdown-menu');

      var tplUrl = attrs.templateUrl;
      if (tplUrl) {
        dropdownCtrl.dropdownMenuTemplateUrl = tplUrl;
      }

      if (!dropdownCtrl.dropdownMenu) {
        dropdownCtrl.dropdownMenu = element;
      }
    }
  };
})

.directive('uibDropdownToggle', function() {
  return {
    require: '?^uibDropdown',
    link: function(scope, element, attrs, dropdownCtrl) {
      if (!dropdownCtrl) {
        return;
      }

      element.addClass('dropdown-toggle');

      dropdownCtrl.toggleElement = element;

      var toggleDropdown = function(event) {
        event.preventDefault();

        if (!element.hasClass('disabled') && !attrs.disabled) {
          scope.$apply(function() {
            dropdownCtrl.toggle();
          });
        }
      };

      element.bind('click', toggleDropdown);

      // WAI-ARIA
      element.attr({ 'aria-haspopup': true, 'aria-expanded': false });
      scope.$watch(dropdownCtrl.isOpen, function(isOpen) {
        element.attr('aria-expanded', !!isOpen);
      });

      scope.$on('$destroy', function() {
        element.unbind('click', toggleDropdown);
      });
    }
  };
});
<|endoftext|>


<commit_before>import BaseLayer from '../base-layer'
import Gradient from './gradient.js'
import * as utils from './utils.js'
import vertextShaderSource from './vertex.glsl'
import fragmentShaderSource from './fragment.glsl'

// -------------------------------------
// WebglLayer
// -------------------------------------

class WebglLayer extends BaseLayer {
  constructor ({ Vector, stats, width, height }) {
    super({ Vector, stats, width, height })

    this.gl = utils.getContext(this.canvas)

    this.scale({ width, height })

    this.imageLoader.onload = this.forceRedraw.bind(this)

    this.positions = []

    this._vertexShader = utils.compileShader(this.gl, vertextShaderSource, this.gl.VERTEX_SHADER)
    this._fragmentShader = utils.compileShader(this.gl, fragmentShaderSource, this.gl.FRAGMENT_SHADER)

    this._program = utils.createProgram(this.gl, this._vertexShader, this._fragmentShader)
    this.gl.useProgram(this._program)

    this._positionLocation = this.gl.getAttribLocation(this._program, 'a_position')
    this._colorLocation = this.gl.getAttribLocation(this._program, 'a_color')

    this._buffer = this.gl.createBuffer()
    this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this._buffer)
    this.gl.enableVertexAttribArray(this._positionLocation)
    this.gl.vertexAttribPointer(
      this._positionLocation,
      2,
      this.gl.FLOAT,
      false,
      Float32Array.BYTES_PER_ELEMENT * 6,
      0
    )
    this.gl.enableVertexAttribArray(this._colorLocation)
    this.gl.vertexAttribPointer(
      this._colorLocation,
      4,
      this.gl.FLOAT,
      false,
      Float32Array.BYTES_PER_ELEMENT * 6,
      Float32Array.BYTES_PER_ELEMENT * 2
    )
  }

  scale ({ width, height }) {
    super.scale({ width, height })

    if (window.devicePixelRatio) {
      this.canvas.width = this.width *= window.devicePixelRatio
      this.canvas.height = this.height *= window.devicePixelRatio
    }

    this.gl.viewport(0, 0, this.width, this.height)
  }

  clear () {
    super.clear()
    this.positions = []
    this.gl.clearColor(0, 0, 0, 0)
    this.gl.clearDepth(1)
    this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT)
  }

  redraw () {
    super.redraw()

    this.gl.bufferData(
      this.gl.ARRAY_BUFFER,
      new Float32Array(this.positions),
      this.gl.STATIC_DRAW
    )
    this.gl.drawArrays(this.gl.LINES, 0, this.positions.length / 6)
  }

  convertPoints (points) {
    var result = []
    for (var i = 0; i < points.length; i++) {
      var point = points[i]
      var x = point.x
      var y = point.y
      result.push(new this.Vector(x / this.width * 2 - 1, y / this.height * -2 + 1))
    }
    return result
  }

  createGradient ({ start, end, from, to }) {
    return new Gradient({ start, end, from, to })
  }

  getColor (color) {
    return Gradient.isGradient(color) ? color.createGradient(this) : utils.parseColor(color)
  }

  drawArc ({ position, radius, startAngle, endAngle, color, width = 1 }) {

  }

  drawCircle ({ position, radius, color, fillColor, width = 1 }) {

  }

  drawImage ({ position, image, width = image.width, height = image.height, opacity = 1 }) {
    if (typeof image === 'string') {
      if (this.imageLoader.getStatus(image) === ImageLoader.IMAGE_STATUS_LOADED) {
        image = this.imageLoader.getImage(image)
        width = width || image.width
        height = height || image.height
      } else if (this.imageLoader.getStatus(image) !== ImageLoader.IMAGE_STATUS_LOADING) {
        this.imageLoader.load(image)
        return
      } else {
        return
      }
    }
  }

  drawPolygon ({ points, color, fillColor, width = 1 }) {
    this.collectStats('drawPolygon')

    this.drawPolyline({
      points: points.concat(points[0]),
      color,
      width
    })
  }

  drawPolyline ({ points, color, lineDash = [], width = 1 }) {
    this.collectStats('drawPolyline')

    points = this.convertPoints(points)
    color = this.getColor(color)
    for (var i = 1; i < points.length; i++) {
      this.positions.push(points[i - 1].x, points[i - 1].y, color[0], color[1], color[2], 1)
      this.positions.push(points[i].x, points[i].y, color[0], color[1], color[2], 1)
    }
  }

  drawRect ({ position, width, height, color, fillColor, strokeWidth = 1 }) {

  }

  drawText ({ position, text, color, font, size, align = 'center', baseline = 'middle' }) {

  }

  measureText ({ text }) {
    return 0
  }

  drawStats () {
    var stats = this.formatStats()

    for (var i = stats.length; i--;) {
      this.drawText({
        position: new this.Vector(this.width - 10, this.height - 14 * (stats.length - i)),
        text: stats[i],
        color: '#fff',
        font: 'Courier, monospace',
        size: 14,
        align: 'right',
        baleline: 'bottom'
      })
    }
  }
}

export default WebglLayer
<commit_msg>Fixed lint issue<commit_after>import BaseLayer from '../base-layer'
import Gradient from './gradient.js'
import * as utils from './utils.js'
import vertextShaderSource from './vertex.glsl'
import fragmentShaderSource from './fragment.glsl'

// -------------------------------------
// WebglLayer
// -------------------------------------

class WebglLayer extends BaseLayer {
  constructor ({ Vector, stats, width, height }) {
    super({ Vector, stats, width, height })

    this.gl = utils.getContext(this.canvas)

    this.scale({ width, height })

    this.imageLoader.onload = this.forceRedraw.bind(this)

    this.positions = []

    this._vertexShader = utils.compileShader(this.gl, vertextShaderSource, this.gl.VERTEX_SHADER)
    this._fragmentShader = utils.compileShader(this.gl, fragmentShaderSource, this.gl.FRAGMENT_SHADER)

    this._program = utils.createProgram(this.gl, this._vertexShader, this._fragmentShader)
    this.gl.useProgram(this._program)

    this._positionLocation = this.gl.getAttribLocation(this._program, 'a_position')
    this._colorLocation = this.gl.getAttribLocation(this._program, 'a_color')

    this._buffer = this.gl.createBuffer()
    this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this._buffer)
    this.gl.enableVertexAttribArray(this._positionLocation)
    this.gl.vertexAttribPointer(
      this._positionLocation,
      2,
      this.gl.FLOAT,
      false,
      Float32Array.BYTES_PER_ELEMENT * 6,
      0
    )
    this.gl.enableVertexAttribArray(this._colorLocation)
    this.gl.vertexAttribPointer(
      this._colorLocation,
      4,
      this.gl.FLOAT,
      false,
      Float32Array.BYTES_PER_ELEMENT * 6,
      Float32Array.BYTES_PER_ELEMENT * 2
    )
  }

  scale ({ width, height }) {
    super.scale({ width, height })

    if (window.devicePixelRatio) {
      this.canvas.width = this.width *= window.devicePixelRatio
      this.canvas.height = this.height *= window.devicePixelRatio
    }

    this.gl.viewport(0, 0, this.width, this.height)
  }

  clear () {
    super.clear()
    this.positions = []
    this.gl.clearColor(0, 0, 0, 0)
    this.gl.clearDepth(1)
    this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT)
  }

  redraw () {
    super.redraw()

    this.gl.bufferData(
      this.gl.ARRAY_BUFFER,
      new Float32Array(this.positions),
      this.gl.STATIC_DRAW
    )
    this.gl.drawArrays(this.gl.LINES, 0, this.positions.length / 6)
  }

  convertPoints (points) {
    var result = []
    for (var i = 0; i < points.length; i++) {
      var point = points[i]
      var x = point.x
      var y = point.y
      result.push(new this.Vector(x / this.width * 2 - 1, y / this.height * -2 + 1))
    }
    return result
  }

  createGradient ({ start, end, from, to }) {
    return new Gradient({ start, end, from, to })
  }

  getColor (color) {
    return Gradient.isGradient(color) ? color.createGradient(this) : utils.parseColor(color)
  }

  drawArc ({ position, radius, startAngle, endAngle, color, width = 1 }) {

  }

  drawCircle ({ position, radius, color, fillColor, width = 1 }) {

  }

  drawImage ({ position, image, width = image.width, height = image.height, opacity = 1 }) {
    if (typeof image === 'string') {
      if (this.imageLoader.getStatus(image) === this.imageLoader.IMAGE_STATUS_LOADED) {
        image = this.imageLoader.getImage(image)
        width = width || image.width
        height = height || image.height
      } else if (this.imageLoader.getStatus(image) !== this.imageLoader.IMAGE_STATUS_LOADING) {
        this.imageLoader.load(image)
        return
      } else {
        return
      }
    }
  }

  drawPolygon ({ points, color, fillColor, width = 1 }) {
    this.collectStats('drawPolygon')

    this.drawPolyline({
      points: points.concat(points[0]),
      color,
      width
    })
  }

  drawPolyline ({ points, color, lineDash = [], width = 1 }) {
    this.collectStats('drawPolyline')

    points = this.convertPoints(points)
    color = this.getColor(color)
    for (var i = 1; i < points.length; i++) {
      this.positions.push(points[i - 1].x, points[i - 1].y, color[0], color[1], color[2], 1)
      this.positions.push(points[i].x, points[i].y, color[0], color[1], color[2], 1)
    }
  }

  drawRect ({ position, width, height, color, fillColor, strokeWidth = 1 }) {

  }

  drawText ({ position, text, color, font, size, align = 'center', baseline = 'middle' }) {

  }

  measureText ({ text }) {
    return 0
  }

  drawStats () {
    var stats = this.formatStats()

    for (var i = stats.length; i--;) {
      this.drawText({
        position: new this.Vector(this.width - 10, this.height - 14 * (stats.length - i)),
        text: stats[i],
        color: '#fff',
        font: 'Courier, monospace',
        size: 14,
        align: 'right',
        baleline: 'bottom'
      })
    }
  }
}

export default WebglLayer
<|endoftext|>


<commit_before>var http = require('http')
var https = require('https')
var mapper = require('./mapper')
var querystring = require('querystring')
var parser = require('./parser')
var url = require('url')

function getHttpLib(rootUrl) {
  switch (url.parse(rootUrl).protocol) {
    case 'https:':
      return https
    case 'http:':
      return http
    default:
      throw new Error('Unsupported protocol: ' + url.parse(rootUrl).protocol)
  }
}

function createClient(rootUrl) {
  var _http = getHttpLib(rootUrl)
  var client = {}

  client.get = function get(path, query) {
    var stream = parser.stream()

    _http.get(
      `${rootUrl}/${path}?${querystring.stringify(query || {})}`,
      (res) => res.pipe(stream)
    )

    return parser.capture(stream)
      .catch(function (err) {
        throw new Error('Failed to parse response to [' + path + ',' + JSON.stringify(query) + '] with:\n' + err.message)
      })
  }

  client.getUser = function getUser(name) {
    return client.get('user', {
      name
    })
      .then(mapper.mapUserResponse)
  }

  client.getThing = function getThing(id, options) {
    return client.get('thing', {
      id,
      stats: (options && options.stats) ? 1 : 0,
      type: options.type
    })
  }

  client.getThings = function getThings(ids, options) {
    return client.getThing(ids.join(','), options)
  }

  function handleErrors(response) {
    if (response.error) {
      return Promise.reject(new Error(response.error[0].message))
    }

    return response
  }

  client.search = function search(options) {
    return client.get('search', options)
      .then((response) => handleErrors(response))
      .then((response) => ({
        total: Number(response.items[0].total),
        items: response.items[0].item
      }))
  }

  function findThing(name, options) {
    var searchType = options && (options.searchType || options.type)
    var getType = options && (options.getType || options.type)
    var exact = options && options.exact

    return client.search({
      query: name,
      type: searchType,
      exact: 1
    })
      .then(function (response) {
        if ((response.total === 0 || !response.items) && !exact) {
          return client.search({
            query: name,
            type: searchType,
            exact: 0
          })
        }

        return response
      })
      .then(function (response) {
        if (response.total === 0 || !response.items) {
          return null
        }

        return client.getThing(response.items[0].id, {
          stats: true,
          type: getType
        })
      })
  }

  client.getBoardGame = function getBoardGame(name, options) {
    var includeExpansions = options && options.includeExpansions
    var exact = options && options.exact

    return findThing(name, {
      exact: !!exact,
      searchType: 'boardgame',
      getType: includeExpansions ? null : 'boardgame'
    })
      .then(mapper.mapBoardGameResponse)
  }

  client.getBoardGameExpansion = function getBoardGameExpansion(name) {
    var exact = options && options.exact

    return findThing(name, {
      exact: !!exact,
      type: 'boardgameexpansion'
    })
      .then(mapper.mapBoardGameResponse)
  }

  return client
}

module.exports = createClient
<commit_msg>Add getBoardGame(Expansion)ById.<commit_after>var http = require('http')
var https = require('https')
var mapper = require('./mapper')
var querystring = require('querystring')
var parser = require('./parser')
var url = require('url')

function getHttpLib(rootUrl) {
  switch (url.parse(rootUrl).protocol) {
    case 'https:':
      return https
    case 'http:':
      return http
    default:
      throw new Error('Unsupported protocol: ' + url.parse(rootUrl).protocol)
  }
}

function createClient(rootUrl) {
  var _http = getHttpLib(rootUrl)
  var client = {}

  client.get = function get(path, query) {
    var stream = parser.stream()

    _http.get(
      `${rootUrl}/${path}?${querystring.stringify(query || {})}`,
      (res) => res.pipe(stream)
    )

    return parser.capture(stream)
      .catch(function (err) {
        throw new Error('Failed to parse response to [' + path + ',' + JSON.stringify(query) + '] with:\n' + err.message)
      })
  }

  client.getUser = function getUser(name) {
    return client.get('user', {
      name
    })
      .then(mapper.mapUserResponse)
  }

  client.getThing = function getThing(id, options) {
    return client.get('thing', {
      id,
      stats: (options && options.stats) ? 1 : 0,
      type: options.type
    })
  }

  client.getThings = function getThings(ids, options) {
    return client.getThing(ids.join(','), options)
  }

  function handleErrors(response) {
    if (response.error) {
      return Promise.reject(new Error(response.error[0].message))
    }

    return response
  }

  client.search = function search(options) {
    return client.get('search', options)
      .then((response) => handleErrors(response))
      .then((response) => ({
        total: Number(response.items[0].total),
        items: response.items[0].item
      }))
  }

  function findThing(name, options) {
    var searchType = options && (options.searchType || options.type)
    var getType = options && (options.getType || options.type)
    var exact = options && options.exact

    return client.search({
      query: name,
      type: searchType,
      exact: 1
    })
      .then(function (response) {
        if ((response.total === 0 || !response.items) && !exact) {
          return client.search({
            query: name,
            type: searchType,
            exact: 0
          })
        }

        return response
      })
      .then(function (response) {
        if (response.total === 0 || !response.items) {
          return null
        }

        return client.getThing(response.items[0].id, {
          stats: true,
          type: getType
        })
      })
  }

  client.getBoardGame = function getBoardGame(name, options) {
    var includeExpansions = options && options.includeExpansions
    var exact = options && options.exact

    return findThing(name, {
      exact: !!exact,
      searchType: 'boardgame',
      getType: includeExpansions ? null : 'boardgame'
    })
      .then(mapper.mapBoardGameResponse)
  }

  client.getBoardGameExpansion = function getBoardGameExpansion(name) {
    var exact = options && options.exact

    return findThing(name, {
      exact: !!exact,
      type: 'boardgameexpansion'
    })
      .then(mapper.mapBoardGameResponse)
  }

  client.getBoardGameById =
  client.getBoardGameExpansionById =
  function getBoardGameById(id, options) {
    return client.getThing(id, {
      stats: true,
      type: 'boardgame'
    })
      .then(mapper.mapBoardGameResponse)
  }

  return client
}

module.exports = createClient
<|endoftext|>


<commit_before>import React from 'react';
import { connect } from 'react-redux';
import Dropdown from '../../taskboard/components/dropdown/dropdown';
import { sendmsgMenuList, getIndexOfArrayByValue } from '../../../utils';
import { newMessage } from '../actions';

class ChatroomInput extends React.Component {

	constructor(props){
		super(props);

		this.state = {
			hotKey: 1,
			message: ''
		};

		this.sendMsgHotKeyDropdown = {
			menuList: sendmsgMenuList,
			btnId: 'sendmsgTypeDropdown',
			handleClick: this.selectSendMsgHotKey.bind(this),
			dropdownIcon: true
		};

		this.handleInputChange = this.handleInputChange.bind(this);
		this.handleKeyPress = this.handleKeyPress.bind(this);
		this.handleClick = this.handleClick.bind(this);
	}

	handleClick(event){
		if(this.state.message.trim()){
			let payload = {
				message: this.state.message,
				user: jwt_decode(localStorage.getItem('token')),
				room: this.props.projectId,
				timestamp: (new Date()).toString(),
				byself: true
			};
			socket.emit('send message', payload);
			this.props.newMessage(payload);
			this.setState({
				message: ''
			});
		} else {
			return;
		}
	}

	handleKeyPress(event){
		if(event.charCode == 13){
			event.preventDefault();		//prevent triggering onchange
			if(this.state.message.trim()){
				let payload = {
					message: this.state.message,
					user: jwt_decode(localStorage.getItem('token')),
					room: this.props.projectId,
					timestamp: (new Date()).toString(),
					byself: true
				};
				socket.emit('send message', payload);
				this.props.newMessage(payload);
				this.setState({
					message: ''
				});
			} else {
				return;
			}
		}
	}

	handleInputChange(event){
		const target = event.target;
		const value = target.value;
		const name = target.name;
		this.setState({
			[name]: value
		});
	}

	selectSendMsgHotKey(e){
		let target = e.target;
		if(!target.name) { return; }
		let index = getIndexOfArrayByValue(sendmsgMenuList, 'name', target.name);		//any better way ???
		this.setState({
			hotKey: sendmsgMenuList[index].keyName
		});
	}

	render(){
		return(

					发送
					<Dropdown dropdown={this.sendMsgHotKeyDropdown} btnName="" btnStyle={{}} />

					<textarea className="form-control" rows="1" name="message"
						placeholder="说点什么吧"
						onKeyPress={this.handleKeyPress}
						onChange={this.handleInputChange} value={this.state.message} />

		);
	}

}

// const mapStateToProps = state => ({
// 	currentUser: state.auth.currentUser
// });

const mapDispatchToProps = dispatch => ({
	newMessage: payload => { dispatch(newMessage(payload)); }
});

export default connect(null, mapDispatchToProps)(ChatroomInput);<commit_msg>chatroom can change input hotkey<commit_after>import React from 'react';
import { connect } from 'react-redux';
import Dropdown from '../../taskboard/components/dropdown/dropdown';
import { sendmsgMenuList, getIndexOfArrayByValue } from '../../../utils';
import { newMessage } from '../actions';

class ChatroomInput extends React.Component {

	constructor(props){
		super(props);

		this.state = {
			hotKey: 1,	// send message with `enter`
			message: ''
		};

		this.sendMsgHotKeyDropdown = {
			menuList: sendmsgMenuList,
			btnId: 'sendmsgTypeDropdown',
			handleClick: this.selectSendMsgHotKey.bind(this),
			dropdownIcon: true
		};

		this.handleInputChange = this.handleInputChange.bind(this);
		this.handleKeyPress = this.handleKeyPress.bind(this);
		this.handleKeyDown = this.handleKeyDown.bind(this);
		this.handleClick = this.handleClick.bind(this);
	}

	handleClick(event){
		if(this.state.message.trim()){
			let payload = {
				message: this.state.message,
				user: jwt_decode(localStorage.getItem('token')),
				room: this.props.projectId,
				timestamp: (new Date()).toString(),
				byself: true
			};
			socket.emit('send message', payload);
			this.props.newMessage(payload);
			this.setState({
				message: ''
			});
		} else {
			return;
		}
	}

	handleKeyDown(event){
		if(event.which == 13 && (this.state.hotKey == 1 && !event.ctrlKey || this.state.hotKey == 2 && event.ctrlKey)){
			event.preventDefault();		//prevent triggering onchange
			if(this.state.message.trim()){
				let payload = {
					message: this.state.message,
					user: jwt_decode(localStorage.getItem('token')),
					room: this.props.projectId,
					timestamp: (new Date()).toString(),
					byself: true
				};
				socket.emit('send message', payload);
				this.props.newMessage(payload);
				this.setState({
					message: ''
				});
			} else {
				return;
			}
		}
		if(event.which == 13 && (this.state.hotKey == 2 && !event.ctrlKey || this.state.hotKey == 1 && event.ctrlKey)){
			event.preventDefault();
			this.setState({
				message: this.state.message + '\n'
			});
		}

	}

	handleKeyPress(event){

	}

	handleInputChange(event){
		const target = event.target;
		const value = target.value;
		const name = target.name;
		this.setState({
			[name]: value
		});
	}

	selectSendMsgHotKey(e){
		let target = e.target;
		if(!target.name) { return; }
		let index = getIndexOfArrayByValue(sendmsgMenuList, 'name', target.name);		//any better way ???
		this.setState({
			hotKey: sendmsgMenuList[index].keyName
		});
	}

	render(){
		return(

					发送
					<Dropdown dropdown={this.sendMsgHotKeyDropdown} btnName="" btnStyle={{}} />

					<textarea className="form-control" rows="1" name="message"
						placeholder="说点什么吧"
						onKeyPress={this.handleKeyPress}
						onKeyDown={this.handleKeyDown}
						onChange={this.handleInputChange} value={this.state.message} />

		);
	}

}

// const mapStateToProps = state => ({
// 	currentUser: state.auth.currentUser
// });

const mapDispatchToProps = dispatch => ({
	newMessage: payload => { dispatch(newMessage(payload)); }
});

export default connect(null, mapDispatchToProps)(ChatroomInput);<|endoftext|>


<commit_before>import React from 'react';

class Notif extends React.Component {
  constructor() {
    super();
    this._id = new Date().getTime();
    this._onActionClick = this._onActionClick.bind(this);
  }

  /*
   * Handle action click event
   * @description Handle click events on the
   */
  _onActionClick(event) {
    event.preventDefault();
    if (this.props.onActionClick) {
      this.props.onActionClick(this.props.id);
    } else {
      return;
    }
  }

  render() {
    const { kind, CustomComponent, componentClassName, actionLabel } = this.props;
    const component = !CustomComponent ?
      <div className={`${componentClassName} ${componentClassName}--${kind}`}>
          <div className={`${componentClassName}__icon`} />
          <div className={`${componentClassName}__content`}>
            <span className={`${componentClassName}__message`}>{this.props.message}

          { actionLabel &&
            <span className={`${componentClassName}__action`}>
              <button onClick={this._onActionClick}>{this.props.actionLabel}</button>

          }
          <div className={`${componentClassName}__close`} />

      :
      <CustomComponent {...this.props} />;

    return component;
  }
}

Notif.defaultProps = {
  kind: 'info',
};

Notif.propTypes = {
  id: React.PropTypes.oneOfType([
    React.PropTypes.string,
    React.PropTypes.number
  ]).isRequired,
  message: React.PropTypes.string.isRequired,
  kind: React.PropTypes.oneOf([
    'success',
    'info',
    'warning',
    'danger'
  ]).isRequired,
  componentClassName: React.PropTypes.string,
  onActionClick: React.PropTypes.func,
  actionLabel: React.PropTypes.string,
  CustomComponent: React.PropTypes.oneOfType([
    React.PropTypes.func,
    React.PropTypes.node,
    React.PropTypes.element
  ]),
};

export default Notif;
<commit_msg>notif is pure component<commit_after>import React from 'react';

const Notif = ({ kind, CustomComponent, componentClassName, actionLabel, onActionClick, id }) => {
  const _onActionClick = (ev) => {
    ev.preventDefault();

    if (!onActionClick) {
      return;
    }

    onActionClick(id);
  };

  if (CustomComponent) {
    return <CustomComponent {...this.props} />;
  }

  return (
    <div className={`${componentClassName} ${componentClassName}--${kind}`}>
        <div className={`${componentClassName}__icon`} />
        <div className={`${componentClassName}__content`}>
          <span className={`${componentClassName}__message`}>{this.props.message}

        { actionLabel &&
          <span className={`${componentClassName}__action`}>
            <button onClick={_onActionClick}>{this.props.actionLabel}</button>

        }
        <div className={`${componentClassName}__close`} />

  );
};

Notif.defaultProps = {
  kind: 'info',
};

Notif.propTypes = {
  id: React.PropTypes.oneOfType([
    React.PropTypes.string,
    React.PropTypes.number
  ]).isRequired,
  message: React.PropTypes.string,
  kind: React.PropTypes.oneOf([
    'success',
    'info',
    'warning',
    'danger'
  ]),
  componentClassName: React.PropTypes.string,
  onActionClick: React.PropTypes.func,
  actionLabel: React.PropTypes.string,
  CustomComponent: React.PropTypes.oneOfType([
    React.PropTypes.func,
    React.PropTypes.node,
    React.PropTypes.element
  ]),
};

export default Notif;
<|endoftext|>


<commit_before>/*
 * openi_data_api
 * openi-ict.eu
 *
 * Copyright (c) 2013 dmccarthy
 * Licensed under the MIT license.
 */

'use strict';

var openiLogger  = require('openi-logger')
var openiUtils   = require('openi-cloudlet-utils')

var init = function(logger_params){
   this.logger = openiLogger(logger_params);
}

var createCloudlet = function(msg){

   var cloudletId = 'c_' + openiUtils.randomHash();

   var cloudletDBObj = {
      id       : cloudletId,
      alias    : msg.json.alias,
      username : msg.json.username
   }

   var responseObject = {
      value      : true,
      cloudletId : cloudletId
   }

   return {
      'action'       : 'CREATE',
      'cloudlet'  : cloudletId,
      'object_data'  : cloudletDBObj,
      'mongrel_resp' : responseObject,
      'clients'       : [
         {'uuid' : msg.uuid, 'connId' : msg.connId }
      ]
   }
}

var deleteCloudlet = function(msg){

   var cloudletId = msg.path.replace('/api/v1/cloudlets/', '')

   var responseObject = {
      value      : true,
      cloudletId : cloudletId
   }

   return {
      'action'       : 'DELETE',
      'cloudlet'  : cloudletId,
      'object_data'  : {},
      'mongrel_resp' : responseObject,
      'clients'       : [
         {'uuid' : msg.uuid, 'connId' : msg.connId }
      ]
   }
}

var exportCloudlet = function(msg){

   var cloudletId = msg.path.replace('/api/v1/cloudlets/', '')

   var responseObject = {
      value      : true,
      cloudletId : cloudletId
   }

   return {
      'action'       : 'FETCH',
      'cloudlet'     : cloudletId,
      'object_name'  : {},
      'mongrel_resp' : responseObject,
      'clients'       : [
         {'uuid' : msg.uuid, 'connId' : msg.connId }
      ]
   }

}

var processMongrel2Message = function (msg) {

   this.logger.log('debug', 'process Mongrel 2 Message function')

   this.logger.logMongrel2Message(msg)

   if (msg.headers.METHOD === 'POST'){
      return createCloudlet(msg)
   }
   else if (msg.headers.METHOD === 'DELETE'){
      return deleteCloudlet(msg)
   }
   else if (msg.headers.METHOD === 'GET'){
      return exportCloudlet(msg)
   }
   else{
      this.logger.log('debug', 'Matching function wasn\'t found')
   }
}

module.exports.init                   = init
module.exports.processMongrel2Message = processMongrel2Message<commit_msg>Updated API and added DBC<commit_after>/*
 * openi_data_api
 * openi-ict.eu
 *
 * Copyright (c) 2013 dmccarthy
 * Licensed under the MIT license.
 */

'use strict';

var dbc          = require('dbc');
var openiLogger  = require('openi-logger')
var openiUtils   = require('openi-cloudlet-utils')

var init = function(logger_params){
   this.logger = openiLogger(logger_params);
}

var createCloudlet = function(msg){

   dbc.assert(null !== msg.json)

   var cloudletId = 'c_' + openiUtils.randomHash();

   var cloudletDBObj = {
      id       : cloudletId,
      alias    : msg.json.alias,
      username : msg.json.username
   }

   var responseObject = {
      value      : true,
      cloudletId : cloudletId
   }

   return {
      'action'        : 'CREATE',
      'cloudlet'      : cloudletId,
      'object_name'   : 'meta',
      'object_data'   : cloudletDBObj,
      'mongrel_resp'  : responseObject,
      'clients'       : [
         {'uuid' : msg.uuid, 'connId' : msg.connId }
      ]
   }
}

var deleteCloudlet = function(msg){

   var cloudletId = msg.path.replace('/api/v1/cloudlets/', '')

   var responseObject = {
      value      : true,
      cloudletId : cloudletId
   }

   return {
      'action'       : 'DELETE',
      'cloudlet'  : cloudletId,
      'object_data'  : {},
      'mongrel_resp' : responseObject,
      'clients'       : [
         {'uuid' : msg.uuid, 'connId' : msg.connId }
      ]
   }
}

var exportCloudlet = function(msg){

   var cloudletId = msg.path.replace('/api/v1/cloudlets/', '')

   var responseObject = {
      value      : true,
      cloudletId : cloudletId
   }

   return {
      'action'       : 'FETCH',
      'cloudlet'     : cloudletId,
      'object_name'  : {},
      'mongrel_resp' : responseObject,
      'clients'       : [
         {'uuid' : msg.uuid, 'connId' : msg.connId }
      ]
   }

}

var processMongrel2Message = function (msg) {

   this.logger.log('debug', 'process Mongrel 2 Message function')

   this.logger.logMongrel2Message(msg)

   if (msg.headers.METHOD === 'POST'){
      return createCloudlet(msg)
   }
   else if (msg.headers.METHOD === 'DELETE'){
      return deleteCloudlet(msg)
   }
   else if (msg.headers.METHOD === 'GET'){
      return exportCloudlet(msg)
   }
   else{
      this.logger.log('debug', 'Matching function wasn\'t found')
   }
}

module.exports.init                   = init
module.exports.processMongrel2Message = processMongrel2Message<|endoftext|>


<commit_before>'use strict';

var gulp = require('gulp');
var less = require('gulp-less');
var through = require('through2');
var transpiler = require('es6-module-transpiler');
var Container = transpiler.Container;
var FileResolver = transpiler.FileResolver;
var AmdFormatter = require('es6-module-transpiler-amd-formatter');
var projectDir = require('fs-jetpack');
var utils = require('./utils');

// -------------------------------------
// Setup
// -------------------------------------

var srcDir = projectDir.cwd('./app/');
var destDir = projectDir.cwd('./build/');

// On Windows and Linux our code is going into main directory...
var destForCodeDir = destDir;
if (utils.os() === 'osx') {
    // ...but on OSX deep into folder in bundle structure.
    destForCodeDir = destDir.cwd('./node-webkit.app/Contents/Resources/app.nw');
}

// -------------------------------------
// Tasks
// -------------------------------------

gulp.task('clean', function(callback) {
    return destDir.dirAsync('.', { empty: true });
});

gulp.task('prepare-runtime', ['clean'] , function() {
    var runtimeForThisOs = './nw/' + utils.os();
    return projectDir.copyAsync(runtimeForThisOs, destDir.path(), {
        overwrite: true,
        allBut: [
            'version',
            'nwsnapshot*',
            'credits.html'
        ]
    });
});

gulp.task('copy', ['prepare-runtime'], function() {
    return projectDir.copyAsync('app', destForCodeDir.path(), {
        overwrite: true,
        only: [
            'app/node_modules',
            'app/vendor',
            '*.html'
        ]
    });
});

// Add and customize OS-specyfic and target-specyfic stuff.
gulp.task('finalize', ['prepare-runtime'], function() {
    var manifest = srcDir.read('package.json', 'json');
    switch (utils.getBuildTarget()) {
        case 'release':
            // Hide dev toolbar if doing a release.
            manifest.window.toolbar = false;
            break;
        case 'test':
            // Add "-test" suffix to name, so node-webkit will write all
            // data like cookies and locaStorage into separate place.
            manifest.name += '-test';
            // Change the main entry to spec runner.
            manifest.main = 'spec.html';
            // Set extra flag so we know this is development mode, and we can
            // alter some behaviours of running app.
            manifest.developmentMode = true;
            break;
        case 'development':
            // Add "-dev" suffix to name, so node-webkit will write all
            // data like cookies and locaStorage into separate place.
            manifest.name += '-dev';
            // Set extra flag so we know this is development mode, and we can
            // alter some behaviours of running app.
            manifest.developmentMode = true;
            break;
    }
    destForCodeDir.write('package.json', manifest, { jsonIndent: 4 });

    projectDir.copy('os/icon.png', destForCodeDir.path('icon.png'));

    // Stuff specyfic for certains OS
    switch (utils.os()) {
        case 'windows':
            // icon
            projectDir.copy('os/windows/icon.ico', destDir.path('icon.ico'));
            break;
        case 'osx':
            // Info.plist
            var manifest = projectDir.read('app/package.json', 'json');
            var info = projectDir.read('os/osx/Info.plist');
            info = utils.replace(info, {
                prettyName: manifest.prettyName,
                version: manifest.version
            });
            dest.write('node-webkit.app/Contents/Info.plist', info);
            // icon
            projectDir.copy('os/osx/icon.icns', destDir.path('node-webkit.app/Contents/Resources/icon.icns'));
            break;
    }
});

var transpileTask = function() {
    return gulp.src([
        'app/**/*.js',
        '!app/node_modules/**',
        '!app/vendor/**'
    ], {
        read: false // Don't read the files. ES6 transpiler will do it.
    })
    .pipe(through.obj(function (file, enc, callback) {
        var relPath = file.path.substring(file.base.length);
        var container = new Container({
            resolvers: [new FileResolver([file.base])],
            formatter: new AmdFormatter()
        });
        try {
            container.getModule(relPath);
            container.write(destForCodeDir.path(relPath));
            callback();
        } catch (err) {
            // Show to the user precise file where transpilation error occured.
            callback(relPath + " " + err.message);
        }
    }));
};
gulp.task('transpile', ['prepare-runtime'], transpileTask);
gulp.task('transpile-watch', transpileTask);

var lessTask = function () {
    return gulp.src('app/stylesheets/main.less')
    .pipe(less())
    .pipe(gulp.dest(destForCodeDir.path('stylesheets')));
};
gulp.task('less', ['prepare-runtime'], lessTask);
gulp.task('less-watch', lessTask);

gulp.task('watch', function () {
    gulp.watch('app/stylesheets/**', ['less-watch']);
    gulp.watch('app/**/*.js', ['transpile-watch']);
});

gulp.task('build', ['copy', 'finalize', 'transpile', 'less']);
<commit_msg>Bugfix<commit_after>'use strict';

var gulp = require('gulp');
var less = require('gulp-less');
var through = require('through2');
var transpiler = require('es6-module-transpiler');
var Container = transpiler.Container;
var FileResolver = transpiler.FileResolver;
var AmdFormatter = require('es6-module-transpiler-amd-formatter');
var projectDir = require('fs-jetpack');
var utils = require('./utils');

// -------------------------------------
// Setup
// -------------------------------------

var srcDir = projectDir.cwd('./app/');
var destDir = projectDir.cwd('./build/');

// On Windows and Linux our code is going into main directory...
var destForCodeDir = destDir;
if (utils.os() === 'osx') {
    // ...but on OSX deep into folder in bundle structure.
    destForCodeDir = destDir.cwd('./node-webkit.app/Contents/Resources/app.nw');
}

// -------------------------------------
// Tasks
// -------------------------------------

gulp.task('clean', function(callback) {
    return destDir.dirAsync('.', { empty: true });
});

gulp.task('prepare-runtime', ['clean'] , function() {
    var runtimeForThisOs = './nw/' + utils.os();
    return projectDir.copyAsync(runtimeForThisOs, destDir.path(), {
        overwrite: true,
        allBut: [
            'version',
            'nwsnapshot*',
            'credits.html'
        ]
    });
});

gulp.task('copy', ['prepare-runtime'], function() {
    return projectDir.copyAsync('app', destForCodeDir.path(), {
        overwrite: true,
        only: [
            'app/node_modules',
            'app/vendor',
            '*.html'
        ]
    });
});

// Add and customize OS-specyfic and target-specyfic stuff.
gulp.task('finalize', ['prepare-runtime'], function() {
    var manifest = srcDir.read('package.json', 'json');
    switch (utils.getBuildTarget()) {
        case 'release':
            // Hide dev toolbar if doing a release.
            manifest.window.toolbar = false;
            break;
        case 'test':
            // Add "-test" suffix to name, so node-webkit will write all
            // data like cookies and locaStorage into separate place.
            manifest.name += '-test';
            // Change the main entry to spec runner.
            manifest.main = 'spec.html';
            // Set extra flag so we know this is development mode, and we can
            // alter some behaviours of running app.
            manifest.developmentMode = true;
            break;
        case 'development':
            // Add "-dev" suffix to name, so node-webkit will write all
            // data like cookies and locaStorage into separate place.
            manifest.name += '-dev';
            // Set extra flag so we know this is development mode, and we can
            // alter some behaviours of running app.
            manifest.developmentMode = true;
            break;
    }
    destForCodeDir.write('package.json', manifest, { jsonIndent: 4 });

    projectDir.copy('os/icon.png', destForCodeDir.path('icon.png'));

    // Stuff specyfic for certains OS
    switch (utils.os()) {
        case 'windows':
            // icon
            projectDir.copy('os/windows/icon.ico', destDir.path('icon.ico'));
            break;
        case 'osx':
            // Info.plist
            var manifest = projectDir.read('app/package.json', 'json');
            var info = projectDir.read('os/osx/Info.plist');
            info = utils.replace(info, {
                prettyName: manifest.prettyName,
                version: manifest.version
            });
            destDir.write('node-webkit.app/Contents/Info.plist', info);
            // icon
            projectDir.copy('os/osx/icon.icns', destDir.path('node-webkit.app/Contents/Resources/icon.icns'));
            break;
    }
});

var transpileTask = function() {
    return gulp.src([
        'app/**/*.js',
        '!app/node_modules/**',
        '!app/vendor/**'
    ], {
        read: false // Don't read the files. ES6 transpiler will do it.
    })
    .pipe(through.obj(function (file, enc, callback) {
        var relPath = file.path.substring(file.base.length);
        var container = new Container({
            resolvers: [new FileResolver([file.base])],
            formatter: new AmdFormatter()
        });
        try {
            container.getModule(relPath);
            container.write(destForCodeDir.path(relPath));
            callback();
        } catch (err) {
            // Show to the user precise file where transpilation error occured.
            callback(relPath + " " + err.message);
        }
    }));
};
gulp.task('transpile', ['prepare-runtime'], transpileTask);
gulp.task('transpile-watch', transpileTask);

var lessTask = function () {
    return gulp.src('app/stylesheets/main.less')
    .pipe(less())
    .pipe(gulp.dest(destForCodeDir.path('stylesheets')));
};
gulp.task('less', ['prepare-runtime'], lessTask);
gulp.task('less-watch', lessTask);

gulp.task('watch', function () {
    gulp.watch('app/stylesheets/**', ['less-watch']);
    gulp.watch('app/**/*.js', ['transpile-watch']);
});

gulp.task('build', ['copy', 'finalize', 'transpile', 'less']);
<|endoftext|>