<commit_before>goog.provide('ol.Projection');

goog.require('goog.array');
goog.require('goog.asserts');
goog.require('goog.math.Coordinate');
goog.require('goog.object');
goog.require('ol.Extent');
goog.require('ol.TransformFunction');

/**
 * @enum {string}
 */
ol.ProjectionUnits = {
  DEGREES: 'degrees',
  METERS: 'm'
};

/**
 * @constructor
 * @param {string} code Code.
 * @param {ol.ProjectionUnits} units Units.
 * @param {ol.Extent} extent Extent.
 */
ol.Projection = function(code, units, extent) {

  /**
   * @private
   * @type {string}
   */
  this.code_ = code;

  /**
   * @private
   * @type {ol.ProjectionUnits}
   */
  this.units_ = units;

  /**
   * @private
   * @type {ol.Extent}
   */
  this.extent_ = extent;

};

/**
 * @return {string} Code.
 */
ol.Projection.prototype.getCode = function() {
  return this.code_;
};

/**
 * @return {ol.Extent} Extent.
 */
ol.Projection.prototype.getExtent = function() {
  return this.extent_;
};

/**
 * @return {ol.ProjectionUnits} Units.
 */
ol.Projection.prototype.getUnits = function() {
  return this.units_;
};

/**
 * @private
 * @type {Object.<string, ol.Projection>}
 */
ol.Projection.projections_ = {};

/**
 * @private
 * @type {Object.<string, Object.<string, ol.TransformFunction>>}
 */
ol.Projection.transforms_ = {};

/**
 * @param {Array.<ol.Projection>} projections Projections.
 */
ol.Projection.addEquivalentProjections = function(projections) {
  ol.Projection.addProjections(projections);
  goog.array.forEach(projections, function(source) {
    goog.array.forEach(projections, function(destination) {
      ol.Projection.addTransform(
          source, destination, ol.Projection.identityTransform);
    });
  });
};

/**
 * @param {Array.<ol.Projection>} projections1 Projections.
 * @param {Array.<ol.Projection>} projections2 Projections.
 * @param {ol.TransformFunction} forwardTransform Forward transform.
 * @param {ol.TransformFunction} inverseTransform Inverse transform.
 */
ol.Projection.addEquivalentTransforms =
    function(projections1, projections2, forwardTransform, inverseTransform) {
  goog.array.forEach(projections1, function(projection1) {
    goog.array.forEach(projections2, function(projection2) {
      ol.Projection.addTransform(projection1, projection2, forwardTransform);
      ol.Projection.addTransform(projection2, projection1, inverseTransform);
    });
  });
};

/**
 * @param {ol.Projection} projection Projection.
 */
ol.Projection.addProjection = function(projection) {
  var projections = ol.Projection.projections_;
  var code = projection.getCode();
  goog.asserts.assert(!goog.object.containsKey(projections, code));
  projections[code] = projection;
};

/**
 * @param {Array.<ol.Projection>} projections Projections.
 */
ol.Projection.addProjections = function(projections) {
  goog.array.forEach(projections, function(projection) {
    ol.Projection.addProjection(projection);
  });
};

/**
 * @param {ol.Projection} source Source.
 * @param {ol.Projection} destination Destination.
 * @param {ol.TransformFunction} transform Transform.
 */
ol.Projection.addTransform = function(source, destination, transform) {
  var projections = ol.Projection.projections_;
  var sourceCode = source.getCode();
  goog.asserts.assert(goog.object.containsKey(projections, sourceCode));
  var destinationCode = destination.getCode();
  goog.asserts.assert(goog.object.containsKey(projections, destinationCode));
  var transforms = ol.Projection.transforms_;
  if (!goog.object.containsKey(transforms, sourceCode)) {
    transforms[sourceCode] = {};
  }
  goog.asserts.assert(
      !goog.object.containsKey(transforms[sourceCode], destinationCode));
  transforms[sourceCode][destinationCode] = transform;
};

/**
 * @param {string} code Code.
 * @return {ol.Projection} Projection.
 */
ol.Projection.createFromCode = function(code) {
  var projections = ol.Projection.projections_;
  goog.asserts.assert(goog.object.containsKey(projections, code));
  return projections[code];
};

/**
 * @param {ol.Projection} projection1 Projection 1.
 * @param {ol.Projection} projection2 Projection 2.
 * @return {boolean} Equivalent.
 */
ol.Projection.equivalent = function(projection1, projection2) {
  if (projection1.getUnits() != projection2.getUnits()) {
    return false;
  } else {
    var transform = ol.Projection.getTransform(projection1, projection2);
    return transform === ol.Projection.identityTransform;
  }
};

/**
 * @param {ol.Projection} source Source.
 * @param {ol.Projection} destination Destination.
 * @return {ol.TransformFunction} Transform.
 */
ol.Projection.getTransform = function(source, destination) {
  var transforms = ol.Projection.transforms_;
  var sourceCode = source.getCode();
  var destinationCode = destination.getCode();
  goog.asserts.assert(goog.object.containsKey(transforms, sourceCode));
  goog.asserts.assert(
      goog.object.containsKey(transforms[sourceCode], destinationCode));
  return transforms[sourceCode][destinationCode];
};

/**
 * @param {string} sourceCode Source code.
 * @param {string} destinationCode Destination code.
 * @return {ol.TransformFunction} Transform.
 */
ol.Projection.getTransformFromCodes = function(sourceCode, destinationCode) {
  var source = ol.Projection.createFromCode(sourceCode);
  var destination = ol.Projection.createFromCode(destinationCode);
  return ol.Projection.getTransform(source, destination);
};

/**
 * @param {goog.math.Coordinate} point Point.
 * @return {goog.math.Coordinate} Point.
 */
ol.Projection.identityTransform = function(point) {
  return point.clone();
};

/**
 * @param {goog.math.Coordinate} point Point.
 * @param {ol.Projection} source Source.
 * @param {ol.Projection} destination Destination.
 * @return {goog.math.Coordinate} Point.
 */
ol.Projection.transform = function(point, source, destination) {
  var transform = ol.Projection.getTransform(source, destination);
  return transform(point);
};

/**
 * @param {goog.math.Coordinate} point Point.
 * @param {string} sourceCode Source code.
 * @param {string} destinationCode Destination code.
 * @return {goog.math.Coordinate} Point.
 */
ol.Projection.transformWithCodes =
    function(point, sourceCode, destinationCode) {
  var transform = ol.Projection.getTransformFromCodes(
      sourceCode, destinationCode);
  return transform(point);
};

/**
 * @const
 * @type {number}
 */
ol.Projection.EPSG_3857_RADIUS = 6378137;

/**
 * @param {goog.math.Coordinate} point Point.
 * @return {goog.math.Coordinate} Point.
 */
ol.Projection.forwardSphericalMercator = function(point) {
  var x = ol.Projection.EPSG_3857_RADIUS * Math.PI * point.x / 180;
  var y = ol.Projection.EPSG_3857_RADIUS *
      Math.log(Math.tan(Math.PI * (point.y + 90) / 360));
  return new goog.math.Coordinate(x, y);
};

/**
 * @param {goog.math.Coordinate} point Point.
 * @return {goog.math.Coordinate} Point.
 */
ol.Projection.inverseSphericalMercator = function(point) {
  var x = 180 * point.x / (ol.Projection.EPSG_3857_RADIUS * Math.PI);
  var y = 360 * Math.atan(
      Math.exp(point.y / ol.Projection.EPSG_3857_RADIUS)) / Math.PI - 90;
  return new goog.math.Coordinate(x, y);
};

/**
 * @const
 * @type {number}
 */
ol.Projection.EPSG_3857_HALF_SIZE = Math.PI * ol.Projection.EPSG_3857_RADIUS;

/**
 * @const
 * @type {ol.Extent}
 */
ol.Projection.EPSG_3857_EXTENT = new ol.Extent(
    ol.Projection.EPSG_3857_HALF_SIZE,
    ol.Projection.EPSG_3857_HALF_SIZE,
    -ol.Projection.EPSG_3857_HALF_SIZE,
    -ol.Projection.EPSG_3857_HALF_SIZE);

/**
 * @type {Array.<string>}
 */
ol.Projection.EPSG_3857_LIKE_CODES = [
  'EPSG:3857',
  'EPSG:102113',
  'EPSG:102100',
  'EPSG:900913'
];

/**
 * @const
 * @type {Array.<ol.Projection>}
 */
ol.Projection.EPSG_3857_LIKE_PROJECTIONS = goog.array.map(
    ol.Projection.EPSG_3857_LIKE_CODES,
    function(code) {
      return new ol.Projection(
          code,
          ol.ProjectionUnits.METERS,
          ol.Projection.EPSG_3857_EXTENT);
    });

/**
 * @const
 * @type {ol.Extent}
 */
ol.Projection.EPSG_4326_EXTENT = new ol.Extent(180, 90, -180, -90);

/**
 * @type {Array.<string>}
 */
ol.Projection.EPSG_4326_LIKE_CODES = [
  'EPSG:4326',
  'CRS:84',
  'urn:ogc:def:crs:EPSG:6.6:4326'
];

/**
 * @const
 * @type {Array.<ol.Projection>}
 */
ol.Projection.EPSG_4326_LIKE_PROJECTIONS = goog.array.map(
    ol.Projection.EPSG_4326_LIKE_CODES,
    function(code) {
      return new ol.Projection(
          code,
          ol.ProjectionUnits.DEGREES,
          ol.Projection.EPSG_4326_EXTENT);
    });

ol.Projection.addEquivalentProjections(
    ol.Projection.EPSG_3857_LIKE_PROJECTIONS);
ol.Projection.addEquivalentProjections(
    ol.Projection.EPSG_4326_LIKE_PROJECTIONS);
ol.Projection.addEquivalentTransforms(
    ol.Projection.EPSG_4326_LIKE_PROJECTIONS,
    ol.Projection.EPSG_3857_LIKE_PROJECTIONS,
    ol.Projection.forwardSphericalMercator,
    ol.Projection.inverseSphericalMercator);
<commit_msg>Shortcut projection equivalence test<commit_after>goog.provide('ol.Projection');

goog.require('goog.array');
goog.require('goog.asserts');
goog.require('goog.math.Coordinate');
goog.require('goog.object');
goog.require('ol.Extent');
goog.require('ol.TransformFunction');

/**
 * @enum {string}
 */
ol.ProjectionUnits = {
  DEGREES: 'degrees',
  METERS: 'm'
};

/**
 * @constructor
 * @param {string} code Code.
 * @param {ol.ProjectionUnits} units Units.
 * @param {ol.Extent} extent Extent.
 */
ol.Projection = function(code, units, extent) {

  /**
   * @private
   * @type {string}
   */
  this.code_ = code;

  /**
   * @private
   * @type {ol.ProjectionUnits}
   */
  this.units_ = units;

  /**
   * @private
   * @type {ol.Extent}
   */
  this.extent_ = extent;

};

/**
 * @return {string} Code.
 */
ol.Projection.prototype.getCode = function() {
  return this.code_;
};

/**
 * @return {ol.Extent} Extent.
 */
ol.Projection.prototype.getExtent = function() {
  return this.extent_;
};

/**
 * @return {ol.ProjectionUnits} Units.
 */
ol.Projection.prototype.getUnits = function() {
  return this.units_;
};

/**
 * @private
 * @type {Object.<string, ol.Projection>}
 */
ol.Projection.projections_ = {};

/**
 * @private
 * @type {Object.<string, Object.<string, ol.TransformFunction>>}
 */
ol.Projection.transforms_ = {};

/**
 * @param {Array.<ol.Projection>} projections Projections.
 */
ol.Projection.addEquivalentProjections = function(projections) {
  ol.Projection.addProjections(projections);
  goog.array.forEach(projections, function(source) {
    goog.array.forEach(projections, function(destination) {
      ol.Projection.addTransform(
          source, destination, ol.Projection.identityTransform);
    });
  });
};

/**
 * @param {Array.<ol.Projection>} projections1 Projections.
 * @param {Array.<ol.Projection>} projections2 Projections.
 * @param {ol.TransformFunction} forwardTransform Forward transform.
 * @param {ol.TransformFunction} inverseTransform Inverse transform.
 */
ol.Projection.addEquivalentTransforms =
    function(projections1, projections2, forwardTransform, inverseTransform) {
  goog.array.forEach(projections1, function(projection1) {
    goog.array.forEach(projections2, function(projection2) {
      ol.Projection.addTransform(projection1, projection2, forwardTransform);
      ol.Projection.addTransform(projection2, projection1, inverseTransform);
    });
  });
};

/**
 * @param {ol.Projection} projection Projection.
 */
ol.Projection.addProjection = function(projection) {
  var projections = ol.Projection.projections_;
  var code = projection.getCode();
  goog.asserts.assert(!goog.object.containsKey(projections, code));
  projections[code] = projection;
};

/**
 * @param {Array.<ol.Projection>} projections Projections.
 */
ol.Projection.addProjections = function(projections) {
  goog.array.forEach(projections, function(projection) {
    ol.Projection.addProjection(projection);
  });
};

/**
 * @param {ol.Projection} source Source.
 * @param {ol.Projection} destination Destination.
 * @param {ol.TransformFunction} transform Transform.
 */
ol.Projection.addTransform = function(source, destination, transform) {
  var projections = ol.Projection.projections_;
  var sourceCode = source.getCode();
  goog.asserts.assert(goog.object.containsKey(projections, sourceCode));
  var destinationCode = destination.getCode();
  goog.asserts.assert(goog.object.containsKey(projections, destinationCode));
  var transforms = ol.Projection.transforms_;
  if (!goog.object.containsKey(transforms, sourceCode)) {
    transforms[sourceCode] = {};
  }
  goog.asserts.assert(
      !goog.object.containsKey(transforms[sourceCode], destinationCode));
  transforms[sourceCode][destinationCode] = transform;
};

/**
 * @param {string} code Code.
 * @return {ol.Projection} Projection.
 */
ol.Projection.createFromCode = function(code) {
  var projections = ol.Projection.projections_;
  goog.asserts.assert(goog.object.containsKey(projections, code));
  return projections[code];
};

/**
 * @param {ol.Projection} projection1 Projection 1.
 * @param {ol.Projection} projection2 Projection 2.
 * @return {boolean} Equivalent.
 */
ol.Projection.equivalent = function(projection1, projection2) {
  if (projection1 === projection2) {
    return true;
  } else if (projection1.getUnits() != projection2.getUnits()) {
    return false;
  } else {
    var transform = ol.Projection.getTransform(projection1, projection2);
    return transform === ol.Projection.identityTransform;
  }
};

/**
 * @param {ol.Projection} source Source.
 * @param {ol.Projection} destination Destination.
 * @return {ol.TransformFunction} Transform.
 */
ol.Projection.getTransform = function(source, destination) {
  var transforms = ol.Projection.transforms_;
  var sourceCode = source.getCode();
  var destinationCode = destination.getCode();
  goog.asserts.assert(goog.object.containsKey(transforms, sourceCode));
  goog.asserts.assert(
      goog.object.containsKey(transforms[sourceCode], destinationCode));
  return transforms[sourceCode][destinationCode];
};

/**
 * @param {string} sourceCode Source code.
 * @param {string} destinationCode Destination code.
 * @return {ol.TransformFunction} Transform.
 */
ol.Projection.getTransformFromCodes = function(sourceCode, destinationCode) {
  var source = ol.Projection.createFromCode(sourceCode);
  var destination = ol.Projection.createFromCode(destinationCode);
  return ol.Projection.getTransform(source, destination);
};

/**
 * @param {goog.math.Coordinate} point Point.
 * @return {goog.math.Coordinate} Point.
 */
ol.Projection.identityTransform = function(point) {
  return point.clone();
};

/**
 * @param {goog.math.Coordinate} point Point.
 * @param {ol.Projection} source Source.
 * @param {ol.Projection} destination Destination.
 * @return {goog.math.Coordinate} Point.
 */
ol.Projection.transform = function(point, source, destination) {
  var transform = ol.Projection.getTransform(source, destination);
  return transform(point);
};

/**
 * @param {goog.math.Coordinate} point Point.
 * @param {string} sourceCode Source code.
 * @param {string} destinationCode Destination code.
 * @return {goog.math.Coordinate} Point.
 */
ol.Projection.transformWithCodes =
    function(point, sourceCode, destinationCode) {
  var transform = ol.Projection.getTransformFromCodes(
      sourceCode, destinationCode);
  return transform(point);
};

/**
 * @const
 * @type {number}
 */
ol.Projection.EPSG_3857_RADIUS = 6378137;

/**
 * @param {goog.math.Coordinate} point Point.
 * @return {goog.math.Coordinate} Point.
 */
ol.Projection.forwardSphericalMercator = function(point) {
  var x = ol.Projection.EPSG_3857_RADIUS * Math.PI * point.x / 180;
  var y = ol.Projection.EPSG_3857_RADIUS *
      Math.log(Math.tan(Math.PI * (point.y + 90) / 360));
  return new goog.math.Coordinate(x, y);
};

/**
 * @param {goog.math.Coordinate} point Point.
 * @return {goog.math.Coordinate} Point.
 */
ol.Projection.inverseSphericalMercator = function(point) {
  var x = 180 * point.x / (ol.Projection.EPSG_3857_RADIUS * Math.PI);
  var y = 360 * Math.atan(
      Math.exp(point.y / ol.Projection.EPSG_3857_RADIUS)) / Math.PI - 90;
  return new goog.math.Coordinate(x, y);
};

/**
 * @const
 * @type {number}
 */
ol.Projection.EPSG_3857_HALF_SIZE = Math.PI * ol.Projection.EPSG_3857_RADIUS;

/**
 * @const
 * @type {ol.Extent}
 */
ol.Projection.EPSG_3857_EXTENT = new ol.Extent(
    ol.Projection.EPSG_3857_HALF_SIZE,
    ol.Projection.EPSG_3857_HALF_SIZE,
    -ol.Projection.EPSG_3857_HALF_SIZE,
    -ol.Projection.EPSG_3857_HALF_SIZE);

/**
 * @type {Array.<string>}
 */
ol.Projection.EPSG_3857_LIKE_CODES = [
  'EPSG:3857',
  'EPSG:102113',
  'EPSG:102100',
  'EPSG:900913'
];

/**
 * @const
 * @type {Array.<ol.Projection>}
 */
ol.Projection.EPSG_3857_LIKE_PROJECTIONS = goog.array.map(
    ol.Projection.EPSG_3857_LIKE_CODES,
    function(code) {
      return new ol.Projection(
          code,
          ol.ProjectionUnits.METERS,
          ol.Projection.EPSG_3857_EXTENT);
    });

/**
 * @const
 * @type {ol.Extent}
 */
ol.Projection.EPSG_4326_EXTENT = new ol.Extent(180, 90, -180, -90);

/**
 * @type {Array.<string>}
 */
ol.Projection.EPSG_4326_LIKE_CODES = [
  'EPSG:4326',
  'CRS:84',
  'urn:ogc:def:crs:EPSG:6.6:4326'
];

/**
 * @const
 * @type {Array.<ol.Projection>}
 */
ol.Projection.EPSG_4326_LIKE_PROJECTIONS = goog.array.map(
    ol.Projection.EPSG_4326_LIKE_CODES,
    function(code) {
      return new ol.Projection(
          code,
          ol.ProjectionUnits.DEGREES,
          ol.Projection.EPSG_4326_EXTENT);
    });

ol.Projection.addEquivalentProjections(
    ol.Projection.EPSG_3857_LIKE_PROJECTIONS);
ol.Projection.addEquivalentProjections(
    ol.Projection.EPSG_4326_LIKE_PROJECTIONS);
ol.Projection.addEquivalentTransforms(
    ol.Projection.EPSG_4326_LIKE_PROJECTIONS,
    ol.Projection.EPSG_3857_LIKE_PROJECTIONS,
    ol.Projection.forwardSphericalMercator,
    ol.Projection.inverseSphericalMercator);
<|endoftext|>


<commit_before>import { existsSync } from 'fs';
import { readFileSync } from 'fs';

function packageParser( metadata ) {
	if ( typeof metadata === 'string' ) {
		metadata = loadAndParseFile( metadata );
	} else if ( typeof metadata !== 'object' ) {
		throw new TypeError( 'Provide string or object.' );
	}

	lintObject( metadata );

	return prepareMetadata( metadata );
}

function loadAndParseFile( path ) {
	if ( !existsSync( path ) ) {
		throw new ReferenceError( 'File with given path does not exist.' );
	}

	const contents = readFileSync( path ).toString();
	let parsed;

	try {
		parsed = JSON.parse( contents );
	} catch ( e ) {
		throw new SyntaxError( 'Given file is not parsable as a correct JSON.' );
	}

	return parsed;
}

function lintObject( obj ) {
	function checkProperty( name ) {
		if ( typeof obj[ name ] === 'undefined' ) {
			throw new ReferenceError( `Package metadata must contain "${ name }" property.` );
		}
	}

	function checkProperties( name1, name2 ) {
		if ( typeof obj[ name1 ] === 'undefined' && typeof obj[ name2 ] === 'undefined' ) {
			throw new ReferenceError( `Package metadata must contain either "${ name1 }" or "${ name2 }" or both properties.` );
		}
	}

	checkProperty( 'name' );
	checkProperty( 'version' );
	checkProperty( 'main' );
	checkProperties( 'module', 'jsnext:main' );
	checkProperty( 'author' );
	checkProperty( 'license' );
}

function prepareAuthorMetadata( author ) {
	if ( typeof author !== 'object' ) {
		return String( author );
	}

	return author.name;
}

function prepareMetadata( obj ) {
	return {
		name: obj.name,
		version: obj.version,
		author: prepareAuthorMetadata( obj.author ),
		license: obj.license,
		src: 'src/index.js',
		dist: {
			esm: obj.module || obj[ 'jsnext:main' ],
			cjs: obj.main
		}
	};
}

export default packageParser;
<commit_msg>fix(parser): set encoding<commit_after>import { existsSync } from 'fs';
import { readFileSync } from 'fs';

function packageParser( metadata ) {
	if ( typeof metadata === 'string' ) {
		metadata = loadAndParseFile( metadata );
	} else if ( typeof metadata !== 'object' ) {
		throw new TypeError( 'Provide string or object.' );
	}

	lintObject( metadata );

	return prepareMetadata( metadata );
}

function loadAndParseFile( path ) {
	if ( !existsSync( path ) ) {
		throw new ReferenceError( 'File with given path does not exist.' );
	}

	const contents = readFileSync( path, 'utf8' );
	let parsed;

	try {
		parsed = JSON.parse( contents );
	} catch ( e ) {
		throw new SyntaxError( 'Given file is not parsable as a correct JSON.' );
	}

	return parsed;
}

function lintObject( obj ) {
	function checkProperty( name ) {
		if ( typeof obj[ name ] === 'undefined' ) {
			throw new ReferenceError( `Package metadata must contain "${ name }" property.` );
		}
	}

	function checkProperties( name1, name2 ) {
		if ( typeof obj[ name1 ] === 'undefined' && typeof obj[ name2 ] === 'undefined' ) {
			throw new ReferenceError( `Package metadata must contain either "${ name1 }" or "${ name2 }" or both properties.` );
		}
	}

	checkProperty( 'name' );
	checkProperty( 'version' );
	checkProperty( 'main' );
	checkProperties( 'module', 'jsnext:main' );
	checkProperty( 'author' );
	checkProperty( 'license' );
}

function prepareAuthorMetadata( author ) {
	if ( typeof author !== 'object' ) {
		return String( author );
	}

	return author.name;
}

function prepareMetadata( obj ) {
	return {
		name: obj.name,
		version: obj.version,
		author: prepareAuthorMetadata( obj.author ),
		license: obj.license,
		src: 'src/index.js',
		dist: {
			esm: obj.module || obj[ 'jsnext:main' ],
			cjs: obj.main
		}
	};
}

export default packageParser;
<|endoftext|>


<commit_before>import React, { Component } from 'react'

import { callPlayer, getSDK } from '../utils'
import createSinglePlayer from '../singlePlayer'

const SDK_URL = 'https://player.vimeo.com/api/player.js'
const SDK_GLOBAL = 'Vimeo'
const MATCH_URL = /vimeo\.com\/.+/
const MATCH_FILE_URL = /vimeo\.com\/external\/.+\.mp4/

export class Vimeo extends Component {
  static displayName = 'Vimeo'
  static canPlay = url => {
    if (MATCH_FILE_URL.test(url)) {
      return false
    }
    return MATCH_URL.test(url)
  }

  callPlayer = callPlayer
  duration = null
  currentTime = null
  secondsLoaded = null
  load (url) {
    this.duration = null
    getSDK(SDK_URL, SDK_GLOBAL).then(Vimeo => {
      if (!this.container) return
      this.player = new Vimeo.Player(this.container, {
        ...this.props.config.vimeo.playerOptions,
        url,
        autoplay: this.props.playing,
        muted: this.props.muted,
        loop: this.props.loop
      })
      this.player.ready().then(() => {
        const iframe = this.container.querySelector('iframe')
        iframe.style.width = '100%'
        iframe.style.height = '100%'
      }).catch(this.props.onError)
      this.player.on('loaded', () => {
        this.props.onReady()
        this.player.getDuration().then(duration => {
          this.duration = duration
        })
      })
      this.player.on('play', this.props.onPlay)
      this.player.on('pause', this.props.onPause)
      this.player.on('seeked', e => this.props.onSeek(e.seconds))
      this.player.on('ended', this.props.onEnded)
      this.player.on('error', this.props.onError)
      this.player.on('timeupdate', ({ seconds }) => {
        this.currentTime = seconds
      })
      this.player.on('progress', ({ seconds }) => {
        this.secondsLoaded = seconds
      })
    }, this.props.onError)
  }
  play () {
    this.callPlayer('play')
  }
  pause () {
    this.callPlayer('pause')
  }
  stop () {
    this.callPlayer('unload')
  }
  seekTo (seconds) {
    this.callPlayer('setCurrentTime', seconds)
  }
  setVolume (fraction) {
    this.callPlayer('setVolume', fraction)
  }
  mute = () => {
    this.setVolume(0)
  }
  unmute = () => {
    if (this.props.volume !== null) {
      this.setVolume(this.props.volume)
    }
  }
  getDuration () {
    return this.duration
  }
  getCurrentTime () {
    return this.currentTime
  }
  getSecondsLoaded () {
    return this.secondsLoaded
  }
  ref = container => {
    this.container = container
  }
  render () {
    const style = {
      width: '100%',
      height: '100%',
      overflow: 'hidden',
      backgroundColor: 'black',
      ...this.props.style
    }
    return (
      <div
        key={this.props.url}
        ref={this.ref}
        style={style}
      />
    )
  }
}

export default createSinglePlayer(Vimeo)
<commit_msg>Refresh vimeo duration on play<commit_after>import React, { Component } from 'react'

import { callPlayer, getSDK } from '../utils'
import createSinglePlayer from '../singlePlayer'

const SDK_URL = 'https://player.vimeo.com/api/player.js'
const SDK_GLOBAL = 'Vimeo'
const MATCH_URL = /vimeo\.com\/.+/
const MATCH_FILE_URL = /vimeo\.com\/external\/.+\.mp4/

export class Vimeo extends Component {
  static displayName = 'Vimeo'
  static canPlay = url => {
    if (MATCH_FILE_URL.test(url)) {
      return false
    }
    return MATCH_URL.test(url)
  }

  callPlayer = callPlayer
  duration = null
  currentTime = null
  secondsLoaded = null
  load (url) {
    this.duration = null
    getSDK(SDK_URL, SDK_GLOBAL).then(Vimeo => {
      if (!this.container) return
      this.player = new Vimeo.Player(this.container, {
        ...this.props.config.vimeo.playerOptions,
        url,
        autoplay: this.props.playing,
        muted: this.props.muted,
        loop: this.props.loop
      })
      this.player.ready().then(() => {
        const iframe = this.container.querySelector('iframe')
        iframe.style.width = '100%'
        iframe.style.height = '100%'
      }).catch(this.props.onError)
      this.player.on('loaded', () => {
        this.props.onReady()
        this.refreshDuration()
      })
      this.player.on('play', () => {
        this.props.onPlay()
        this.refreshDuration()
      })
      this.player.on('pause', this.props.onPause)
      this.player.on('seeked', e => this.props.onSeek(e.seconds))
      this.player.on('ended', this.props.onEnded)
      this.player.on('error', this.props.onError)
      this.player.on('timeupdate', ({ seconds }) => {
        this.currentTime = seconds
      })
      this.player.on('progress', ({ seconds }) => {
        this.secondsLoaded = seconds
      })
    }, this.props.onError)
  }
  refreshDuration () {
    this.player.getDuration().then(duration => {
      this.duration = duration
    })
  }
  play () {
    this.callPlayer('play')
  }
  pause () {
    this.callPlayer('pause')
  }
  stop () {
    this.callPlayer('unload')
  }
  seekTo (seconds) {
    this.callPlayer('setCurrentTime', seconds)
  }
  setVolume (fraction) {
    this.callPlayer('setVolume', fraction)
  }
  mute = () => {
    this.setVolume(0)
  }
  unmute = () => {
    if (this.props.volume !== null) {
      this.setVolume(this.props.volume)
    }
  }
  getDuration () {
    return this.duration
  }
  getCurrentTime () {
    return this.currentTime
  }
  getSecondsLoaded () {
    return this.secondsLoaded
  }
  ref = container => {
    this.container = container
  }
  render () {
    const style = {
      width: '100%',
      height: '100%',
      overflow: 'hidden',
      backgroundColor: 'black',
      ...this.props.style
    }
    return (
      <div
        key={this.props.url}
        ref={this.ref}
        style={style}
      />
    )
  }
}

export default createSinglePlayer(Vimeo)
<|endoftext|>


<commit_before>import Plugin from './Plugin'
import tus from 'tus-js-client'
import UppySocket from '../core/UppySocket'

/**
 * Tus resumable file uploader
 *
 */
export default class Tus10 extends Plugin {
  constructor (core, opts) {
    super(core, opts)
    this.type = 'uploader'
    this.id = 'Tus'
    this.title = 'Tus'

    // set default options
    const defaultOptions = {
      resume: true,
      allowPause: true
    }

    // merge default options with the ones set by user
    this.opts = Object.assign({}, defaultOptions, opts)
  }

  pauseResume (action, fileID) {
    const updatedFiles = Object.assign({}, this.core.getState().files)
    const inProgressUpdatedFiles = Object.keys(updatedFiles).filter((file) => {
      return !updatedFiles[file].progress.uploadComplete &&
             updatedFiles[file].progress.uploadStarted
    })

    switch (action) {
      case 'toggle':
        if (updatedFiles[fileID].uploadComplete) return

        const wasPaused = updatedFiles[fileID].isPaused || false
        const isPaused = !wasPaused
        let updatedFile
        if (wasPaused) {
          updatedFile = Object.assign({}, updatedFiles[fileID], {
            isPaused: false
          })
        } else {
          updatedFile = Object.assign({}, updatedFiles[fileID], {
            isPaused: true
          })
        }
        updatedFiles[fileID] = updatedFile
        this.core.setState({files: updatedFiles})
        return isPaused
      case 'pauseAll':
        inProgressUpdatedFiles.forEach((file) => {
          const updatedFile = Object.assign({}, updatedFiles[file], {
            isPaused: true
          })
          updatedFiles[file] = updatedFile
        })
        this.core.setState({files: updatedFiles})
        return
      case 'resumeAll':
        inProgressUpdatedFiles.forEach((file) => {
          const updatedFile = Object.assign({}, updatedFiles[file], {
            isPaused: false
          })
          updatedFiles[file] = updatedFile
        })
        this.core.setState({files: updatedFiles})
        return
    }
  }

/**
 * Create a new Tus upload
 *
 * @param {object} file for use with upload
 * @param {integer} current file in a queue
 * @param {integer} total number of files in a queue
 * @returns {Promise}
 */
  upload (file, current, total) {
    this.core.log(`uploading ${current} of ${total}`)

    // Create a new tus upload
    return new Promise((resolve, reject) => {
      const upload = new tus.Upload(file.data, {

        // TODO merge this.opts or this.opts.tus here
        metadata: file.meta,
        resume: this.opts.resume,
        endpoint: this.opts.endpoint,

        onError: (err) => {
          this.core.log(err)
          this.core.emitter.emit('core:upload-error', file.id)
          reject('Failed because: ' + err)
        },
        onProgress: (bytesUploaded, bytesTotal) => {
          // Dispatch progress event
          console.log(bytesUploaded, bytesTotal)
          this.core.emitter.emit('core:upload-progress', {
            uploader: this,
            id: file.id,
            bytesUploaded: bytesUploaded,
            bytesTotal: bytesTotal
          })
        },
        onSuccess: () => {
          this.core.emitter.emit('core:upload-success', file.id, upload.url)

          this.core.log(`Download ${upload.file.name} from ${upload.url}`)
          resolve(upload)
        }
      })

      this.core.emitter.on('core:file-remove', (fileID) => {
        if (fileID === file.id) {
          console.log('removing file: ', fileID)
          upload.abort()
          resolve(`upload ${fileID} was removed`)
        }
      })

      this.core.emitter.on('core:upload-pause', (fileID) => {
        if (fileID === file.id) {
          const isPaused = this.pauseResume('toggle', fileID)
          isPaused ? upload.abort() : upload.start()
        }
      })

      this.core.emitter.on('core:pause-all', () => {
        const files = this.core.getState().files
        if (!files[file.id]) return
        upload.abort()
      })

      this.core.emitter.on('core:resume-all', () => {
        const files = this.core.getState().files
        if (!files[file.id]) return
        upload.start()
      })

      upload.start()
      this.core.emitter.emit('core:upload-started', file.id, upload)
    })
  }

  uploadRemote (file, current, total) {
    return new Promise((resolve, reject) => {
      this.core.log(file.remote.url)
      fetch(file.remote.url, {
        method: 'post',
        credentials: 'include',
        headers: {
          'Accept': 'application/json',
          'Content-Type': 'application/json'
        },
        body: JSON.stringify(Object.assign({}, file.remote.body, {
          endpoint: this.opts.endpoint,
          protocol: 'tus'
        }))
      })
      .then((res) => {
        if (res.status < 200 && res.status > 300) {
          return reject(res.statusText)
        }

        res.json()
        .then((data) => {
          // get the host domain
          var regex = /^(?:https?:\/\/|\/\/)?(?:[^@\/\n]+@)?(?:www\.)?([^\/\n]+)/
          var host = regex.exec(file.remote.host)[1]
          var socketProtocol = location.protocol === 'https:' ? 'wss' : 'ws'

          var token = data.token
          var socket = new UppySocket({
            target: socketProtocol + `://${host}/api/${token}`
          })

          socket.on('progress', (progressData) => {
            const {progress, bytesUploaded, bytesTotal} = progressData

            if (progress) {
              this.core.log(`Upload progress: ${progress}`)

              // Dispatch progress event
              this.core.emitter.emit('core:upload-progress', {
                uploader: this,
                id: file.id,
                bytesUploaded: bytesUploaded,
                bytesTotal: bytesTotal
              })

              if (progress === '100.00') {
                this.core.emitter.emit('core:upload-success', file.id)
                socket.close()
                return resolve()
              }
            }
          })
        })
      })
    })
  }

  uploadFiles (files) {
    if (Object.keys(files).length === 0) {
      this.core.log('no files to upload!')
      return
    }

    const uploaders = []
    files.forEach((file, index) => {
      const current = parseInt(index, 10) + 1
      const total = files.length

      if (!file.isRemote) {
        uploaders.push(this.upload(file, current, total))
      } else {
        uploaders.push(this.uploadRemote(file, current, total))
      }
    })

    return Promise.all(uploaders)
      .then(() => {
        this.core.log('All files uploaded')
        return { uploadedCount: files.length }
      })
      .catch((err) => {
        this.core.log('Upload error: ' + err)
      })
  }

  selectForUpload (files) {
    // TODO: replace files[file].isRemote with some logic
    //
    // filter files that are now yet being uploaded / haven’t been uploaded
    // and remote too
    const filesForUpload = Object.keys(files).filter((file) => {
      if (!files[file].progress.uploadStarted || files[file].isRemote) {
        return true
      }
      return false
    }).map((file) => {
      return files[file]
    })

    this.uploadFiles(filesForUpload)
  }

  actions () {
    this.core.emitter.on('core:pause-all', () => {
      this.pauseResume('pauseAll')
    })

    this.core.emitter.on('core:resume-all', () => {
      this.pauseResume('resumeAll')
    })

    this.core.emitter.on('core:upload', () => {
      this.core.log('Tus is uploading...')
      const files = this.core.getState().files
      this.selectForUpload(files)
    })
  }

  addResumableUploadsCapabilityFlag () {
    const newCapabilities = Object.assign({}, this.core.getState().capabilities)
    newCapabilities.resumableUploads = true
    this.core.setState({
      capabilities: newCapabilities
    })
  }

  install () {
    this.addResumableUploadsCapabilityFlag()
    this.actions()
  }
}
<commit_msg>remote progress debug<commit_after>import Plugin from './Plugin'
import tus from 'tus-js-client'
import UppySocket from '../core/UppySocket'

/**
 * Tus resumable file uploader
 *
 */
export default class Tus10 extends Plugin {
  constructor (core, opts) {
    super(core, opts)
    this.type = 'uploader'
    this.id = 'Tus'
    this.title = 'Tus'

    // set default options
    const defaultOptions = {
      resume: true,
      allowPause: true
    }

    // merge default options with the ones set by user
    this.opts = Object.assign({}, defaultOptions, opts)
  }

  pauseResume (action, fileID) {
    const updatedFiles = Object.assign({}, this.core.getState().files)
    const inProgressUpdatedFiles = Object.keys(updatedFiles).filter((file) => {
      return !updatedFiles[file].progress.uploadComplete &&
             updatedFiles[file].progress.uploadStarted
    })

    switch (action) {
      case 'toggle':
        if (updatedFiles[fileID].uploadComplete) return

        const wasPaused = updatedFiles[fileID].isPaused || false
        const isPaused = !wasPaused
        let updatedFile
        if (wasPaused) {
          updatedFile = Object.assign({}, updatedFiles[fileID], {
            isPaused: false
          })
        } else {
          updatedFile = Object.assign({}, updatedFiles[fileID], {
            isPaused: true
          })
        }
        updatedFiles[fileID] = updatedFile
        this.core.setState({files: updatedFiles})
        return isPaused
      case 'pauseAll':
        inProgressUpdatedFiles.forEach((file) => {
          const updatedFile = Object.assign({}, updatedFiles[file], {
            isPaused: true
          })
          updatedFiles[file] = updatedFile
        })
        this.core.setState({files: updatedFiles})
        return
      case 'resumeAll':
        inProgressUpdatedFiles.forEach((file) => {
          const updatedFile = Object.assign({}, updatedFiles[file], {
            isPaused: false
          })
          updatedFiles[file] = updatedFile
        })
        this.core.setState({files: updatedFiles})
        return
    }
  }

/**
 * Create a new Tus upload
 *
 * @param {object} file for use with upload
 * @param {integer} current file in a queue
 * @param {integer} total number of files in a queue
 * @returns {Promise}
 */
  upload (file, current, total) {
    this.core.log(`uploading ${current} of ${total}`)

    // Create a new tus upload
    return new Promise((resolve, reject) => {
      const upload = new tus.Upload(file.data, {

        // TODO merge this.opts or this.opts.tus here
        metadata: file.meta,
        resume: this.opts.resume,
        endpoint: this.opts.endpoint,

        onError: (err) => {
          this.core.log(err)
          this.core.emitter.emit('core:upload-error', file.id)
          reject('Failed because: ' + err)
        },
        onProgress: (bytesUploaded, bytesTotal) => {
          // Dispatch progress event
          console.log(bytesUploaded, bytesTotal)
          this.core.emitter.emit('core:upload-progress', {
            uploader: this,
            id: file.id,
            bytesUploaded: bytesUploaded,
            bytesTotal: bytesTotal
          })
        },
        onSuccess: () => {
          this.core.emitter.emit('core:upload-success', file.id, upload.url)

          this.core.log(`Download ${upload.file.name} from ${upload.url}`)
          resolve(upload)
        }
      })

      this.core.emitter.on('core:file-remove', (fileID) => {
        if (fileID === file.id) {
          console.log('removing file: ', fileID)
          upload.abort()
          resolve(`upload ${fileID} was removed`)
        }
      })

      this.core.emitter.on('core:upload-pause', (fileID) => {
        if (fileID === file.id) {
          const isPaused = this.pauseResume('toggle', fileID)
          isPaused ? upload.abort() : upload.start()
        }
      })

      this.core.emitter.on('core:pause-all', () => {
        const files = this.core.getState().files
        if (!files[file.id]) return
        upload.abort()
      })

      this.core.emitter.on('core:resume-all', () => {
        const files = this.core.getState().files
        if (!files[file.id]) return
        upload.start()
      })

      upload.start()
      this.core.emitter.emit('core:upload-started', file.id, upload)
    })
  }

  uploadRemote (file, current, total) {
    return new Promise((resolve, reject) => {
      this.core.log(file.remote.url)
      fetch(file.remote.url, {
        method: 'post',
        credentials: 'include',
        headers: {
          'Accept': 'application/json',
          'Content-Type': 'application/json'
        },
        body: JSON.stringify(Object.assign({}, file.remote.body, {
          endpoint: this.opts.endpoint,
          protocol: 'tus'
        }))
      })
      .then((res) => {
        if (res.status < 200 && res.status > 300) {
          return reject(res.statusText)
        }

        res.json()
        .then((data) => {
          // get the host domain
          var regex = /^(?:https?:\/\/|\/\/)?(?:[^@\/\n]+@)?(?:www\.)?([^\/\n]+)/
          var host = regex.exec(file.remote.host)[1]
          var socketProtocol = location.protocol === 'https:' ? 'wss' : 'ws'

          var token = data.token
          var socket = new UppySocket({
            target: socketProtocol + `://${host}/api/${token}`
          })

          socket.on('progress', (progressData) => {
            console.log(progressData)
            const {progress, bytesUploaded, bytesTotal} = progressData

            if (progress) {
              this.core.log(`Upload progress: ${progress}`)

              // Dispatch progress event
              this.core.emitter.emit('core:upload-progress', {
                uploader: this,
                id: file.id,
                bytesUploaded: bytesUploaded,
                bytesTotal: bytesTotal
              })

              if (progress === '100.00') {
                this.core.emitter.emit('core:upload-success', file.id)
                socket.close()
                return resolve()
              }
            }
          })
        })
      })
    })
  }

  uploadFiles (files) {
    if (Object.keys(files).length === 0) {
      this.core.log('no files to upload!')
      return
    }

    const uploaders = []
    files.forEach((file, index) => {
      const current = parseInt(index, 10) + 1
      const total = files.length

      if (!file.isRemote) {
        uploaders.push(this.upload(file, current, total))
      } else {
        uploaders.push(this.uploadRemote(file, current, total))
      }
    })

    return Promise.all(uploaders)
      .then(() => {
        this.core.log('All files uploaded')
        return { uploadedCount: files.length }
      })
      .catch((err) => {
        this.core.log('Upload error: ' + err)
      })
  }

  selectForUpload (files) {
    // TODO: replace files[file].isRemote with some logic
    //
    // filter files that are now yet being uploaded / haven’t been uploaded
    // and remote too
    const filesForUpload = Object.keys(files).filter((file) => {
      if (!files[file].progress.uploadStarted || files[file].isRemote) {
        return true
      }
      return false
    }).map((file) => {
      return files[file]
    })

    this.uploadFiles(filesForUpload)
  }

  actions () {
    this.core.emitter.on('core:pause-all', () => {
      this.pauseResume('pauseAll')
    })

    this.core.emitter.on('core:resume-all', () => {
      this.pauseResume('resumeAll')
    })

    this.core.emitter.on('core:upload', () => {
      this.core.log('Tus is uploading...')
      const files = this.core.getState().files
      this.selectForUpload(files)
    })
  }

  addResumableUploadsCapabilityFlag () {
    const newCapabilities = Object.assign({}, this.core.getState().capabilities)
    newCapabilities.resumableUploads = true
    this.core.setState({
      capabilities: newCapabilities
    })
  }

  install () {
    this.addResumableUploadsCapabilityFlag()
    this.actions()
  }
}
<|endoftext|>


<commit_before>import { partial } from '../util'
import createDebug from 'debug'
import _wrapRoom from '../data/room'

const debug = createDebug('miniplug:rooms')

export default function roomsPlugin () {
  const currentRoom = Symbol('Current room')

  return (mp) => {
    const wrapRoom = partial(_wrapRoom, mp)

    mp[currentRoom] = null

    mp.on('roomState', (state) => {
      mp[currentRoom] = wrapRoom(state.meta)
    })

    // Add a handler to process a room property update.
    //
    //  * `eventName` - Plug.dj socket event name for the property.
    //  * `sockProp` - Property name of the new value on the plug.dj socket
    //        event parameter.
    //  * `roomProp` - Property name for the value on the miniplug room object.
    function addUpdateHandler (eventName, sockProp, roomProp) {
      mp.ws.on(eventName, (data, targetSlug) => {
        const user = mp.user(data.u)
        const value = data[sockProp]

        debug(eventName, user && user.id, value)

        if (mp[currentRoom] && mp[currentRoom].slug === targetSlug) {
          mp[currentRoom][roomProp] = value
        }

        mp.emit(eventName, value, user)
        mp.emit('roomUpdate', {
          [roomProp]: value
        }, user)
      })
    }

    mp.on('connected', () => {
      addUpdateHandler('roomNameUpdate', 'n', 'name')
      addUpdateHandler('roomDescriptionUpdate', 'd', 'description')
      addUpdateHandler('roomWelcomeUpdate', 'w', 'welcome')
      addUpdateHandler('roomMinChatLevelUpdate', 'm', 'minChatLevel')
    })

    const room = () => mp[currentRoom]

    const getRooms = (query = '', page = 0, limit = 50) =>
      mp.get('rooms', { q: query, page, limit })
        .map(wrapRoom)
    const getFavorites = (query = '', page = 0, limit = 50) =>
      mp.get('rooms/favorites', { q: query, page, limit })
        .map(wrapRoom)
    const createRoom = (name, isPrivate = false) =>
      mp.post('rooms', { name: name, private: isPrivate }).get(0)
    const getMyRooms = () =>
      mp.get('rooms/me').map(wrapRoom)

    const favoriteRoom = (rid) =>
      mp.post('rooms/favorites', { id: rid })
    const unfavoriteRoom = (rid) =>
      mp.del(`rooms/favorites/${rid}`)
    const join = (slug) =>
      mp.post('rooms/join', { slug: slug }).then(getRoomState)
    const getRoomState = () =>
      mp.get('rooms/state').get(0)
        .tap(mp.emit.bind(mp, 'roomState'))

    const validateRoomName = (name) =>
      mp.get(`rooms/validate/${encodeURIComponent(name)}`)
        .get(0)
        .catch(() => Promise.reject(new Error('Room name unavailable.')))

    Object.assign(mp, {
      // local
      room,
      // remote
      getRooms,
      getFavorites,
      getMyRooms,
      createRoom,
      validateRoomName,
      // favorites
      favoriteRoom,
      unfavoriteRoom,
      // joining
      join,
      getRoomState
    })
  }
}
<commit_msg>Add room property setters.<commit_after>import { partial } from '../util'
import createDebug from 'debug'
import _wrapRoom from '../data/room'

const debug = createDebug('miniplug:rooms')

export default function roomsPlugin () {
  const currentRoom = Symbol('Current room')

  return (mp) => {
    const wrapRoom = partial(_wrapRoom, mp)

    mp[currentRoom] = null

    mp.on('roomState', (state) => {
      mp[currentRoom] = wrapRoom(state.meta)
    })

    // Add a handler to process a room property update.
    //
    //  * `eventName` - Plug.dj socket event name for the property.
    //  * `sockProp` - Property name of the new value on the plug.dj socket
    //        event parameter.
    //  * `roomProp` - Property name for the value on the miniplug room object.
    function addUpdateHandler (eventName, sockProp, roomProp) {
      mp.ws.on(eventName, (data, targetSlug) => {
        const user = mp.user(data.u)
        const value = data[sockProp]

        debug(eventName, user && user.id, value)

        if (mp[currentRoom] && mp[currentRoom].slug === targetSlug) {
          mp[currentRoom][roomProp] = value
        }

        mp.emit(eventName, value, user)
        mp.emit('roomUpdate', {
          [roomProp]: value
        }, user)
      })
    }

    mp.on('connected', () => {
      addUpdateHandler('roomNameUpdate', 'n', 'name')
      addUpdateHandler('roomDescriptionUpdate', 'd', 'description')
      addUpdateHandler('roomWelcomeUpdate', 'w', 'welcome')
      addUpdateHandler('roomMinChatLevelUpdate', 'm', 'minChatLevel')
    })

    const room = () => mp[currentRoom]

    const getRooms = (query = '', page = 0, limit = 50) =>
      mp.get('rooms', { q: query, page, limit })
        .map(wrapRoom)
    const getFavorites = (query = '', page = 0, limit = 50) =>
      mp.get('rooms/favorites', { q: query, page, limit })
        .map(wrapRoom)
    const createRoom = (name, isPrivate = false) =>
      mp.post('rooms', { name: name, private: isPrivate }).get(0)
    const getMyRooms = () =>
      mp.get('rooms/me').map(wrapRoom)

    const favoriteRoom = (rid) =>
      mp.post('rooms/favorites', { id: rid })
    const unfavoriteRoom = (rid) =>
      mp.del(`rooms/favorites/${rid}`)
    const join = (slug) =>
      mp.post('rooms/join', { slug: slug }).then(getRoomState)
    const getRoomState = () =>
      mp.get('rooms/state').get(0)
        .tap(mp.emit.bind(mp, 'roomState'))

    const updateRoom = (patch) => {
      if (!room()) {
        return Promise.reject(new Error('You are not currently in a room.'))
      }
      // TODO check keys used in `patch`? Only 'name', 'description', 'welcome'
      // and 'minChatLevel' can be updated.
      return mp.post('rooms/update', patch)
    }

    const setRoomWelcome = (welcome) => updateRoom({ welcome })
    const setRoomDescription = (description) => updateRoom({ description })
    const setRoomName = (name) => updateRoom({ name })
    const setRoomMinChatLevel = (minChatLevel) => updateRoom({ minChatLevel })

    const validateRoomName = (name) =>
      mp.get(`rooms/validate/${encodeURIComponent(name)}`)
        .get(0)
        .catch(() => Promise.reject(new Error('Room name unavailable.')))

    Object.assign(mp, {
      // local
      room,
      // remote
      getRooms,
      getFavorites,
      getMyRooms,
      createRoom,
      validateRoomName,
      setRoomWelcome,
      setRoomDescription,
      setRoomName,
      setRoomMinChatLevel,
      // favorites
      favoriteRoom,
      unfavoriteRoom,
      // joining
      join,
      getRoomState
    })
  }
}
<|endoftext|>


<commit_before>"use strict"
var fs = require("fs")
var path = require("path")
var dz = require("dezalgo")
var npa = require("npm-package-arg")

module.exports = function (spec, where, cb) {
  if (where instanceof Function) { cb = where; where = null }
  if (where == null) where = "."
  cb = dz(cb)
  try {
    var dep = npa(spec)
  }
  catch (e) {
    return cb(e)
  }
  if ((dep.type == "range" || dep.type == "version") && dep.name != dep.raw) return cb(null, dep)
  var specpath = dep.type == "local"
               ? path.resolve(where, dep.spec)
               : path.resolve(where, dep.rawSpec? dep.rawSpec: dep.name)
  fs.stat(specpath, function (er, s) {
    if (er) return finalize()
    if (!s.isDirectory()) return finalize("local")
    fs.stat(path.join(specpath, "package.json"), function (er) {
      finalize(er ? null : "directory")
    })
  })
  function finalize(type) {
    if (type != null && type != dep.type) {
      dep.type = type
      if (! dep.rawSpec) {
        dep.rawSpec = dep.name
        dep.name = null
      }
    }
    if (dep.type == "local" || dep.type == "directory") dep.spec = specpath
    cb(null, dep)
  }
}
<commit_msg>realize-package-specifier@3.0.2<commit_after>"use strict"
var fs = require("fs")
var path = require("path")
var dz = require("dezalgo")
var npa = require("npm-package-arg")

module.exports = function (spec, where, cb) {
  if (where instanceof Function) { cb = where; where = null }
  if (where == null) where = "."
  cb = dz(cb)
  try {
    var dep = npa(spec)
  }
  catch (e) {
    return cb(e)
  }
  if ((dep.type == "range" || dep.type == "version") && dep.name != dep.raw) return cb(null, dep)
  var specpath = dep.type == "local"
               ? path.resolve(where, dep.spec)
               : path.resolve(where, dep.rawSpec? dep.rawSpec: dep.name)
  fs.stat(specpath, function (er, s) {
    if (er) return finalize()
    if (!s.isDirectory()) return finalize("local")
    fs.stat(path.join(specpath, "package.json"), function (er) {
      finalize(er ? null : "directory")
    })
  })
  function finalize(type) {
    if (type != null && type != dep.type) {
      dep.type = type
      if (! dep.rawSpec) {
        dep.rawSpec = dep.name
        dep.name = null
      }
    }
    if (dep.type == "local" || dep.type == "directory") {
      dep.spec = path.resolve(specpath)
    }
    cb(null, dep)
  }
}
<|endoftext|>


<commit_before>angular.module('opal.controllers').controller(
    'EpisodeListCtrl', function($scope, $q, $http, $cookieStore,
                                $location, $routeParams,
                                $modal, $rootScope,
                                Flow, Item,
                                Episode, schema, episodes, options,
                                profile,
                                episodeVisibility, viewDischarged){

        var version = window.version;
        $scope.state = 'normal';

	    $scope.rix = 0; // row index
	    $scope.cix = 0; // column index
	    $scope.iix = 0; // item index

	    $scope.mouseRix = -1; // index of row mouse is currently over
	    $scope.mouseCix = -1; // index of column mouse is currently over
        $scope._ =  _;

	    $scope.query = {hospital_number: '', name: '', ward: '', bed: ''};

        if(viewDischarged){
            $scope.path_base = '/discharge/';
        }else{
            $scope.path_base = '/list/';
        }

        if(!$routeParams.tag){
            var tag =  $cookieStore.get('opal.currentTag') || 'mine';
            $location.path($scope.path_base + tag);
            return
        }
        $scope.currentTag = $routeParams.tag;

        if(!$routeParams.subtag){
            // We now force redirect to the first subtag if there is one
            if($scope.currentTag in options.tag_hierarchy &&
               options.tag_hierarchy[$scope.currentTag].length > 0){
                var subtag = options.tag_hierarchy[$scope.currentTag][0];
                var target = $scope.path_base + $scope.currentTag + '/' + subtag;
                $location.path(target);
                return
            }else{
                $scope.currentSubTag = 'all';
            }
        }else{
            $scope.currentSubTag = $routeParams.subtag;
        }
        $scope.columns = schema.columns;

        $scope.profile = profile;
        $scope.tag_display = options.tag_display;

	    function getVisibleEpisodes() {
		    var visibleEpisodes = [];
            var episode_list = [];

            visibleEpisodes = _.filter(episodes, function(episode){
                return episodeVisibility(episode, $scope, viewDischarged)
            });
		    visibleEpisodes.sort(compareEpisodes);
		    return visibleEpisodes;
	    };

	    $scope.rows = getVisibleEpisodes();

	    function compareEpisodes(p1, p2) {
		    return p1.compare(p2);
	    };

        $scope.jumpToTag = function(tag){
            if(_.contains(_.keys(options.tag_hierarchy), tag)){
                $location.path($scope.path_base + tag)
            }else{

                for(var prop in options.tag_hierarchy){
                    if(options.tag_hierarchy.hasOwnProperty(prop)){
                        if(_.contains(_.values(options.tag_hierarchy[prop]), tag)){
                            $location.path($scope.path_base + prop + '/' + tag)
                        }
                    }
                }

            };
        }

        $scope.otherTags = function(episode){
            tags = episode.getTags();
            return _.filter(tags, function(t){
                if(t in options.tag_hierarchy &&
                   options.tag_hierarchy[t].length > 0){ return false };
                if(t == $scope.currentTag){ return false };
                if(t == $scope.currentSubTag){ return false };
                return true
            });
        }

	    $scope.$watch('currentTag', function() {
		    $cookieStore.put('opal.currentTag', $scope.currentTag);
            if($scope.currentTag != $routeParams.tag){
                $scope.state = 'reloading'
            }
            var target = $scope.path_base +  $scope.currentTag;

            $location.path(target);
	    });

	    $scope.$watch('currentSubTag', function(){
		    $cookieStore.put('opal.currentSubTag', $scope.currentSubTag);
            if($scope.currentSubTag == 'all'){
                if($routeParams.subtag && $scope.currentSubTag != $routeParams.subtag){
                    $scope.state = 'reloading'
                }
                $location.path($scope.path_base +  $scope.currentTag);
            }else{
                if($scope.currentSubTag != $routeParams.subtag){
                    $scope.state = 'reloading'
                }
                $location.path($scope.path_base +  $scope.currentTag + '/' +  $scope.currentSubTag);
            }
	    });

        $scope.showSubtags = function(withsubtags){
            var show =  _.contains(withsubtags, $scope.currentTag);
            return show
        };

	    $scope.$watch('query.hospital_number', function() {
		    $scope.rows = getVisibleEpisodes();
	    });
	    $scope.$watch('query.ward', function() {
		    $scope.rows = getVisibleEpisodes();
	    });
	    $scope.$watch('query.bed', function() {
		    $scope.rows = getVisibleEpisodes();
	    });

	    $scope.$watch('query.name', function() {
		    $scope.rows = getVisibleEpisodes();
	    });

	    $scope.$on('keydown', function(event, e) {
		    if ($scope.state == 'normal') {
			    switch (e.keyCode) {
			    case 37: // left
				    goLeft();
				    break;
			    case 39: // right
				    goRight();
				    break;
			    case 38: // up
				    goUp();
				    break;
			    case 40: // down
				    goDown();
				    break;
			    case 13: // enter
			    case 113: // F2
				    $scope.editItem($scope.rix, $scope.cix, $scope.iix);
                    e.preventDefault();
				    break;
			    case 8: // backspace
				    e.preventDefault();
			    case 46: // delete
				    $scope.deleteItem($scope.rix, $scope.cix, $scope.iix);
				    break;
			    case 191: // question mark
				    if(e.shiftKey){
					    showKeyboardShortcuts();
				    }
				    break;
                case 78: // n
                    $scope.addEpisode();
                    e.preventDefault();
                    break;
			    };
		    };
	    });

	    function getColumnName(cix) {
		    return $scope.columns[cix].name;
	    };

	    function getRowIxFromEpisodeId(episodeId) {
		    for (var rix = 0; rix < $scope.rows.length; rix++) {
			    if ($scope.rows[rix].id == episodeId) {
				    return rix;
			    }
		    };
		    return -1;
	    };

	    function getEpisode(rix) {
		    return $scope.rows[rix];
	    };

	    $scope.print = function() {
		    window.print();
	    };

	    $scope.selectItem = function(rix, cix, iix) {
		    $scope.rix = rix;
		    $scope.cix = cix;
		    $scope.iix = iix;
	    };

	    $scope.focusOnQuery = function() {
		    $scope.selectItem(-1, -1, -1);
		    $scope.state = 'search';
	    };

	    $scope.blurOnQuery = function() {
		    if ($scope.rix == -1) {
			    $scope.selectItem(0, 0, 0);
		    };
		    $scope.state = 'normal';
	    };

	    $scope.addEpisode = function() {
            if(profile.readonly){ return null; };

            var enter = Flow(
                'enter', schema, options,
                {
                    current_tags: {
                        tag: $scope.currentTag,
                        subtag: $scope.currentSubTag
                    }
                }
            );

		    $scope.state = 'modal';

            enter.then(
                function(episode) {
		            // User has either retrieved an existing episode or created a new one,
		            // or has cancelled the process at some point.
		            //
		            // This ensures that the relevant episode is added to the table and
		            // selected.
		            var rowIx;
		            $scope.state = 'normal';
		            if (episode && episode != 'cancel') {
			            episodes[episode.id] = episode;
			            $scope.rows = getVisibleEpisodes();
			            rowIx = getRowIxFromEpisodeId(episode.id);
			            $scope.selectItem(rowIx, 0, 0);
		            };
	            },
                function(reason){
                    // The modal has been dismissed. Practically speaking this means
                    // that the Angular UI called dismiss rather than our cancel()
                    // method on the OPAL controller. We just need to re-set in order
                    // to re-enable keybard listeners.
                    $scope.state = 'normal';
                });
	    };

	    $scope.dischargeEpisode = function(rix, event) {
		    var episode = getEpisode(rix);

            if(profile.readonly){ return null; };

		    event.preventDefault();// Required to prevent the page reloading
		    $scope.state = 'modal';

            var exit = Flow(
                'exit', schema, options,
                {
                    current_tags: {
                        tag   : $scope.currentTag,
                        subtag: $scope.currentSubTag
                    },
                    episode: episode
                }
            );

            exit.then(function(result) {
			    $scope.state = 'normal';

			    if (result == 'discharged' | result == 'moved') {
				    $scope.rows = getVisibleEpisodes();
				    $scope.selectItem(0, $scope.cix, 0);
			    };
		    });
	    };

        // TODO: Test This!
        $scope.removeFromMine = function(rix, event){
            if(profile.readonly){
                return null;
            };

            event.preventDefault();

            var modal;
            var episode = getEpisode(rix);
            var tagging = episode.tagging[0];
            editing = tagging.makeCopy();
            editing.mine = false;
            tagging.save(editing).then(function(result){
                $scope.rows = getVisibleEpisodes();
            })

        };

      _openEditItemModal = function(item, columnName, episode) {
        if(profile.readonly){
          return null;
        };
        $scope.state = 'modal';

        modal = $modal.open({
          templateUrl: '/templates/modals/' + columnName + '.html/',
          controller: 'EditItemCtrl',
          resolve: {
            item: function() { return item; },
            options: function() { return options; },
            episode: function() { return episode; }
          }
        });

        modal.result.then(function(result) {
          $scope.state = 'normal';

          if (columnName == 'tagging') {
            // User may have removed current tag
            $scope.rows = getVisibleEpisodes();
            $scope.selectItem(getRowIxFromEpisodeId(episode.id), $scope.cix, 0);
          }

          if (result == 'save-and-add-another') {
            $scope.editItem(rix, cix, episode.getNumberOfItems(columnName));
          };
                if (item.sort){
                    episode.sortColumn(item.columnName, item.sort);
                };
        }, function(){
                $scope.state = 'normal';
            });
      }

      $scope.newNamedItem = function(episode, name) {
        var item = episode.newItem(name, {column: $rootScope.fields[name]});
        if (!episode[name]) {
          episode[name] = [];
        }
        episode[name][0] = item;
        _openEditItemModal(item, name, episode);
      }

      $scope.editNamedItem = function(episode, name, iix) {
        var item;
        if (episode[name][iix] && episode[name][iix].columnName) {
          item = episode[name][iix];
        } else {
          item = new Item(episode[name][iix], episode, $rootScope.fields[name]);
          episode[name][iix] = item;
        }

        _openEditItemModal(item, name, episode);
      }

	    $scope.editItem = function(rix, cix, iix) {
		    var modal;
		    var columnName = getColumnName(cix);
		    var episode = getEpisode(rix);
		    var item;

		    if (iix == episode.getNumberOfItems(columnName)) {
			    item = episode.newItem(columnName, {schema: schema});
		    } else {
			    item = episode.getItem(columnName, iix);
		    };

		    $scope.selectItem(rix, cix, iix);
        _openEditItemModal(item, columnName, episode);
	    };

	    $scope.deleteItem = function(rix, cix, iix) {
		    var modal;
		    var columnName = getColumnName(cix);
		    var episode = getEpisode(rix);
		    var item = episode.getItem(columnName, iix);

            if(profile.readonly){
                return null;
            };

            if (schema.isReadOnly(columnName)) {
                // Cannont delete readonly columns
                return;
            }

		    if (schema.isSingleton(columnName)) {
			    // Cannot delete singleton
			    return;
		    }

		    if (!angular.isDefined(item)) {
			    // Cannot delete 'Add'
			    return;
		    }

		    $scope.state = 'modal'
		    modal = $modal.open({
			    templateUrl: '/templates/modals/delete_item_confirmation.html/',
			    controller: 'DeleteItemConfirmationCtrl',
			    resolve: {
				    item: function() { return item; }
			    }
		    });

		    modal.result.then(function(result) {
			    $scope.state = 'normal';
		    });
	    };

	    $scope.mouseEnter = function(rix, cix) {
		    $scope.mouseRix = rix;
		    $scope.mouseCix = cix;
	    }

	    $scope.mouseLeave = function() {
		    $scope.mouseRix = -1;
		    $scope.mouseCix = -1;
	    }

        function showKeyboardShortcuts(){
		    // TODO fix this
            $('#keyboard-shortcuts').modal();
        };

	    function goLeft() {
		    if ($scope.cix > 0) {
			    $scope.cix--;
			    $scope.iix = 0;
		    };
	    };

	    function goRight() {
		    if ($scope.cix < $scope.columns.length - 1) {
			    $scope.cix++;
			    $scope.iix = 0;
		    };
	    };

	    function goUp() {
		    var episode;
		    var columnName = getColumnName($scope.cix);

		    if ($scope.iix > 0) {
			    $scope.iix--;
		    } else if ($scope.rix > 0) {
			    $scope.rix--;
			    if (schema.isSingleton(columnName)) {
				    $scope.iix = 0;
			    } else {
				    episode = getEpisode($scope.rix);
				    $scope.iix = episode.getNumberOfItems(columnName);
			    };
		    };
	    };

	    function goDown() {
		    var episode = getEpisode($scope.rix);
		    var columnName = getColumnName($scope.cix);

		    if (!schema.isSingleton(columnName) &&
		        ($scope.iix < episode.getNumberOfItems(columnName))) {
			    $scope.iix++;
		    } else if ($scope.rix < $scope.rows.length - 1) {
			    $scope.rix++;
			    $scope.iix = 0;
		    };
	    };

        $scope.controller_for_episode = function(controller, template, size, episode){
            $modal.open({
                controller : controller,
                templateUrl: template,
                size       : size,
                resolve    : {
                    episode: function(){ return episode }
                }
            });
        }
    });
<commit_msg>Make the url fragment available to episode_list.$scope<commit_after>angular.module('opal.controllers').controller(
    'EpisodeListCtrl', function($scope, $q, $http, $cookieStore,
                                $location, $routeParams,
                                $modal, $rootScope,
                                Flow, Item,
                                Episode, schema, episodes, options,
                                profile,
                                episodeVisibility, viewDischarged){

        var version = window.version;
        $scope.state = 'normal';
        $scope.url = $location.url()

	    $scope.rix = 0; // row index
	    $scope.cix = 0; // column index
	    $scope.iix = 0; // item index

	    $scope.mouseRix = -1; // index of row mouse is currently over
	    $scope.mouseCix = -1; // index of column mouse is currently over
        $scope._ =  _;

	    $scope.query = {hospital_number: '', name: '', ward: '', bed: ''};

        if(viewDischarged){
            $scope.path_base = '/discharge/';
        }else{
            $scope.path_base = '/list/';
        }

        if(!$routeParams.tag){
            var tag =  $cookieStore.get('opal.currentTag') || 'mine';
            $location.path($scope.path_base + tag);
            return
        }
        $scope.currentTag = $routeParams.tag;

        if(!$routeParams.subtag){
            // We now force redirect to the first subtag if there is one
            if($scope.currentTag in options.tag_hierarchy &&
               options.tag_hierarchy[$scope.currentTag].length > 0){
                var subtag = options.tag_hierarchy[$scope.currentTag][0];
                var target = $scope.path_base + $scope.currentTag + '/' + subtag;
                $location.path(target);
                return
            }else{
                $scope.currentSubTag = 'all';
            }
        }else{
            $scope.currentSubTag = $routeParams.subtag;
        }
        $scope.columns = schema.columns;

        $scope.profile = profile;
        $scope.tag_display = options.tag_display;

	    function getVisibleEpisodes() {
		    var visibleEpisodes = [];
            var episode_list = [];

            visibleEpisodes = _.filter(episodes, function(episode){
                return episodeVisibility(episode, $scope, viewDischarged)
            });
		    visibleEpisodes.sort(compareEpisodes);
		    return visibleEpisodes;
	    };

	    $scope.rows = getVisibleEpisodes();

	    function compareEpisodes(p1, p2) {
		    return p1.compare(p2);
	    };

        $scope.jumpToTag = function(tag){
            if(_.contains(_.keys(options.tag_hierarchy), tag)){
                $location.path($scope.path_base + tag)
            }else{

                for(var prop in options.tag_hierarchy){
                    if(options.tag_hierarchy.hasOwnProperty(prop)){
                        if(_.contains(_.values(options.tag_hierarchy[prop]), tag)){
                            $location.path($scope.path_base + prop + '/' + tag)
                        }
                    }
                }

            };
        }

        $scope.otherTags = function(episode){
            tags = episode.getTags();
            return _.filter(tags, function(t){
                if(t in options.tag_hierarchy &&
                   options.tag_hierarchy[t].length > 0){ return false };
                if(t == $scope.currentTag){ return false };
                if(t == $scope.currentSubTag){ return false };
                return true
            });
        }

	    $scope.$watch('currentTag', function() {
		    $cookieStore.put('opal.currentTag', $scope.currentTag);
            if($scope.currentTag != $routeParams.tag){
                $scope.state = 'reloading'
            }
            var target = $scope.path_base +  $scope.currentTag;

            $location.path(target);
	    });

	    $scope.$watch('currentSubTag', function(){
		    $cookieStore.put('opal.currentSubTag', $scope.currentSubTag);
            if($scope.currentSubTag == 'all'){
                if($routeParams.subtag && $scope.currentSubTag != $routeParams.subtag){
                    $scope.state = 'reloading'
                }
                $location.path($scope.path_base +  $scope.currentTag);
            }else{
                if($scope.currentSubTag != $routeParams.subtag){
                    $scope.state = 'reloading'
                }
                $location.path($scope.path_base +  $scope.currentTag + '/' +  $scope.currentSubTag);
            }
	    });

        $scope.showSubtags = function(withsubtags){
            var show =  _.contains(withsubtags, $scope.currentTag);
            return show
        };

	    $scope.$watch('query.hospital_number', function() {
		    $scope.rows = getVisibleEpisodes();
	    });
	    $scope.$watch('query.ward', function() {
		    $scope.rows = getVisibleEpisodes();
	    });
	    $scope.$watch('query.bed', function() {
		    $scope.rows = getVisibleEpisodes();
	    });

	    $scope.$watch('query.name', function() {
		    $scope.rows = getVisibleEpisodes();
	    });

	    $scope.$on('keydown', function(event, e) {
		    if ($scope.state == 'normal') {
			    switch (e.keyCode) {
			    case 37: // left
				    goLeft();
				    break;
			    case 39: // right
				    goRight();
				    break;
			    case 38: // up
				    goUp();
				    break;
			    case 40: // down
				    goDown();
				    break;
			    case 13: // enter
			    case 113: // F2
				    $scope.editItem($scope.rix, $scope.cix, $scope.iix);
                    e.preventDefault();
				    break;
			    case 8: // backspace
				    e.preventDefault();
			    case 46: // delete
				    $scope.deleteItem($scope.rix, $scope.cix, $scope.iix);
				    break;
			    case 191: // question mark
				    if(e.shiftKey){
					    showKeyboardShortcuts();
				    }
				    break;
                case 78: // n
                    $scope.addEpisode();
                    e.preventDefault();
                    break;
			    };
		    };
	    });

	    function getColumnName(cix) {
		    return $scope.columns[cix].name;
	    };

	    function getRowIxFromEpisodeId(episodeId) {
		    for (var rix = 0; rix < $scope.rows.length; rix++) {
			    if ($scope.rows[rix].id == episodeId) {
				    return rix;
			    }
		    };
		    return -1;
	    };

	    function getEpisode(rix) {
		    return $scope.rows[rix];
	    };

	    $scope.print = function() {
		    window.print();
	    };

	    $scope.selectItem = function(rix, cix, iix) {
		    $scope.rix = rix;
		    $scope.cix = cix;
		    $scope.iix = iix;
	    };

	    $scope.focusOnQuery = function() {
		    $scope.selectItem(-1, -1, -1);
		    $scope.state = 'search';
	    };

	    $scope.blurOnQuery = function() {
		    if ($scope.rix == -1) {
			    $scope.selectItem(0, 0, 0);
		    };
		    $scope.state = 'normal';
	    };

	    $scope.addEpisode = function() {
            if(profile.readonly){ return null; };

            var enter = Flow(
                'enter', schema, options,
                {
                    current_tags: {
                        tag: $scope.currentTag,
                        subtag: $scope.currentSubTag
                    }
                }
            );

		    $scope.state = 'modal';

            enter.then(
                function(episode) {
		            // User has either retrieved an existing episode or created a new one,
		            // or has cancelled the process at some point.
		            //
		            // This ensures that the relevant episode is added to the table and
		            // selected.
		            var rowIx;
		            $scope.state = 'normal';
		            if (episode && episode != 'cancel') {
			            episodes[episode.id] = episode;
			            $scope.rows = getVisibleEpisodes();
			            rowIx = getRowIxFromEpisodeId(episode.id);
			            $scope.selectItem(rowIx, 0, 0);
		            };
	            },
                function(reason){
                    // The modal has been dismissed. Practically speaking this means
                    // that the Angular UI called dismiss rather than our cancel()
                    // method on the OPAL controller. We just need to re-set in order
                    // to re-enable keybard listeners.
                    $scope.state = 'normal';
                });
	    };

	    $scope.dischargeEpisode = function(rix, event) {
		    var episode = getEpisode(rix);

            if(profile.readonly){ return null; };

		    event.preventDefault();// Required to prevent the page reloading
		    $scope.state = 'modal';

            var exit = Flow(
                'exit', schema, options,
                {
                    current_tags: {
                        tag   : $scope.currentTag,
                        subtag: $scope.currentSubTag
                    },
                    episode: episode
                }
            );

            exit.then(function(result) {
			    $scope.state = 'normal';

			    if (result == 'discharged' | result == 'moved') {
				    $scope.rows = getVisibleEpisodes();
				    $scope.selectItem(0, $scope.cix, 0);
			    };
		    });
	    };

        // TODO: Test This!
        $scope.removeFromMine = function(rix, event){
            if(profile.readonly){
                return null;
            };

            event.preventDefault();

            var modal;
            var episode = getEpisode(rix);
            var tagging = episode.tagging[0];
            editing = tagging.makeCopy();
            editing.mine = false;
            tagging.save(editing).then(function(result){
                $scope.rows = getVisibleEpisodes();
            })

        };

      _openEditItemModal = function(item, columnName, episode) {
        if(profile.readonly){
          return null;
        };
        $scope.state = 'modal';

        modal = $modal.open({
          templateUrl: '/templates/modals/' + columnName + '.html/',
          controller: 'EditItemCtrl',
          resolve: {
            item: function() { return item; },
            options: function() { return options; },
            episode: function() { return episode; }
          }
        });

        modal.result.then(function(result) {
          $scope.state = 'normal';

          if (columnName == 'tagging') {
            // User may have removed current tag
            $scope.rows = getVisibleEpisodes();
            $scope.selectItem(getRowIxFromEpisodeId(episode.id), $scope.cix, 0);
          }

          if (result == 'save-and-add-another') {
            $scope.editItem(rix, cix, episode.getNumberOfItems(columnName));
          };
                if (item.sort){
                    episode.sortColumn(item.columnName, item.sort);
                };
        }, function(){
                $scope.state = 'normal';
            });
      }

      $scope.newNamedItem = function(episode, name) {
        var item = episode.newItem(name, {column: $rootScope.fields[name]});
        if (!episode[name]) {
          episode[name] = [];
        }
        episode[name][0] = item;
        _openEditItemModal(item, name, episode);
      }

      $scope.editNamedItem = function(episode, name, iix) {
        var item;
        if (episode[name][iix] && episode[name][iix].columnName) {
          item = episode[name][iix];
        } else {
          item = new Item(episode[name][iix], episode, $rootScope.fields[name]);
          episode[name][iix] = item;
        }

        _openEditItemModal(item, name, episode);
      }

	    $scope.editItem = function(rix, cix, iix) {
		    var modal;
		    var columnName = getColumnName(cix);
		    var episode = getEpisode(rix);
		    var item;

		    if (iix == episode.getNumberOfItems(columnName)) {
			    item = episode.newItem(columnName, {schema: schema});
		    } else {
			    item = episode.getItem(columnName, iix);
		    };

		    $scope.selectItem(rix, cix, iix);
        _openEditItemModal(item, columnName, episode);
	    };

	    $scope.deleteItem = function(rix, cix, iix) {
		    var modal;
		    var columnName = getColumnName(cix);
		    var episode = getEpisode(rix);
		    var item = episode.getItem(columnName, iix);

            if(profile.readonly){
                return null;
            };

            if (schema.isReadOnly(columnName)) {
                // Cannont delete readonly columns
                return;
            }

		    if (schema.isSingleton(columnName)) {
			    // Cannot delete singleton
			    return;
		    }

		    if (!angular.isDefined(item)) {
			    // Cannot delete 'Add'
			    return;
		    }

		    $scope.state = 'modal'
		    modal = $modal.open({
			    templateUrl: '/templates/modals/delete_item_confirmation.html/',
			    controller: 'DeleteItemConfirmationCtrl',
			    resolve: {
				    item: function() { return item; }
			    }
		    });

		    modal.result.then(function(result) {
			    $scope.state = 'normal';
		    });
	    };

	    $scope.mouseEnter = function(rix, cix) {
		    $scope.mouseRix = rix;
		    $scope.mouseCix = cix;
	    }

	    $scope.mouseLeave = function() {
		    $scope.mouseRix = -1;
		    $scope.mouseCix = -1;
	    }

        function showKeyboardShortcuts(){
		    // TODO fix this
            $('#keyboard-shortcuts').modal();
        };

	    function goLeft() {
		    if ($scope.cix > 0) {
			    $scope.cix--;
			    $scope.iix = 0;
		    };
	    };

	    function goRight() {
		    if ($scope.cix < $scope.columns.length - 1) {
			    $scope.cix++;
			    $scope.iix = 0;
		    };
	    };

	    function goUp() {
		    var episode;
		    var columnName = getColumnName($scope.cix);

		    if ($scope.iix > 0) {
			    $scope.iix--;
		    } else if ($scope.rix > 0) {
			    $scope.rix--;
			    if (schema.isSingleton(columnName)) {
				    $scope.iix = 0;
			    } else {
				    episode = getEpisode($scope.rix);
				    $scope.iix = episode.getNumberOfItems(columnName);
			    };
		    };
	    };

	    function goDown() {
		    var episode = getEpisode($scope.rix);
		    var columnName = getColumnName($scope.cix);

		    if (!schema.isSingleton(columnName) &&
		        ($scope.iix < episode.getNumberOfItems(columnName))) {
			    $scope.iix++;
		    } else if ($scope.rix < $scope.rows.length - 1) {
			    $scope.rix++;
			    $scope.iix = 0;
		    };
	    };

        $scope.controller_for_episode = function(controller, template, size, episode){
            $modal.open({
                controller : controller,
                templateUrl: template,
                size       : size,
                resolve    : {
                    episode: function(){ return episode }
                }
            });
        }
    });
<|endoftext|>


<commit_before>import { PLAYER_MOVE, COMPUTER_MOVE,
         RESET_GAME, SET_GAME_MODE } from "../actions/GameActions.js";

export const VS_HUMAN = "VS_HUMAN";
export const EASY = "EASY";
export const MEDIUM = "MEDIUM";
export const HARD = "HARD";

export const difficultyModificator = {
  EASY: 0.8,
  MEDIUM: 0.5,
  HARD: 0.2
}

export const initialGameState = function() {
  return {
    gameGrid: [["","",""],
               ["","",""],
               ["","",""]],
    currentTurn: "x",
    victoryStatistics: {
      x: 0,
      o: 0
    },
    gameMode: -1,
    AI: {
      playingX: false
    },
    gameOver: false
  };
};

const game = function (state = initialGameState(), action) {
  switch (action.type) {
    case PLAYER_MOVE:
      return handlePlayerMove(state, action.x, action.y);
    case COMPUTER_MOVE:
      return handleComputerMove(state);
    case SET_GAME_MODE:
      return setGameMode(state, action.mode);
    case RESET_GAME:
      return initialGameState();
    default:
      return state;
  }
};

const handlePlayerMove = function (state, x, y) {
  if(state.gameOver) {
    return startNewGame(state);
  }
  if(state.gameGrid[x][y] !== "" ||
     (((state.currentTurn === "x") === state.AI.playingX) &&
     state.gameMode !== VS_HUMAN)) {
    return state;
  }
  return applyMove(state, x, y);
}

const handleComputerMove = function (state) {
  if(state.gameMode === VS_HUMAN || state.gameOver ||
     (state.currentTurn === "o") === state.AI.playingX) {
    return state;
  }
  let move;
  if(Math.random() < difficultyModificator[state.gameMode]) {
    // make random move
    move = getRandomElement(getAvaliableMoves(state.gameGrid));
  }
  else {
    //make best move possible
    move = minimax(state, 0);
  }
  return applyMove(state, move.x, move.y);
}

const setGameMode = function (state, mode) {
  var newState = Object.assign({}, state, {
    gameMode: mode
  });
  newState.AI.isOn = mode !== VS_HUMAN;
  return newState;
}

const applyMove = function (state, x, y) {

  var newState = Object.assign({}, state, {
    gameGrid: state.gameGrid.map(a => a.slice())
  });
  newState.gameGrid[x][y] = newState.currentTurn;
  newState.currentTurn = newState.currentTurn === "x" ? "o": "x";

  var winner = checkBoard(newState.gameGrid);
  if(winner) {
    newState.gameOver = true;
    newState.victoryStatistics = {
      x: state.victoryStatistics.x,
      o: state.victoryStatistics.o
    };
    if(winner === "tie") {
      return newState;
    }
    newState.victoryStatistics[winner]++;
  }
  return newState;
};

const startNewGame = function (state) {
  return Object.assign({}, initialGameState(), {
    gameMode: state.gameMode,
    victoryStatistics: state.victoryStatistics,
    AI : {
      playingX: !state.AI.playingX
    }
  });
}

// check whether someone won or not
const checkBoard = function (board) {
  var winner;

  //check rows
  winner = checkRows(board);
  if(winner) {
    return winner;
  }

  // check columns
  winner = checkColumns(board);
  if(winner) {
    return winner;
  }

  // check diagonals
  winner = checkDiagonals(board);
  if(winner) {
    return winner;
  }

  // check if there's still empty tiles
  if(isBoardFull(board)) {
    return "tie";
  }

  return "";
};

const checkRows = function (b) {
  for (let i = 0; i < b.length; i++) {
    if(b[i][0] === b[i][1] && b[i][1] === b[i][2] && b[i][0] !== "")
      return b[i][0];
  }
};

const checkColumns = function (b) {
  for (let i = 0; i < b.length; i++) {
    if(b[0][i] === b[1][i] && b[1][i] === b[2][i] && b[0][i] !== "")
      return b[0][i];
  }
}

const checkDiagonals = function (b) {
  if((b[0][0] === b[1][1] && b[1][1] === b[2][2] ||
      b[0][2] === b[1][1] && b[1][1] === b[2][0]) &&
      b[1][1] !== "") {
    return b[1][1];
  }
}

const isBoardFull = function (b) {
  return !b.some(row => row.some(elem => elem === ""));
}

const MAX_SCORE = 10;

const score = function (winner, depth) {
  if(winner === "tie") {
    return 0;
  }
  if(depth%2 === 1) {
    return MAX_SCORE - depth;
  }
  else {
    return depth - MAX_SCORE;
  }
};

const minimax = function (game, depth) {
  var scores = [],
      moves = [];
  if(checkBoard(game.gameGrid) !== "") {
    return score (game, depth);
  }

  moves = getAvaliableMoves(game.gameGrid);
  scores = moves.map((move) => minimax(simulateMove(game, move), depth+1));

  if(depth === 0) {
    return moves[scores.indexOf(Math.max.apply(null, scores))];
  }
  if(depth%2 === 0) {
    return Math.max.apply(null, scores);
  }
  return Math.min.apply(null, scores);
};

const getAvaliableMoves = function(gameGrid) {
  var moves = [];
  for(let i = 0; i < gameGrid.length; i++) {
    for(let j = 0; j < gameGrid[i].length; j++) {
       if(gameGrid[i][j] === "") {
         moves.push({x: i, y: j});
       }
     }
  }
  return moves;
};

const getRandomElement = function (array) {
  return array[Math.floor(Math.random()*array.length)];
};

const simulateMove = function(game, {x, y}) {
  var newGameState;
  if(game.gameGrid[x][y] !== "") {
    throw {type: "WTF?"};
  }
  newGameState = JSON.parse(JSON.stringify(game));
  newGameState.gameGrid[x][y] = newGameState.currentTurn;
  newGameState.currentTurn = newGameState.currentTurn === "x" ? "o" : "x";
  return newGameState;
};

export default game;
<commit_msg>Remove AI functions and move some functional to gameTick<commit_after>import { APPLY_MOVE, RESET_GAME,
  SET_GAME_MODE, START_NEXT_MATCH } from "../actions/GameActions.js";

export const VS_HUMAN = "VS_HUMAN";
export const EASY = "EASY";
export const MEDIUM = "MEDIUM";
export const HARD = "HARD";

export const initialGameState = function() {
  return {
    gameGrid: [["","",""],
               ["","",""],
               ["","",""]],
    currentTurn: "x",
    victoryStatistics: {
      x: 0,
      o: 0
    },
    gameMode: -1,
    AI: {
      playingX: false
    },
    gameOver: false
  };
};

const game = function (state = initialGameState(), action) {
  switch (action.type) {
    case APPLY_MOVE:
      return applyMove(state, action.x, action.y);
    case START_NEXT_MATCH:
      return startNextMatch(state);
    case SET_GAME_MODE:
      return setGameMode(state, action.mode);
    case RESET_GAME:
      return initialGameState();
    default:
      return state;
  }
};

const setGameMode = function (state, mode) {
  return Object.assign({}, state, {
    gameMode: mode
  });
}

const applyMove = function (state, x, y) {
  var newState = Object.assign({}, state, {
    gameGrid: state.gameGrid.map(a => a.slice())
  });
  newState.gameGrid[x][y] = newState.currentTurn;
  newState.currentTurn = newState.currentTurn === "x" ? "o": "x";

  var winner = checkBoard(newState.gameGrid);
  if(winner) {
    newState.gameOver = true;
    newState.victoryStatistics = {
      x: state.victoryStatistics.x,
      o: state.victoryStatistics.o
    };
    if(winner === "tie") {
      return newState;
    }
    newState.victoryStatistics[winner]++;
  }
  return newState;
};

const startNextMatch = function (state) {
  return Object.assign({}, initialGameState(), {
    gameMode: state.gameMode,
    victoryStatistics: state.victoryStatistics,
    AI : {
      playingX: !state.AI.playingX
    }
  });
}

// check whether someone won or not
export const checkBoard = function (board) {
  var winner;

  //check rows
  winner = checkRows(board);
  if(winner) {
    return winner;
  }

  // check columns
  winner = checkColumns(board);
  if(winner) {
    return winner;
  }

  // check diagonals
  winner = checkDiagonals(board);
  if(winner) {
    return winner;
  }

  // check if there's still empty tiles
  if(isBoardFull(board)) {
    return "tie";
  }

  return "";
};

const checkRows = function (b) {
  for (let i = 0; i < b.length; i++) {
    if(b[i][0] === b[i][1] && b[i][1] === b[i][2] && b[i][0] !== "")
      return b[i][0];
  }
};

const checkColumns = function (b) {
  for (let i = 0; i < b.length; i++) {
    if(b[0][i] === b[1][i] && b[1][i] === b[2][i] && b[0][i] !== "")
      return b[0][i];
  }
}

const checkDiagonals = function (b) {
  if((b[0][0] === b[1][1] && b[1][1] === b[2][2] ||
      b[0][2] === b[1][1] && b[1][1] === b[2][0]) &&
      b[1][1] !== "") {
    return b[1][1];
  }
}

const isBoardFull = function (b) {
  return !b.some(row => row.some(elem => elem === ""));
}

export default game;
<|endoftext|>


<commit_before>/**
 * @module
 * @prototype {Function} __render
 */

import { SmoothScrollbar } from '../smooth_scrollbar';

export { SmoothScrollbar };

function nextTick(options, current, movement) {
    const { friction } = options;

    if (Math.abs(movement) < 1) {
        let next = current + movement;

        return {
            movement: 0,
            position: movement > 0 ? Math.ceil(next) : Math.floor(next)
        };
    }

    let q = 1 - friction / 100;

    return {
        movement: movement * q,
        position: current + movement * (1 - q)
    };
};

function nextTick(options, current, movement) {
    const { friction, renderByPixels } = options;

    let q = 1 - friction / 100;

    let nextMovement, nextPosition;

    if (renderByPixels) {
        nextMovement = movement * q | 0;
        nextPosition = current + movement - nextMovement;
    } else {
        if (Math.abs(movement) < 0.1) {
            nextMovement = 0;
            nextPosition = movement > 0 ?
                            Math.ceil(current + movement) :
                            Math.floor(current + movement);
        } else {
            nextMovement = movement * q;
            nextPosition = current + movement - nextMovement;
        }
    }

    return {
        movement: nextMovement,
        position: current + movement - nextMovement
    };
};

function __render() {
    const {
        options,
        offset,
        movement,
        __timerID
    } = this;

    if (movement.x || movement.y) {
        let nextX = nextTick(options, offset.x, movement.x);
        let nextY = nextTick(options, offset.y, movement.y);

        movement.x = nextX.movement;
        movement.y = nextY.movement;

        this.setPosition(nextX.position, nextY.position);
    }

    __timerID.render = requestAnimationFrame(this::__render);

};

Object.defineProperty(SmoothScrollbar.prototype, '__render', {
    value: __render,
    writable: true,
    configurable: true
});<commit_msg>remove(render): remove duplicate code<commit_after>/**
 * @module
 * @prototype {Function} __render
 */

import { SmoothScrollbar } from '../smooth_scrollbar';

export { SmoothScrollbar };

function nextTick(options, current, movement) {
    const { friction, renderByPixels } = options;

    let q = 1 - friction / 100;

    let nextMovement, nextPosition;

    if (renderByPixels) {
        nextMovement = movement * q | 0;
        nextPosition = current + movement - nextMovement;
    } else {
        if (Math.abs(movement) < 0.1) {
            nextMovement = 0;
            nextPosition = movement > 0 ?
                            Math.ceil(current + movement) :
                            Math.floor(current + movement);
        } else {
            nextMovement = movement * q;
            nextPosition = current + movement - nextMovement;
        }
    }

    return {
        movement: nextMovement,
        position: current + movement - nextMovement
    };
};

function __render() {
    const {
        options,
        offset,
        movement,
        __timerID
    } = this;

    if (movement.x || movement.y) {
        let nextX = nextTick(options, offset.x, movement.x);
        let nextY = nextTick(options, offset.y, movement.y);

        movement.x = nextX.movement;
        movement.y = nextY.movement;

        this.setPosition(nextX.position, nextY.position);
    }

    __timerID.render = requestAnimationFrame(this::__render);

};

Object.defineProperty(SmoothScrollbar.prototype, '__render', {
    value: __render,
    writable: true,
    configurable: true
});<|endoftext|>