<commit_before>//__BEGIN_LICENSE__
//Copyright (c) 2015, United States Government, as represented by the
//Administrator of the National Aeronautics and Space Administration.
//All rights reserved.

//The xGDS platform is licensed under the Apache License, Version 2.0
//(the "License"); you may not use this file except in compliance with the License.
//You may obtain a copy of the License at
//http://www.apache.org/licenses/LICENSE-2.0.

//Unless required by applicable law or agreed to in writing, software distributed
//under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
//CONDITIONS OF ANY KIND, either express or implied. See the License for the
//specific language governing permissions and limitations under the License.
//__END_LICENSE__

var xgds_video = xgds_video || {};
$.extend(xgds_video,{
	createSliderLegend: function(isResizing) {
		/**
		 * Create a slider ribbon that shows breaks between segments
		 */
		var ribbon = $("#ribbon");
		if (_.isUndefined(xgds_video.options.resizeSlider)){
			$( window ).resize(function() {
				xgds_video.createSliderLegend(true);
			});
			xgds_video.options.resizeSlider = true;
		}

		var singleSource = (Object.keys(xgds_video.options.displaySegments).length == 1);
		for (var sourceName in xgds_video.options.displaySegments) {
			var segments = xgds_video.options.displaySegments[sourceName];
			var source = segments[0].source;
			var dividerName = "divider-" + source.shortName;
			if (isResizing){
				$("#ribbon").find("#"+dividerName).remove();
			}
			var labels = {};
			var endPointCheck = true;
			$.each(segments, function(id) {
				var segment = segments[id];
				if (!segment.endTime) {
					endPointCheck = false;
					return false;
				}
			});
			if (!endPointCheck) {
				return;
			}
			//get the total slider range in seconds
			var firstSegmentStartMoment = moment(segments[0].startTime);
			var episodeStartMoment = singleSource ? firstSegmentStartMoment : moment(xgds_video.options.episode.startTime);
			var episodeEndMoment = singleSource ? moment(segments[segments.length - 1].endTime) : moment(xgds_video.options.episode.endTime);
			var totalDuration = episodeEndMoment.diff(episodeStartMoment, 'seconds');
			var color = source.displayColor;
			if (color == '') {
				//assign a random color
				color = '#' + (Math.random() * 0xFFFFFF << 0).toString(16);
			}

			var fullWidth = $("#masterSlider").width();
			var sourceRibbon = $('
' + source.shortName  + '');
			sourceRibbon.appendTo(ribbon)

			if (!singleSource){
				//handle empty space in front of first segment
				var emptySegmentDuration = firstSegmentStartMoment.diff(episodeStartMoment, 'seconds');
				var emptySegmentWidth = Math.round(fullWidth * (emptySegmentDuration / totalDuration));
				var emptySegmentHTML = '';
				sourceRibbon.append(emptySegmentHTML);
			}
			//for each video segment
			$.each(segments, function(id) {
				var segment = segments[id];
				var source = segment.source;
				var segDuration = 0;
				var width = 0;
				//get the duration of the video segment
				var segmentStartMoment = moment(segment.startTime);
				var segmentEndMoment = moment(segment.endTime);
				segDuration = segmentEndMoment.diff(segmentStartMoment, 'seconds');
				width = fullWidth * (segDuration / totalDuration);
				width = Math.round(width);
				//draw the visualization
				var htmlNugget = '';
				sourceRibbon.append(htmlNugget);
				if ((id + 1) < segments.length) { //if there is a next segment
					var nextSegment = segments[id + 1];
					var nextSegmentStartMoment = moment(nextSegment.startTime);
					var gapTime = nextSegmentStartMoment.diff(segmentEndMoment, 'seconds');
					emptySegmentWidth = Math.round(fullWidth * (gapTime / totalDuration));
					var htmlNugget2 = '';
					sourceRibbon.append(htmlNugget2);
				}
			});

		}
		var leftMargin = 0;
		var leftPadding = 0;
		var labels = $(".sourceLabel");
		for (var i=0; i<labels.length; i++){
			var l = $(labels[i]);
			if (l.width() > leftMargin) {
				leftMargin = l.width();
				leftPadding = parseInt(l.css('margin-right'));
			}
		}
		if (leftMargin > 0){
			var value = leftMargin + leftPadding;
			$("#masterSlider").css("margin-left", value + 'px');
		}
	},

	getPercent: function(width, totalWidth) {
		return Math.round(width / totalWidth * 100);
	},

	uponSliderMoveCallBack: function(event, ui) {
		/**
		 * Slider Callback:
		 * update slider time text when moving slider.
		 */
		//update slider time label on top
		xgds_video.options.movingSlider = true;
		xgds_video.setSliderTimeLabel(moment.unix(ui.value));
	},

	getSlider: function() {
              if ($(xgds_video.masterSlider).length == 0) {
                 xgds_video.masterSlider = $("#masterSlider");
              }
              return $(xgds_video.masterSlider);
        },
        uponSliderStopCallBack: function(event, ui) {
		/**
		 * Slider Callback:
		 *    get the current slider position and do
		 *    offset = slider position - each video's start time
		 *    seek each video at offset. (means each video's offset will be different,
		 *    but their test site time same)
		 *    update the test site times to equal slider position.
		 */
		xgds_video.options.seekFlag = true;
		var currTime = xgds_video.getSlider().slider('value'); //in seconds
		currTime = new moment(currTime * 1000).tz(xgds_video.options.timeZone); // why this did not use the default time zone I do not know
		var jumped = xgds_video.jumpToPosition(currTime);
		if (jumped == 'unplayable') {
			if (xgds_video.options.delayed_live) {
				xgds_video.nowButtonCallback();
			}
		}

		//XXX take care of the case where seek time is not within playable range.
		//then go to the nearest available segment and play from there.
		xgds_video.options.movingSlider = false;
	},
	setupSlider: function() {
		/**
		 * initialize master slider with range (episode start time->episode end time)
		 */
		var keysLength = (Object.keys(xgds_video.options.displaySegments).length);
		if (keysLength == 0){
			return;
		}
		var singleSource = (keysLength == 1);

		var startMoment = undefined;
		var endMoment = undefined;
		var endTime = undefined;
		if (!singleSource) {
			// use the episode
			startMoment = moment(xgds_video.options.episode.startTime);
			endTime = xgds_video.options.episode.endTime ? xgds_video.options.episode.endTime : xgds_video.options.lastSegment.endTime;
			endMoment = moment(endTime);
		} else {
			// use the segments from the single source
			var segments = xgds_video.options.displaySegments[Object.keys(xgds_video.options.displaySegments)[0]];
			startMoment = moment(segments[0].startTime);
			endTime = xgds_video.options.lastSegment.endTime;
			endMoment = moment(endTime);
		}
		//var duration = endMoment.diff(startMoment, 'seconds');
                if (xgds_video.options.delayed_live) {
                       xgds_video.masterSlider = playback.masterSlider;
                       xgds_video.masterSlider.on("slidestop", xgds_video.uponSliderStopCallBack);
                       xgds_video.masterSlider.on("slide", xgds_video.uponSliderMoveCallBack);
                }	else if (endTime !== undefined) {
                        xgds_video.masterSlider = $('#masterSlider').slider({
                                step: 1,
                                min: startMoment.unix(), //moment(xgds_video.options.firstSegment.startTime).unix(),
                                max: endMoment.unix(),
                                stop: xgds_video.uponSliderStopCallBack,
                                slide: xgds_video.uponSliderMoveCallBack,
                                range: 'min'
                        });
                        xgds_video.setSliderTimeLabel(startMoment);
                        xgds_video.createSliderLegend(false);
                } else {
			alert('The end time of a video segment is not available.' +
			'Cannot setup slider');
		}
	},
	setSliderTime:function(datetime) {
		//update the slider
		var seconds = Math.round(datetime.unix());
		xgds_video.getSlider().slider('value', seconds);
		xgds_video.setSliderTimeLabel(datetime);
		// if we are in delayed live we also want to update playback current time
		if (xgds_video.options.delayed_live){
			// we don't want to have a callback loop
			//playback.setCurrentTime(datetime);

			playback.currentTime = moment(datetime).tz(playback.displayTZ);
		}
	}

});
<commit_msg>if slider is already set up do not make another<commit_after>//__BEGIN_LICENSE__
//Copyright (c) 2015, United States Government, as represented by the
//Administrator of the National Aeronautics and Space Administration.
//All rights reserved.

//The xGDS platform is licensed under the Apache License, Version 2.0
//(the "License"); you may not use this file except in compliance with the License.
//You may obtain a copy of the License at
//http://www.apache.org/licenses/LICENSE-2.0.

//Unless required by applicable law or agreed to in writing, software distributed
//under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
//CONDITIONS OF ANY KIND, either express or implied. See the License for the
//specific language governing permissions and limitations under the License.
//__END_LICENSE__

var xgds_video = xgds_video || {};
$.extend(xgds_video,{
	createSliderLegend: function(isResizing) {
		/**
		 * Create a slider ribbon that shows breaks between segments
		 */
		var ribbon = $("#ribbon");
		if (_.isUndefined(xgds_video.options.resizeSlider)){
			$( window ).resize(function() {
				xgds_video.createSliderLegend(true);
			});
			xgds_video.options.resizeSlider = true;
		}

		var singleSource = (Object.keys(xgds_video.options.displaySegments).length == 1);
		for (var sourceName in xgds_video.options.displaySegments) {
			var segments = xgds_video.options.displaySegments[sourceName];
			var source = segments[0].source;
			var dividerName = "divider-" + source.shortName;
			if (isResizing){
				$("#ribbon").find("#"+dividerName).remove();
			}
			var labels = {};
			var endPointCheck = true;
			$.each(segments, function(id) {
				var segment = segments[id];
				if (!segment.endTime) {
					endPointCheck = false;
					return false;
				}
			});
			if (!endPointCheck) {
				return;
			}
			//get the total slider range in seconds
			var firstSegmentStartMoment = moment(segments[0].startTime);
			var episodeStartMoment = singleSource ? firstSegmentStartMoment : moment(xgds_video.options.episode.startTime);
			var episodeEndMoment = singleSource ? moment(segments[segments.length - 1].endTime) : moment(xgds_video.options.episode.endTime);
			var totalDuration = episodeEndMoment.diff(episodeStartMoment, 'seconds');
			var color = source.displayColor;
			if (color == '') {
				//assign a random color
				color = '#' + (Math.random() * 0xFFFFFF << 0).toString(16);
			}

			var fullWidth = $("#masterSlider").width();
			var sourceRibbon = $('
' + source.shortName  + '');
			sourceRibbon.appendTo(ribbon)

			if (!singleSource){
				//handle empty space in front of first segment
				var emptySegmentDuration = firstSegmentStartMoment.diff(episodeStartMoment, 'seconds');
				var emptySegmentWidth = Math.round(fullWidth * (emptySegmentDuration / totalDuration));
				var emptySegmentHTML = '';
				sourceRibbon.append(emptySegmentHTML);
			}
			//for each video segment
			$.each(segments, function(id) {
				var segment = segments[id];
				var source = segment.source;
				var segDuration = 0;
				var width = 0;
				//get the duration of the video segment
				var segmentStartMoment = moment(segment.startTime);
				var segmentEndMoment = moment(segment.endTime);
				segDuration = segmentEndMoment.diff(segmentStartMoment, 'seconds');
				width = fullWidth * (segDuration / totalDuration);
				width = Math.round(width);
				//draw the visualization
				var htmlNugget = '';
				sourceRibbon.append(htmlNugget);
				if ((id + 1) < segments.length) { //if there is a next segment
					var nextSegment = segments[id + 1];
					var nextSegmentStartMoment = moment(nextSegment.startTime);
					var gapTime = nextSegmentStartMoment.diff(segmentEndMoment, 'seconds');
					emptySegmentWidth = Math.round(fullWidth * (gapTime / totalDuration));
					var htmlNugget2 = '';
					sourceRibbon.append(htmlNugget2);
				}
			});

		}
		var leftMargin = 0;
		var leftPadding = 0;
		var labels = $(".sourceLabel");
		for (var i=0; i<labels.length; i++){
			var l = $(labels[i]);
			if (l.width() > leftMargin) {
				leftMargin = l.width();
				leftPadding = parseInt(l.css('margin-right'));
			}
		}
		if (leftMargin > 0){
			var value = leftMargin + leftPadding;
			$("#masterSlider").css("margin-left", value + 'px');
		}
	},

	getPercent: function(width, totalWidth) {
		return Math.round(width / totalWidth * 100);
	},

	uponSliderMoveCallBack: function(event, ui) {
		/**
		 * Slider Callback:
		 * update slider time text when moving slider.
		 */
		//update slider time label on top
		xgds_video.options.movingSlider = true;
		xgds_video.setSliderTimeLabel(moment.unix(ui.value));
	},

	getSlider: function() {
              if ($(xgds_video.masterSlider).length == 0) {
                 xgds_video.masterSlider = $("#masterSlider");
              }
              return $(xgds_video.masterSlider);
        },
        uponSliderStopCallBack: function(event, ui) {
		/**
		 * Slider Callback:
		 *    get the current slider position and do
		 *    offset = slider position - each video's start time
		 *    seek each video at offset. (means each video's offset will be different,
		 *    but their test site time same)
		 *    update the test site times to equal slider position.
		 */
		xgds_video.options.seekFlag = true;
		var currTime = xgds_video.getSlider().slider('value'); //in seconds
		currTime = new moment(currTime * 1000).tz(xgds_video.options.timeZone); // why this did not use the default time zone I do not know
		var jumped = xgds_video.jumpToPosition(currTime);
		if (jumped == 'unplayable') {
			if (xgds_video.options.delayed_live) {
				xgds_video.nowButtonCallback();
			}
		}

		//XXX take care of the case where seek time is not within playable range.
		//then go to the nearest available segment and play from there.
		xgds_video.options.movingSlider = false;
	},
	setupSlider: function() {
		/**
		 * initialize master slider with range (episode start time->episode end time)
		 */
		var keysLength = (Object.keys(xgds_video.options.displaySegments).length);
		if (keysLength == 0){
			return;
		}
		var singleSource = (keysLength == 1);

		var startMoment = undefined;
		var endMoment = undefined;
		var endTime = undefined;
		if (!singleSource) {
			// use the episode
			startMoment = moment(xgds_video.options.episode.startTime);
			endTime = xgds_video.options.episode.endTime ? xgds_video.options.episode.endTime : xgds_video.options.lastSegment.endTime;
			endMoment = moment(endTime);
		} else {
			// use the segments from the single source
			var segments = xgds_video.options.displaySegments[Object.keys(xgds_video.options.displaySegments)[0]];
			startMoment = moment(segments[0].startTime);
			endTime = xgds_video.options.lastSegment.endTime;
			endMoment = moment(endTime);
		}
		//var duration = endMoment.diff(startMoment, 'seconds');
                if (xgds_video.options.delayed_live) {
                       xgds_video.masterSlider = playback.masterSlider;
                       xgds_video.masterSlider.on("slidestop", xgds_video.uponSliderStopCallBack);
                       xgds_video.masterSlider.on("slide", xgds_video.uponSliderMoveCallBack);
                }	else if (endTime !== undefined) {
                		if (!_.isUndefined(playback.masterSlider)) {
                			xgds_video.masterSlider = playback.masterSlider;
						} else {
							xgds_video.masterSlider = $('#masterSlider').slider({
								step: 1,
								min: startMoment.unix(), //moment(xgds_video.options.firstSegment.startTime).unix(),
								max: endMoment.unix(),
								stop: xgds_video.uponSliderStopCallBack,
								slide: xgds_video.uponSliderMoveCallBack,
								range: 'min'
							});
							xgds_video.setSliderTimeLabel(startMoment);
							xgds_video.createSliderLegend(false);
						}
                } else {
			alert('The end time of a video segment is not available.' +
			'Cannot setup slider');
		}
	},
	setSliderTime:function(datetime) {
		//update the slider
		var seconds = Math.round(datetime.unix());
		xgds_video.getSlider().slider('value', seconds);
		xgds_video.setSliderTimeLabel(datetime);
		// if we are in delayed live we also want to update playback current time
		if (xgds_video.options.delayed_live){
			// we don't want to have a callback loop
			//playback.setCurrentTime(datetime);

			playback.currentTime = moment(datetime).tz(playback.displayTZ);
		}
	}

});
<|endoftext|>


<commit_before>import { Vector2 } from '../math/Vector2.js';
import { Vector3 } from '../math/Vector3.js';
import { Matrix4 } from '../math/Matrix4.js';
import { Object3D } from '../core/Object3D.js';
import { SpriteMaterial } from '../materials/SpriteMaterial.js';

/**
 * @author mikael emtinger / http://gomo.se/
 * @author alteredq / http://alteredqualia.com/
 */

function Sprite( material ) {

	Object3D.call( this );

	this.type = 'Sprite';

	this.material = ( material !== undefined ) ? material : new SpriteMaterial();

	this.center = new Vector2( 0.5, 0.5 );

}

Sprite.prototype = Object.assign( Object.create( Object3D.prototype ), {

	constructor: Sprite,

	isSprite: true,

	raycast: ( function () {

		var intersectPoint = new Vector3();
		var worldPosition = new Vector3();
		var worldScale = new Vector3();

		var alignedPosition = new Vector2();
		var rotatedPosition = new Vector2();
		var matrixWorldInverse = new Matrix4();

		return function raycast( raycaster, intersects ) {

			worldScale.setFromMatrixScale( this.matrixWorld );

			// compute position in camera space
			alignedPosition.set( ( 0.5 - this.center.x ) * worldScale.x, ( 0.5 - this.center.y ) * worldScale.y );

			var rotation = this.material.rotation;
			if ( rotation !== 0 ) {
				var cos = Math.cos( rotation ), sin = Math.sin( rotation );
				rotatedPosition.x = ( cos * alignedPosition.x ) - ( sin * alignedPosition.y );
				rotatedPosition.y = ( sin * alignedPosition.x ) + ( cos * alignedPosition.y );
			} else {
				rotatedPosition.copy( alignedPosition );
			}

			worldPosition.setFromMatrixPosition( this.modelViewMatrix );
			worldPosition.x += rotatedPosition.x;
			worldPosition.y += rotatedPosition.y;

			// transform to world space
			worldPosition.applyMatrix4( matrixWorldInverse.getInverse( this.modelViewMatrix ) ).applyMatrix4( this.matrixWorld );

			raycaster.ray.closestPointToPoint( worldPosition, intersectPoint );

			var guessSizeSq = worldScale.x * worldScale.y / 4;

			if ( worldPosition.distanceToSquared( intersectPoint ) > guessSizeSq ) return;

			var distance = raycaster.ray.origin.distanceTo( intersectPoint );

			if ( distance < raycaster.near || distance > raycaster.far ) return;

			intersects.push( {

				distance: distance,
				point: intersectPoint.clone(),
				face: null,
				object: this

			} );

		};

	}() ),

	clone: function () {

		return new this.constructor( this.material ).copy( this );

	},

	copy: function ( source ) {

		Object3D.prototype.copy.call( this, source );

		if ( source.center !== undefined ) this.center.copy( source.center );

		return this;

	}

} );

export { Sprite };
<commit_msg>fix lint<commit_after>import { Vector2 } from '../math/Vector2.js';
import { Vector3 } from '../math/Vector3.js';
import { Matrix4 } from '../math/Matrix4.js';
import { Object3D } from '../core/Object3D.js';
import { SpriteMaterial } from '../materials/SpriteMaterial.js';

/**
 * @author mikael emtinger / http://gomo.se/
 * @author alteredq / http://alteredqualia.com/
 */

function Sprite( material ) {

	Object3D.call( this );

	this.type = 'Sprite';

	this.material = ( material !== undefined ) ? material : new SpriteMaterial();

	this.center = new Vector2( 0.5, 0.5 );

}

Sprite.prototype = Object.assign( Object.create( Object3D.prototype ), {

	constructor: Sprite,

	isSprite: true,

	raycast: ( function () {

		var intersectPoint = new Vector3();
		var worldPosition = new Vector3();
		var worldScale = new Vector3();

		var alignedPosition = new Vector2();
		var rotatedPosition = new Vector2();
		var matrixWorldInverse = new Matrix4();

		return function raycast( raycaster, intersects ) {

			worldScale.setFromMatrixScale( this.matrixWorld );

			// compute position in camera space
			alignedPosition.set( ( 0.5 - this.center.x ) * worldScale.x, ( 0.5 - this.center.y ) * worldScale.y );

			var rotation = this.material.rotation;
			if ( rotation !== 0 ) {

				var cos = Math.cos( rotation ), sin = Math.sin( rotation );
				rotatedPosition.x = ( cos * alignedPosition.x ) - ( sin * alignedPosition.y );
				rotatedPosition.y = ( sin * alignedPosition.x ) + ( cos * alignedPosition.y );

			} else {

				rotatedPosition.copy( alignedPosition );

			}

			worldPosition.setFromMatrixPosition( this.modelViewMatrix );
			worldPosition.x += rotatedPosition.x;
			worldPosition.y += rotatedPosition.y;

			// transform to world space
			worldPosition.applyMatrix4( matrixWorldInverse.getInverse( this.modelViewMatrix ) ).applyMatrix4( this.matrixWorld );

			raycaster.ray.closestPointToPoint( worldPosition, intersectPoint );

			var guessSizeSq = worldScale.x * worldScale.y / 4;

			if ( worldPosition.distanceToSquared( intersectPoint ) > guessSizeSq ) return;

			var distance = raycaster.ray.origin.distanceTo( intersectPoint );

			if ( distance < raycaster.near || distance > raycaster.far ) return;

			intersects.push( {

				distance: distance,
				point: intersectPoint.clone(),
				face: null,
				object: this

			} );

		};

	}() ),

	clone: function () {

		return new this.constructor( this.material ).copy( this );

	},

	copy: function ( source ) {

		Object3D.prototype.copy.call( this, source );

		if ( source.center !== undefined ) this.center.copy( source.center );

		return this;

	}

} );

export { Sprite };
<|endoftext|>


<commit_before>import { ASSETS, COLLISION_GROUPS, MATERIALS, PHYSICS } from 'constants';
import { getCollisionGroup, getMaterial } from 'lib/utils';

export default class Target extends Phaser.Sprite {
  constructor(game, x, y) {
    const { world } = game;

    super(game, x, y, ASSETS.TARGET);

    game.physics.p2.enable(this, true);

    this.body.clearShapes();
    this.body.loadPolygon(PHYSICS, 'target');

    this.body.static = true;
    game.debug.body(this);

    this.inputEnabled = true;
    this.events.onInputDown.add(this.listener, this);

    // Material
    const targetMaterial = getMaterial(game, MATERIALS.TARGET);
    this.body.setMaterial(targetMaterial);

    // Collision Group
    const targetGroup = getCollisionGroup(game, COLLISION_GROUPS.TARGET);
    const bulletGroup = getCollisionGroup(game, COLLISION_GROUPS.BULLET);

    this.body.setCollisionGroup(targetGroup);
    this.body.collides(bulletGroup);
  }

  listener (sprite, pointer) {
    if (pointer.button === Phaser.Mouse.RIGHT_BUTTON) {
      this.body.angle -= 45;
      return;
    }

    this.body.angle += 45;
  }
}

<commit_msg>Fix targets to only rotate 90 degrees.<commit_after>import { ASSETS, COLLISION_GROUPS, MATERIALS, PHYSICS } from 'constants';
import { getCollisionGroup, getMaterial } from 'lib/utils';
import fp from 'lodash/fp';

const possibleAngles = [45, 135, 225, 315];

export default class Target extends Phaser.Sprite {
  constructor(game, x, y) {
    const { world } = game;

    super(game, x, y, ASSETS.TARGET);

    game.physics.p2.enable(this, true);

    // Set random angle
    this.body.angle = fp.sample(possibleAngles);

    this.body.clearShapes();
    this.body.loadPolygon(PHYSICS, 'target');

    this.body.static = true;
    game.debug.body(this);

    this.inputEnabled = true;
    this.events.onInputDown.add(this.listener, this);

    // Material
    const targetMaterial = getMaterial(game, MATERIALS.TARGET);
    this.body.setMaterial(targetMaterial);

    // Collision Group
    const targetGroup = getCollisionGroup(game, COLLISION_GROUPS.TARGET);
    const bulletGroup = getCollisionGroup(game, COLLISION_GROUPS.BULLET);

    this.body.setCollisionGroup(targetGroup);
    this.body.collides(bulletGroup);
  }

  listener (sprite, pointer) {
    if (pointer.button === Phaser.Mouse.LEFT_BUTTON) {
      this.body.angle += 90;
    }
  }
}
<|endoftext|>


<commit_before>define([],
  function() {

    function typeOf(obj) {
      // summary:
      //   A better type then Object.toString() or typeof.
      // description:
      //      toType(undefined); //"undefined"
      //      toType(new); //"null"
      //      toType({a: 4}); //"object"
      //      toType([1, 2, 3]); //"array"
      //      (function() {console.log(toType(arguments))})(); //arguments
      //      toType(new ReferenceError); //"error"
      //      toType(new Date); //"date"
      //      toType(/a-z/); //"regexp"
      //      toType(Math); //"math"
      //      toType(JSON); //"json"
      //      toType(new Number(4)); //"number"
      //      toType(new String("abc")); //"string"
      //      toType(new Boolean(true)); //"boolean"

      /* based on
       http://javascriptweblog.wordpress.com/2011/08/08/fixing-the-javascript-typeof-operator/
       */

      var result = Object.prototype.toString.call(obj).match(/\s([a-zA-Z]+)/)[1].toLowerCase();
      // on some browsers, the main window returns as "global" (WebKit) or "window" (FF), but this is an object too
      if (result === "global" || result == "window") {
        result = "object";
      }
      return result; // return String
    }

    function getPrototypeChain(/*Object*/ obj) {
      // summary:
      //   Returns an array where result[i] is the prototype
      //   of result[i-1], with obj === result[0]. result[length]
      //   is thus Object.prototype.

      function recursive(acc, o) {
        if (!o) {
          return acc;
        }
        else {
          acc.push(o);
          return recursive(acc, Object.getPrototypeOf(o));
        }
      }

      return recursive([], obj);
    }

    function getAllKeys(/*Object*/ obj) {
      // summary:
      //   Returns an array containing the names of all given objects enumerable properties.
      //   Like keys, but for the entire prototype chain. The array starts with the properties
      //   of Object.prototype, and works down the chain.

      return getPrototypeChain(obj).reduceRight(
        function(acc, proto) {
          return acc.concat(Object.keys(proto));
        },
        []
      );
    }

    function isInt(/*Number*/ n) {
      // summary:
      //   True if n is an integer.
      //   n must be a number.

      return n % 1 === 0;
    }

    var js = {
      // summary:
      //   Methods to aid with the JavaScript language.
      typeOf: typeOf,
      getPrototypeChain: getPrototypeChain,
      getAllKeys: getAllKeys,
      isInt: isInt
    };

    return js;
  }
);
<commit_msg>nub<commit_after>define([],
  function() {

    function typeOf(obj) {
      // summary:
      //   A better type then Object.toString() or typeof.
      // description:
      //      toType(undefined); //"undefined"
      //      toType(new); //"null"
      //      toType({a: 4}); //"object"
      //      toType([1, 2, 3]); //"array"
      //      (function() {console.log(toType(arguments))})(); //arguments
      //      toType(new ReferenceError); //"error"
      //      toType(new Date); //"date"
      //      toType(/a-z/); //"regexp"
      //      toType(Math); //"math"
      //      toType(JSON); //"json"
      //      toType(new Number(4)); //"number"
      //      toType(new String("abc")); //"string"
      //      toType(new Boolean(true)); //"boolean"

      /* based on
       http://javascriptweblog.wordpress.com/2011/08/08/fixing-the-javascript-typeof-operator/
       */

      var result = Object.prototype.toString.call(obj).match(/\s([a-zA-Z]+)/)[1].toLowerCase();
      // on some browsers, the main window returns as "global" (WebKit) or "window" (FF), but this is an object too
      if (result === "global" || result == "window") {
        result = "object";
      }
      return result; // return String
    }

    function getPrototypeChain(/*Object*/ obj) {
      // summary:
      //   Returns an array where result[i] is the prototype
      //   of result[i-1], with obj === result[0]. result[length]
      //   is thus Object.prototype.

      function recursive(acc, o) {
        if (!o) {
          return acc;
        }
        else {
          acc.push(o);
          return recursive(acc, Object.getPrototypeOf(o));
        }
      }

      return recursive([], obj);
    }

    function getAllKeys(/*Object*/ obj) {
      // summary:
      //   Returns an array containing the names of all given objects enumerable properties.
      //   Like keys, but for the entire prototype chain. The array starts with the properties
      //   of Object.prototype, and works down the chain.

      return getPrototypeChain(obj).reduceRight(
        function(acc, proto) {
          return acc.concat(Object.keys(proto));
        },
        []
      );
    }

    function isInt(/*Number*/ n) {
      // summary:
      //   True if n is an integer.
      //   n must be a number.

      return n % 1 === 0;
    }

    function nub(/*Array*/ array, /*Boolean?*/ removeNull) {
      // summary:
      //   Returns an array that has all the elements of `array`, in order, but with later duplicates removed.
      //   If `removeNull` is given and thruthy, nulls and undefineds are removed too.

      if (!array) {
        return array;
      }
      var result = array.reduce(
        function(acc, el) {
          if ((!removeNull || el) && acc.indexOf(el) < 0) {
            acc.push(el);
          }
          return acc;
        },
        []
      );
      return result;
    }

    var js = {
      // summary:
      //   Methods to aid with the JavaScript language.
      typeOf: typeOf,
      getPrototypeChain: getPrototypeChain,
      getAllKeys: getAllKeys,
      isInt: isInt,
      nub: nub
    };

    return js;
  }
);
<|endoftext|>


<commit_before>import { userExpired, userFound, loadingUser, loadUserError } from './actions';
import { USER_EXPIRED, LOADING_USER, USER_FOUND } from './constants';

// store the user here to prevent future promise calls to getUser()
export let storedUser = null;
// store the next middleware here so it can be accessed by the getUserCallback
export let nextMiddleware = null;

// helper function to set the stored next middleware (for testing)
export function setNext(newNext) {
  nextMiddleware = newNext;
}

// a function to get the next middleware (for testing)
export function getNext() {
  return nextMiddleware;
}

// helper function to set the stored user manually (for testing)
export function setStoredUser(user) {
  storedUser = user;
}

// helper function to remove the stored user manually (for testing)
export function removeStoredUser() {
  storedUser = null;
}

// callback function to the userManager's getUser
export function getUserCallback(user) {
  if (!user || user.expired) {
    nextMiddleware(userExpired());
  } else {
    storedUser = user;
    nextMiddleware(userFound(user));
  }
}

// callback for the userManager's getUser.catch
export function errorCallback(error) {
  console.error(`redux-oidc: Error loading user in oidcMiddleware: ${error.message}`);
  nextMiddleware(loadUserError());
}

// the middleware handler function
export function middlewareHandler(next, action, userManager) {
  // prevent an infinite loop of dispatches of these action types (issue #30 & #63)
  if (action.type === USER_EXPIRED || action.type === LOADING_USER || action.type === USER_FOUND) {
    return next(action);
  }

  nextMiddleware = next;

  if (!storedUser || storedUser.expired) {
    next(loadingUser());
    userManager.getUser()
      .then(getUserCallback)
      .catch(errorCallback);
  }
  return next(action);
}

// the middleware creator function
export default function createOidcMiddleware(userManager) {
  if (!userManager || !userManager.getUser) {
    throw new Error('You must provide a user manager!');
  }

  // the middleware
  return (store) => (next) => (action) => {
    middlewareHandler(next, action, userManager);
  }
};
<commit_msg>Return the value returned by the middlewareHandler<commit_after>import { userExpired, userFound, loadingUser, loadUserError } from './actions';
import { USER_EXPIRED, LOADING_USER, USER_FOUND } from './constants';

// store the user here to prevent future promise calls to getUser()
export let storedUser = null;
// store the next middleware here so it can be accessed by the getUserCallback
export let nextMiddleware = null;

// helper function to set the stored next middleware (for testing)
export function setNext(newNext) {
  nextMiddleware = newNext;
}

// a function to get the next middleware (for testing)
export function getNext() {
  return nextMiddleware;
}

// helper function to set the stored user manually (for testing)
export function setStoredUser(user) {
  storedUser = user;
}

// helper function to remove the stored user manually (for testing)
export function removeStoredUser() {
  storedUser = null;
}

// callback function to the userManager's getUser
export function getUserCallback(user) {
  if (!user || user.expired) {
    nextMiddleware(userExpired());
  } else {
    storedUser = user;
    nextMiddleware(userFound(user));
  }
}

// callback for the userManager's getUser.catch
export function errorCallback(error) {
  console.error(`redux-oidc: Error loading user in oidcMiddleware: ${error.message}`);
  nextMiddleware(loadUserError());
}

// the middleware handler function
export function middlewareHandler(next, action, userManager) {
  // prevent an infinite loop of dispatches of these action types (issue #30 & #63)
  if (action.type === USER_EXPIRED || action.type === LOADING_USER || action.type === USER_FOUND) {
    return next(action);
  }

  nextMiddleware = next;

  if (!storedUser || storedUser.expired) {
    next(loadingUser());
    userManager.getUser()
      .then(getUserCallback)
      .catch(errorCallback);
  }
  return next(action);
}

// the middleware creator function
export default function createOidcMiddleware(userManager) {
  if (!userManager || !userManager.getUser) {
    throw new Error('You must provide a user manager!');
  }

  // the middleware
  return (store) => (next) => (action) => {
    return middlewareHandler(next, action, userManager);
  }
};
<|endoftext|>


<commit_before>/**
 * @module ol/source/IIIF
 */

import {DEFAULT_TILE_SIZE} from '../tilegrid/common.js';
import {getTopLeft} from '../extent.js';
import {CustomTile} from './Zoomify.js';
import {Versions} from '../format/IIIFInfo.js';
import {assert} from '../asserts.js';
import TileGrid from '../tilegrid/TileGrid.js';
import TileImage from './TileImage.js';

/**
 * @typedef {Object} Options
 * @property {import("./Source.js").AttributionLike} [attributions] Attributions.
 * @property {boolean} [attributionsCollapsible=true] Attributions are collapsible.
 * @property {number} [cacheSize]
 * @property {null|string} [crossOrigin]
 * @property {import("../extent.js").Extent} [extent=[0, -height, width, 0]]
 * @property {string} [format='jpg'] Requested image format.
 * @property {import("../proj.js").ProjectionLike} [projection]
 * @property {string} [quality] Requested IIIF image quality. Default is 'native'
 * for version 1, 'default' for versions 2 and 3.
 * @property {number} [reprojectionErrorThreshold=0.5] Maximum allowed reprojection error (in pixels).
 * Higher values can increase reprojection performance, but decrease precision.
 * @property {import("../size.js").Size} size Size of the image [width, height].
 * @property {import("../size.js").Size[]} [sizes] Supported scaled image sizes.
 * Content of the IIIF info.json 'sizes' property, but as array of Size objects.
 * @property {import("./State.js").default} [state] Source state.
 * @property {Array<string>} [supports=[]] Supported IIIF region and size calculation
 * features.
 * @property {number} [tilePixelRatio]
 * @property {number|import("../size.js").Size} [tileSize] Tile size.
 * Same tile size is used for all zoom levels. If tile size is a number,
 * a square tile is assumed. If the IIIF image service supports arbitrary
 * tiling (sizeByH, sizeByW, sizeByWh or sizeByPct as well as regionByPx or regionByPct
 * are supported), the default tilesize is 256.
 * @property {number} [transition]
 * @property {string} [url] Base URL of the IIIF Image service.
 * This should be the same as the IIIF Image @id.
 * @property {Versions} [version=Versions.VERSION2] Service's IIIF Image API version.
 * @property {number} [zDirection] Indicate which resolution should be used
 * by a renderer if the views resolution does not match any resolution of the tile source.
 * If 0, the nearest resolution will be used. If 1, the nearest lower resolution
 * will be used. If -1, the nearest higher resolution will be used.
 */

function formatPercentage(percentage) {
  return percentage.toLocaleString('en', {maximumFractionDigits: 10});
}

/**
 * @classdesc
 * Layer source for IIIF Image API services.
 * @api
 */
class IIIF extends TileImage {

  /**
   * @param {Options} opt_options Tile source options. Use {@link import("../format/IIIFInfo.js").IIIFInfo}
   * to parse Image API service information responses into constructor options.
   * @api
   */
  constructor(opt_options) {

    const options = opt_options || {};

    let baseUrl = options.url || '';
    baseUrl = baseUrl + (baseUrl.lastIndexOf('/') === baseUrl.length - 1 || baseUrl === '' ? '' : '/');
    const version = options.version || Versions.VERSION2;
    const sizes = options.sizes || [];
    const size = options.size;
    assert(size != undefined && Array.isArray(size) && size.length == 2 &&
      !isNaN(size[0]) && size[0] > 0 && !isNaN(size[1]) && size[1] > 0, 60);
    const width = size[0];
    const height = size[1];
    const tileSize = options.tileSize;
    const tilePixelRatio = options.tilePixelRatio || 1;
    const format = options.format || 'jpg';
    const quality = options.quality || (options.version == Versions.VERSION1 ? 'native' : 'default');
    let resolutions = options.resolutions || [];
    const supports = options.supports || [];
    const extent = options.extent || [0, -height, width, 0];

    const supportsListedSizes = sizes != undefined && Array.isArray(sizes) && sizes.length > 0;
    const supportsListedTiles = tileSize != undefined && (Number.isInteger(tileSize) && tileSize > 0 || Array.isArray(tileSize) && tileSize.length > 0);
    const supportsArbitraryTiling = supports != undefined && Array.isArray(supports) &&
      (supports.includes('regionByPx') || supports.includes('regionByPct')) &&
      (supports.includes('sizeByWh') || supports.includes('sizeByH') ||
      supports.includes('sizeByW') || supports.includes('sizeByPct'));

    let tileWidth,
        tileHeight,
        maxZoom;

    resolutions.sort(function(a, b) {
      return b - a;
    });

    if (supportsListedTiles || supportsArbitraryTiling) {
      if (tileSize != undefined) {
        if (Number.isInteger(tileSize) && tileSize > 0) {
          tileWidth = tileSize;
          tileHeight = tileSize;
        } else if (Array.isArray(tileSize) && tileSize.length > 0) {
          if (tileSize.length == 1 || tileSize[1] == undefined && Number.isInteger(tileSize[0])) {
            tileWidth = tileSize[0];
            tileHeight = tileSize[0];
          }
          if (tileSize.length == 2) {
            if (Number.isInteger(tileSize[0]) && Number.isInteger(tileSize[1])) {
              tileWidth = tileSize[0];
              tileHeight = tileSize[1];
            } else if (tileSize[0] == undefined && Number.isInteger(tileSize[1])) {
              tileWidth = tileSize[1];
              tileHeight = tileSize[1];
            }
          }
        }
      }
      if (tileWidth === undefined || tileHeight === undefined) {
        tileWidth = DEFAULT_TILE_SIZE;
        tileHeight = DEFAULT_TILE_SIZE;
      }
      if (resolutions.length == 0) {
        maxZoom = Math.max(
          Math.ceil(Math.log(width / tileWidth) / Math.LN2),
          Math.ceil(Math.log(height / tileHeight) / Math.LN2)
        );
        for (let i = maxZoom; i >= 0; i--) {
          resolutions.push(Math.pow(2, i));
        }
      } else {
        const maxScaleFactor = Math.max([...resolutions]);
        // TODO maxScaleFactor might not be a power to 2
        maxZoom = Math.round(Math.log(maxScaleFactor) / Math.LN2);
      }
    } else {
      // No tile support.
      tileWidth = width;
      tileHeight = height;
      resolutions = [];
      if (supportsListedSizes) {
        /*
         * 'sizes' provided. Use full region in different resolutions. Every
         * resolution has only one tile.
         */
        sizes.sort(function(a, b) {
          return a[0] - b[0];
        });
        maxZoom = -1;
        const ignoredSizesIndex = [];
        for (let i = 0; i < sizes.length; i++) {
          const resolution = width / sizes[i][0];
          if (resolutions.length > 0 && resolutions[resolutions.length - 1] == resolution) {
            ignoredSizesIndex.push(i);
            continue;
          }
          resolutions.push(resolution);
          maxZoom++;
        }
        if (ignoredSizesIndex.length > 0) {
          for (let i = 0; i < ignoredSizesIndex.length; i++) {
            sizes.splice(ignoredSizesIndex[i] - i, 1);
          }
        }
      } else {
        // No useful image information at all. Try pseudo tile with full image.
        resolutions.push(1);
        sizes.push([width, height]);
        maxZoom = 0;
      }
    }

    const tileGrid = new TileGrid({
      tileSize: [tileWidth, tileHeight],
      extent: extent,
      origin: getTopLeft(extent),
      resolutions: resolutions
    });

    const tileUrlFunction = function(tileCoord, pixelRatio, projection) {
      let regionParam,
          sizeParam;
      const zoom = tileCoord[0];
      if (zoom > maxZoom) {
        return;
      }
      const tileX = tileCoord[1],
          tileY = tileCoord[2],
          scale = resolutions[zoom];
      if (tileX === undefined || tileY === undefined || scale === undefined ||
      tileX < 0 || Math.ceil(width / scale / tileWidth) <= tileX ||
      tileY < 0 || Math.ceil(height / scale / tileHeight) <= tileY) {
        return;
      }
      if (supportsArbitraryTiling || supportsListedTiles) {
        const regionX = tileX * tileWidth * scale,
            regionY = tileY * tileHeight * scale;
        let regionW = tileWidth * scale,
            regionH = tileHeight * scale,
            sizeW = tileWidth,
            sizeH = tileHeight;
        if (regionX + regionW > width) {
          regionW = width - regionX;
        }
        if (regionY + regionH > height) {
          regionH = height - regionY;
        }
        if (regionX + tileWidth * scale > width) {
          sizeW = Math.floor((width - regionX + scale - 1) / scale);
        }
        if (regionY + tileHeight * scale > height) {
          sizeH = Math.floor((height - regionY + scale - 1) / scale);
        }
        if (regionX == 0 && regionW == width && regionY == 0 && regionH == height) {
          // canonical full image region parameter is 'full', not 'x,y,w,h'
          regionParam = 'full';
        } else if (!supportsArbitraryTiling || supports.includes('regionByPx')) {
          regionParam = regionX + ',' + regionY + ',' + regionW + ',' + regionH;
        } else if (supports.includes('regionByPct')) {
          const pctX = formatPercentage(regionX / width * 100),
              pctY = formatPercentage(regionY / height * 100),
              pctW = formatPercentage(regionW / width * 100),
              pctH = formatPercentage(regionH / height * 100);
          regionParam = 'pct:' + pctX + ',' + pctY + ',' + pctW + ',' + pctH;
        }
        if (version == Versions.VERSION3 && (!supportsArbitraryTiling || supports.includes('sizeByWh'))) {
          sizeParam = sizeW + ',' + sizeH;
        } else if (!supportsArbitraryTiling || supports.includes('sizeByW')) {
          sizeParam = sizeW + ',';
        } else if (supports.includes('sizeByH')) {
          sizeParam = ',' + sizeH;
        } else if (supports.includes('sizeByWh')) {
          sizeParam = sizeW + ',' + sizeH;
        } else if (supports.includes('sizeByPct')) {
          sizeParam = 'pct:' + formatPercentage(100 / scale);
        }
      } else {
        regionParam = 'full';
        if (supportsListedSizes) {
          const regionWidth = sizes[zoom][0],
              regionHeight = sizes[zoom][1];
          if (version == Versions.VERSION3) {
            if (regionWidth == width && regionHeight == height) {
              sizeParam = 'max';
            } else {
              sizeParam = regionWidth + ',' + regionHeight;
            }
          } else {
            if (regionWidth == width) {
              sizeParam = 'full';
            } else {
              sizeParam = regionWidth + ',';
            }
          }
        } else {
          sizeParam = version == Versions.VERSION3 ? 'max' : 'full';
        }
      }
      return baseUrl + regionParam + '/' + sizeParam + '/0/' + quality + '.' + format;
    };

    const IiifTileClass = CustomTile.bind(null, tilePixelRatio, tileGrid);

    super({
      attributions: options.attributions,
      attributionsCollapsible: options.attributionsCollapsible,
      cacheSize: options.cacheSize,
      crossOrigin: options.crossOrigin,
      projection: options.projection,
      reprojectionErrorThreshold: options.reprojectionErrorThreshold,
      state: options.state,
      tileClass: IiifTileClass,
      tileGrid: tileGrid,
      tilePixelRatio: options.tilePixelRatio,
      tileUrlFunction: tileUrlFunction,
      transition: options.transition
    });

    /**
     * @inheritDoc
     */
    this.zDirection = options.zDirection;

  }

}

export default IIIF;
<commit_msg>Fix several TypeScript type check problems<commit_after>/**
 * @module ol/source/IIIF
 */

import {DEFAULT_TILE_SIZE} from '../tilegrid/common.js';
import {getTopLeft} from '../extent.js';
import {CustomTile} from './Zoomify.js';
import {Versions} from '../format/IIIFInfo.js';
import {assert} from '../asserts.js';
import TileGrid from '../tilegrid/TileGrid.js';
import TileImage from './TileImage.js';

/**
 * @typedef {Object} Options
 * @property {import("./Source.js").AttributionLike} [attributions] Attributions.
 * @property {boolean} [attributionsCollapsible=true] Attributions are collapsible.
 * @property {number} [cacheSize]
 * @property {null|string} [crossOrigin]
 * @property {import("../extent.js").Extent} [extent=[0, -height, width, 0]]
 * @property {string} [format='jpg'] Requested image format.
 * @property {import("../proj.js").ProjectionLike} [projection]
 * @property {string} [quality] Requested IIIF image quality. Default is 'native'
 * for version 1, 'default' for versions 2 and 3.
 * @property {number} [reprojectionErrorThreshold=0.5] Maximum allowed reprojection error (in pixels).
 * Higher values can increase reprojection performance, but decrease precision.
 * @property {Array<number>} [resolutions] Supported resolutions as given in IIIF 'scaleFactors'
 * @property {import("../size.js").Size} size Size of the image [width, height].
 * @property {import("../size.js").Size[]} [sizes] Supported scaled image sizes.
 * Content of the IIIF info.json 'sizes' property, but as array of Size objects.
 * @property {import("./State.js").default} [state] Source state.
 * @property {Array<string>} [supports=[]] Supported IIIF region and size calculation
 * features.
 * @property {number} [tilePixelRatio]
 * @property {number|import("../size.js").Size} [tileSize] Tile size.
 * Same tile size is used for all zoom levels. If tile size is a number,
 * a square tile is assumed. If the IIIF image service supports arbitrary
 * tiling (sizeByH, sizeByW, sizeByWh or sizeByPct as well as regionByPx or regionByPct
 * are supported), the default tilesize is 256.
 * @property {number} [transition]
 * @property {string} [url] Base URL of the IIIF Image service.
 * This should be the same as the IIIF Image ID.
 * @property {Versions} [version=Versions.VERSION2] Service's IIIF Image API version.
 * @property {number} [zDirection] Indicate which resolution should be used
 * by a renderer if the views resolution does not match any resolution of the tile source.
 * If 0, the nearest resolution will be used. If 1, the nearest lower resolution
 * will be used. If -1, the nearest higher resolution will be used.
 */

function formatPercentage(percentage) {
  return percentage.toLocaleString('en', {maximumFractionDigits: 10});
}

/**
 * @classdesc
 * Layer source for IIIF Image API services.
 * @api
 */
class IIIF extends TileImage {

  /**
   * @param {Options} opt_options Tile source options. Use {@link import("../format/IIIFInfo.js").IIIFInfo}
   * to parse Image API service information responses into constructor options.
   * @api
   */
  constructor(opt_options) {

    /**
     * @type {Partial<Options>} options
     */
    const options = opt_options || {};

    let baseUrl = options.url || '';
    baseUrl = baseUrl + (baseUrl.lastIndexOf('/') === baseUrl.length - 1 || baseUrl === '' ? '' : '/');
    const version = options.version || Versions.VERSION2;
    const sizes = options.sizes || [];
    const size = options.size;
    assert(size != undefined && Array.isArray(size) && size.length == 2 &&
      !isNaN(size[0]) && size[0] > 0 && !isNaN(size[1]) && size[1] > 0, 60);
    const width = size[0];
    const height = size[1];
    const tileSize = options.tileSize;
    const tilePixelRatio = options.tilePixelRatio || 1;
    const format = options.format || 'jpg';
    const quality = options.quality || (options.version == Versions.VERSION1 ? 'native' : 'default');
    let resolutions = options.resolutions || [];
    const supports = options.supports || [];
    const extent = options.extent || [0, -height, width, 0];

    const supportsListedSizes = sizes != undefined && Array.isArray(sizes) && sizes.length > 0;
    const supportsListedTiles = tileSize != undefined && (typeof tileSize === 'number' && Number.isInteger(tileSize) && tileSize > 0 || Array.isArray(tileSize) && tileSize.length > 0);
    const supportsArbitraryTiling = supports != undefined && Array.isArray(supports) &&
      (supports.includes('regionByPx') || supports.includes('regionByPct')) &&
      (supports.includes('sizeByWh') || supports.includes('sizeByH') ||
      supports.includes('sizeByW') || supports.includes('sizeByPct'));

    let tileWidth,
        tileHeight,
        maxZoom;

    resolutions.sort(function(a, b) {
      return b - a;
    });

    if (supportsListedTiles || supportsArbitraryTiling) {
      if (tileSize != undefined) {
        if (typeof tileSize === 'number' && Number.isInteger(tileSize) && tileSize > 0) {
          tileWidth = tileSize;
          tileHeight = tileSize;
        } else if (Array.isArray(tileSize) && tileSize.length > 0) {
          if (tileSize.length == 1 || tileSize[1] == undefined && Number.isInteger(tileSize[0])) {
            tileWidth = tileSize[0];
            tileHeight = tileSize[0];
          }
          if (tileSize.length == 2) {
            if (Number.isInteger(tileSize[0]) && Number.isInteger(tileSize[1])) {
              tileWidth = tileSize[0];
              tileHeight = tileSize[1];
            } else if (tileSize[0] == undefined && Number.isInteger(tileSize[1])) {
              tileWidth = tileSize[1];
              tileHeight = tileSize[1];
            }
          }
        }
      }
      if (tileWidth === undefined || tileHeight === undefined) {
        tileWidth = DEFAULT_TILE_SIZE;
        tileHeight = DEFAULT_TILE_SIZE;
      }
      if (resolutions.length == 0) {
        maxZoom = Math.max(
          Math.ceil(Math.log(width / tileWidth) / Math.LN2),
          Math.ceil(Math.log(height / tileHeight) / Math.LN2)
        );
        for (let i = maxZoom; i >= 0; i--) {
          resolutions.push(Math.pow(2, i));
        }
      } else {
        const maxScaleFactor = Math.max(...resolutions);
        // TODO maxScaleFactor might not be a power to 2
        maxZoom = Math.round(Math.log(maxScaleFactor) / Math.LN2);
      }
    } else {
      // No tile support.
      tileWidth = width;
      tileHeight = height;
      resolutions = [];
      if (supportsListedSizes) {
        /*
         * 'sizes' provided. Use full region in different resolutions. Every
         * resolution has only one tile.
         */
        sizes.sort(function(a, b) {
          return a[0] - b[0];
        });
        maxZoom = -1;
        const ignoredSizesIndex = [];
        for (let i = 0; i < sizes.length; i++) {
          const resolution = width / sizes[i][0];
          if (resolutions.length > 0 && resolutions[resolutions.length - 1] == resolution) {
            ignoredSizesIndex.push(i);
            continue;
          }
          resolutions.push(resolution);
          maxZoom++;
        }
        if (ignoredSizesIndex.length > 0) {
          for (let i = 0; i < ignoredSizesIndex.length; i++) {
            sizes.splice(ignoredSizesIndex[i] - i, 1);
          }
        }
      } else {
        // No useful image information at all. Try pseudo tile with full image.
        resolutions.push(1);
        sizes.push([width, height]);
        maxZoom = 0;
      }
    }

    const tileGrid = new TileGrid({
      tileSize: [tileWidth, tileHeight],
      extent: extent,
      origin: getTopLeft(extent),
      resolutions: resolutions
    });

    const tileUrlFunction = function(tileCoord, pixelRatio, projection) {
      let regionParam,
          sizeParam;
      const zoom = tileCoord[0];
      if (zoom > maxZoom) {
        return;
      }
      const tileX = tileCoord[1],
          tileY = tileCoord[2],
          scale = resolutions[zoom];
      if (tileX === undefined || tileY === undefined || scale === undefined ||
      tileX < 0 || Math.ceil(width / scale / tileWidth) <= tileX ||
      tileY < 0 || Math.ceil(height / scale / tileHeight) <= tileY) {
        return;
      }
      if (supportsArbitraryTiling || supportsListedTiles) {
        const regionX = tileX * tileWidth * scale,
            regionY = tileY * tileHeight * scale;
        let regionW = tileWidth * scale,
            regionH = tileHeight * scale,
            sizeW = tileWidth,
            sizeH = tileHeight;
        if (regionX + regionW > width) {
          regionW = width - regionX;
        }
        if (regionY + regionH > height) {
          regionH = height - regionY;
        }
        if (regionX + tileWidth * scale > width) {
          sizeW = Math.floor((width - regionX + scale - 1) / scale);
        }
        if (regionY + tileHeight * scale > height) {
          sizeH = Math.floor((height - regionY + scale - 1) / scale);
        }
        if (regionX == 0 && regionW == width && regionY == 0 && regionH == height) {
          // canonical full image region parameter is 'full', not 'x,y,w,h'
          regionParam = 'full';
        } else if (!supportsArbitraryTiling || supports.includes('regionByPx')) {
          regionParam = regionX + ',' + regionY + ',' + regionW + ',' + regionH;
        } else if (supports.includes('regionByPct')) {
          const pctX = formatPercentage(regionX / width * 100),
              pctY = formatPercentage(regionY / height * 100),
              pctW = formatPercentage(regionW / width * 100),
              pctH = formatPercentage(regionH / height * 100);
          regionParam = 'pct:' + pctX + ',' + pctY + ',' + pctW + ',' + pctH;
        }
        if (version == Versions.VERSION3 && (!supportsArbitraryTiling || supports.includes('sizeByWh'))) {
          sizeParam = sizeW + ',' + sizeH;
        } else if (!supportsArbitraryTiling || supports.includes('sizeByW')) {
          sizeParam = sizeW + ',';
        } else if (supports.includes('sizeByH')) {
          sizeParam = ',' + sizeH;
        } else if (supports.includes('sizeByWh')) {
          sizeParam = sizeW + ',' + sizeH;
        } else if (supports.includes('sizeByPct')) {
          sizeParam = 'pct:' + formatPercentage(100 / scale);
        }
      } else {
        regionParam = 'full';
        if (supportsListedSizes) {
          const regionWidth = sizes[zoom][0],
              regionHeight = sizes[zoom][1];
          if (version == Versions.VERSION3) {
            if (regionWidth == width && regionHeight == height) {
              sizeParam = 'max';
            } else {
              sizeParam = regionWidth + ',' + regionHeight;
            }
          } else {
            if (regionWidth == width) {
              sizeParam = 'full';
            } else {
              sizeParam = regionWidth + ',';
            }
          }
        } else {
          sizeParam = version == Versions.VERSION3 ? 'max' : 'full';
        }
      }
      return baseUrl + regionParam + '/' + sizeParam + '/0/' + quality + '.' + format;
    };

    const IiifTileClass = CustomTile.bind(null, tilePixelRatio, tileGrid);

    super({
      attributions: options.attributions,
      attributionsCollapsible: options.attributionsCollapsible,
      cacheSize: options.cacheSize,
      crossOrigin: options.crossOrigin,
      projection: options.projection,
      reprojectionErrorThreshold: options.reprojectionErrorThreshold,
      state: options.state,
      tileClass: IiifTileClass,
      tileGrid: tileGrid,
      tilePixelRatio: options.tilePixelRatio,
      tileUrlFunction: tileUrlFunction,
      transition: options.transition
    });

    /**
     * @inheritDoc
     */
    this.zDirection = options.zDirection;

  }

}

export default IIIF;
<|endoftext|>


<commit_before>function onOpen() {
  var spreadsheet = SpreadsheetApp.getActive();
  spreadsheet.addMenu('Time Tracking', [{ "name": 'Lauch Timer', "functionName": 'launchTimer_'}]);
}

function launchTimer_() {
  var ui = SpreadsheetApp.getUi();
  ui.showSidebar(HtmlService.createHtmlOutputFromFile('timer.html'));
}

function beginTimeSheetEntry() {
  var cell = SpreadsheetApp.getActiveSheet().getActiveCell();
  cell.setValue(new Date());
  return [cell.getRow(), cell.getColumn()];
}
<commit_msg>remember time sheet entry position when timer begins<commit_after>

Timer

  <form>
    <input type="button" value="Start" id="toggleTimerButton" />
  </form>

<|endoftext|>


<commit_before>define(function (require, exports, module) {
    "use strict";

    /**
     * Js 派生实现
     *
     * @param {Function} parent 父类
     * @param {Function} [constructor]  子类构造函数
     * @param {Object} [proto] 子类原型
     * @access public
     * @return {Function} 新的类
     *
     * @example
     *
     * var ClassA = derive(Object, function(__super){
     *      console.log("I'm an instance of ClassA:", this instanceof ClassA);
     * });
     *
     * var ClassB = derive(ClassA, function(__super){
     *      console.log("I'm an instance of ClassB:", this instanceof ClassB);
     *      __super();
     * }, {
     *      test:function(){
     *          console.log("test method!");
     *      }
     * });
     *
     * var b = new ClassB();
     * //I'm an instance of ClassA: true
     * //I'm an instance of ClassA: true
     * b.test();
     * //test method!
     */

    var isFunction = require("base/isFunction");
    var copyProperties = require("base/copyProperties");
    var each = require("base/each");
    var hasOwnProperty = require("base/hasOwnProperty");

    function bindSuper(fn, superFn) {
        //return fn;

        if (!isFunction(superFn)) {
            superFn = throwNoSuper;
        }

        return function () {
            var returnValue;
            var _super = this._super;
            this._super = superFn;
            //try {
            returnValue = fn.apply(this, arguments);
            //} finally {
            this._super = _super;
            //}
            return returnValue;
        };
    }

    function throwNoSuper() {
        throw new Error("this._super is not a function.");
    }

    function derive(parent, constructor, proto) {

        //如果没有传 constructor 参数
        if (typeof constructor === 'object') {
            proto = constructor;
            constructor = hasOwnProperty(proto, "constructor") ?
                proto.constructor :
                function () {
                    //this._super.apply(this, arguments);
                    parent.apply(this, arguments);
                };
            delete proto.constructor;
        }

        var //tmp = function () {},
        //子类构造函数
        //subClass = bindSuper(constructor, parent),
            subClass = constructor,
            subClassPrototype,
            key, value,
            parts, modifier, properties, property;

        //原型链桥接
        //tmp.prototype = parent.prototype;
        //subClassPrototype = new tmp();
        subClassPrototype = Object.create(parent.prototype);
        proto = proto || {};

        //复制属性到子类的原型链上
        //copyProperties(
        //    subClassPrototype,
        //    constructor.prototype
        //);
        //subClassPrototype.constructor = constructor.prototype.constructor;

        properties = {};
        each(proto, function (value, key) {
            parts = key.split(" ");
            if (parts.length === 1) {
                //if (isFunction(value)) {
                //subClassPrototype[key] = bindSuper(value, parent.prototype[key]);
                //} else {
                subClassPrototype[key] = value;
                //}
            } else {
                modifier = parts.shift();
                key = parts.join(" ");
                switch (modifier) {
                case "get":
                case "set":
                    property = properties[key] || {};
                    property[modifier] = value;
                    properties[key] = property;
                    break;
                default:
                    //TODO
                }
            }
        });
        Object.defineProperties(subClassPrototype, properties);

        //each(properties, function (conf, name) {
        //    Object.defineProperty(subClassPrototype, name, conf);
        //});

        subClass.prototype = subClassPrototype;
        subClassPrototype.constructor = subClass;
        return subClass;
    }

    module.exports = derive;
    //});

});
<commit_msg>修复未指定parent时的初始化问题<commit_after>define(function (require, exports, module) {
    "use strict";

    /**
     * Js 派生实现
     *
     * @param {Function} parent 父类
     * @param {Function} [constructor]  子类构造函数
     * @param {Object} [proto] 子类原型
     * @access public
     * @return {Function} 新的类
     *
     * @example
     *
     * var ClassA = derive(Object, function(__super){
     *      console.log("I'm an instance of ClassA:", this instanceof ClassA);
     * });
     *
     * var ClassB = derive(ClassA, function(__super){
     *      console.log("I'm an instance of ClassB:", this instanceof ClassB);
     *      __super();
     * }, {
     *      test:function(){
     *          console.log("test method!");
     *      }
     * });
     *
     * var b = new ClassB();
     * //I'm an instance of ClassA: true
     * //I'm an instance of ClassA: true
     * b.test();
     * //test method!
     */

    var isFunction = require("base/isFunction");
    var copyProperties = require("base/copyProperties");
    var each = require("base/each");
    var hasOwnProperty = require("base/hasOwnProperty");

    function bindSuper(fn, superFn) {
        //return fn;

        if (!isFunction(superFn)) {
            superFn = throwNoSuper;
        }

        return function () {
            var returnValue;
            var _super = this._super;
            this._super = superFn;
            //try {
            returnValue = fn.apply(this, arguments);
            //} finally {
            this._super = _super;
            //}
            return returnValue;
        };
    }

    function throwNoSuper() {
        throw new Error("this._super is not a function.");
    }

    function derive(parent, constructor, proto) {

        //如果没有传 constructor 参数
        if (typeof constructor === 'object') {
            proto = constructor;
            constructor = hasOwnProperty(proto, "constructor") ?
                proto.constructor :
                function () {
                    parent.apply(this, arguments);
                };
            delete proto.constructor;
        }

        var //tmp = function () {},
        //子类构造函数
        //subClass = bindSuper(constructor, parent),
            subClass = constructor,
            subClassPrototype,
            key, value,
            parts, modifier, properties, property;

        //原型链桥接
        //tmp.prototype = parent.prototype;
        //subClassPrototype = new tmp();
        subClassPrototype = Object.create(parent.prototype);
        proto = proto || {};

        //复制属性到子类的原型链上
        //copyProperties(
        //    subClassPrototype,
        //    constructor.prototype
        //);
        //subClassPrototype.constructor = constructor.prototype.constructor;

        properties = {};
        each(proto, function (value, key) {
            parts = key.split(" ");
            if (parts.length === 1) {
                //if (isFunction(value)) {
                //subClassPrototype[key] = bindSuper(value, parent.prototype[key]);
                //} else {
                subClassPrototype[key] = value;
                //}
            } else {
                modifier = parts.shift();
                key = parts.join(" ");
                switch (modifier) {
                case "get":
                case "set":
                    property = properties[key] || {};
                    property[modifier] = value;
                    properties[key] = property;
                    break;
                default:
                    //TODO
                }
            }
        });
        Object.defineProperties(subClassPrototype, properties);

        //each(properties, function (conf, name) {
        //    Object.defineProperty(subClassPrototype, name, conf);
        //});

        subClass.prototype = subClassPrototype;
        subClassPrototype.constructor = subClass;
        return subClass;
    }

    module.exports = derive;
    //});

});
<|endoftext|>


<commit_before>define(function (require, exports, module) {
    "use strict";

    /**
     * Js 派生实现
     *
     * @param {Function} parent 父类
     * @param {Function} [constructor]  子类构造函数
     * @param {Object} [proto] 子类原型
     * @access public
     * @return {Function} 新的类
     *
     * @example
     *
     * var ClassA = derive(Object, function(__super){
     *      console.log("I'm an instance of ClassA:", this instanceof ClassA);
     * });
     *
     * var ClassB = derive(ClassA, function(__super){
     *      console.log("I'm an instance of ClassB:", this instanceof ClassB);
     *      __super();
     * }, {
     *      test:function(){
     *          console.log("test method!");
     *      }
     * });
     *
     * var b = new ClassB();
     * //I'm an instance of ClassA: true
     * //I'm an instance of ClassA: true
     * b.test();
     * //test method!
     */

    var isFunction = require("base/isFunction");
    var copyProperties = require("base/copyProperties");
    var each = require("base/each");
    var hasOwnProperty = require("base/hasOwnProperty");

    function bindSuper(fn, superFn) {
        //return fn;

        if (!isFunction(superFn)) {
            superFn = throwNoSuper;
        }

        return function () {
            var returnValue;
            var _super = this._super;
            this._super = superFn;
            try {
                returnValue = fn.apply(this, arguments);
            } finally {
                this._super = _super;
            }
            return returnValue;
        };
    }

    function throwNoSuper() {
        throw new Error("this._super is not a function.");
    }

    function derive(parent, constructor, proto) {

        //如果没有传 constructor 参数
        if (typeof constructor === 'object') {
            proto = constructor;
            constructor = hasOwnProperty(proto, "constructor") ?
                proto.constructor :
                function () {
                    this._super.apply(this, arguments);
                };
            delete proto.constructor;
        }

        var tmp = function () {},
            //子类构造函数
            subClass = bindSuper(constructor, parent),
            subClassPrototype,
            key, value,
            parts, modifier, properties, property;

        //原型链桥接
        tmp.prototype = parent.prototype;
        subClassPrototype = new tmp();
        proto = proto || {};

        //复制属性到子类的原型链上
        copyProperties(
            subClassPrototype,
            constructor.prototype
        );
        subClassPrototype.constructor = constructor.prototype.constructor;

        properties = {};
        each(proto, function (value, key) {
            parts = key.split(" ");
            if (parts.length === 1) {
                if (isFunction(value)) {
                    subClassPrototype[key] = bindSuper(value, parent.prototype[key]);
                } else {
                    subClassPrototype[key] = value;
                }
            } else {
                modifier = parts.shift();
                key = parts.join(" ");
                switch (modifier) {
                case "get":
                case "set":
                    property = properties[key] || {};
                    property[modifier] = value;
                    properties[key] = property;
                    break;
                default:
                    //TODO
                }
            }
        });
        Object.defineProperties(subClassPrototype, properties);

        //each(properties, function (conf, name) {
        //    Object.defineProperty(subClassPrototype, name, conf);
        //});

        subClass.prototype = subClassPrototype;
        return subClass;
    }

    module.exports = derive;
    //});

});
<commit_msg>去掉try，优化性能<commit_after>define(function (require, exports, module) {
    "use strict";

    /**
     * Js 派生实现
     *
     * @param {Function} parent 父类
     * @param {Function} [constructor]  子类构造函数
     * @param {Object} [proto] 子类原型
     * @access public
     * @return {Function} 新的类
     *
     * @example
     *
     * var ClassA = derive(Object, function(__super){
     *      console.log("I'm an instance of ClassA:", this instanceof ClassA);
     * });
     *
     * var ClassB = derive(ClassA, function(__super){
     *      console.log("I'm an instance of ClassB:", this instanceof ClassB);
     *      __super();
     * }, {
     *      test:function(){
     *          console.log("test method!");
     *      }
     * });
     *
     * var b = new ClassB();
     * //I'm an instance of ClassA: true
     * //I'm an instance of ClassA: true
     * b.test();
     * //test method!
     */

    var isFunction = require("base/isFunction");
    var copyProperties = require("base/copyProperties");
    var each = require("base/each");
    var hasOwnProperty = require("base/hasOwnProperty");

    function bindSuper(fn, superFn) {
        //return fn;

        if (!isFunction(superFn)) {
            superFn = throwNoSuper;
        }

        return function () {
            var returnValue;
            var _super = this._super;
            this._super = superFn;
            //try {
            returnValue = fn.apply(this, arguments);
            //} finally {
            this._super = _super;
            //}
            return returnValue;
        };
    }

    function throwNoSuper() {
        throw new Error("this._super is not a function.");
    }

    function derive(parent, constructor, proto) {

        //如果没有传 constructor 参数
        if (typeof constructor === 'object') {
            proto = constructor;
            constructor = hasOwnProperty(proto, "constructor") ?
                proto.constructor :
                function () {
                    this._super.apply(this, arguments);
                };
            delete proto.constructor;
        }

        var tmp = function () {},
            //子类构造函数
            subClass = bindSuper(constructor, parent),
            subClassPrototype,
            key, value,
            parts, modifier, properties, property;

        //原型链桥接
        tmp.prototype = parent.prototype;
        subClassPrototype = new tmp();
        proto = proto || {};

        //复制属性到子类的原型链上
        copyProperties(
            subClassPrototype,
            constructor.prototype
        );
        subClassPrototype.constructor = constructor.prototype.constructor;

        properties = {};
        each(proto, function (value, key) {
            parts = key.split(" ");
            if (parts.length === 1) {
                if (isFunction(value)) {
                    subClassPrototype[key] = bindSuper(value, parent.prototype[key]);
                } else {
                    subClassPrototype[key] = value;
                }
            } else {
                modifier = parts.shift();
                key = parts.join(" ");
                switch (modifier) {
                case "get":
                case "set":
                    property = properties[key] || {};
                    property[modifier] = value;
                    properties[key] = property;
                    break;
                default:
                    //TODO
                }
            }
        });
        Object.defineProperties(subClassPrototype, properties);

        //each(properties, function (conf, name) {
        //    Object.defineProperty(subClassPrototype, name, conf);
        //});

        subClass.prototype = subClassPrototype;
        return subClass;
    }

    module.exports = derive;
    //});

});
<|endoftext|>


<commit_before>'use strict'

const Option=require('./option-classes')
const BaseOptionsOutput=require('crnx-base/options-output')

class OptionsOutput extends BaseOptionsOutput {
	setOptionClassWriters(optionClassWriters) {
		super.setOptionClassWriters(optionClassWriters)
		optionClassWriters.set(Option.LiveNumber,(option,writeOption,i18n,generateId)=>{
			const p=option.precision
			const setInputAttrs=$input=>$input
				.attr('min',option.availableMin)
				.attr('max',option.availableMax)
				.attr('step',Math.pow(0.1,p).toFixed(p))
			const writeMinMaxInput=minOrMax=>setInputAttrs($("<input type='number' required>"))
				.val(option[minOrMax])
				.on('input change',function(){
					if (this.checkValidity()) {
						option[minOrMax]=parseFloat(this.value)
					}
				})
			const inputCheckboxId=generateId()
			let $inputCheckbox,$rangeMinInput,$rangeMaxInput
			const $output=optionClassWriters.get(Option.Number)(option,writeOption,i18n,generateId)
			$output.find('button').before(
				$inputCheckbox=$("<input type='checkbox' id='"+inputCheckboxId+"'>")
					.prop('checked',option.input)
					.change(function(){
						option.input=$(this).prop('checked')
					}),
				" "+i18n('options-output.input')+" ",
				option.$range=$("").append(
					i18n('options-output.range')+" ",
					$rangeMinInput=writeMinMaxInput('min'),
					" .. ",
					$rangeMaxInput=writeMinMaxInput('max')
				),
				" "
			).click(function(){
				$inputCheckbox.prop('checked',false).change()
				$rangeMinInput.val(option.defaultMin).change()
				$rangeMaxInput.val(option.defaultMax).change()
			})
			return $output
		})
		optionClassWriters.set(Option.LiveSelect,(option,writeOption,i18n,generateId)=>{
			const id=generateId()
			const inputCheckboxId=generateId()
			let $select,$inputCheckbox
			return option.$=$("
").append(
				""+i18n('options.'+option.fullName)+": ",
				$select=$("<select id='"+id+"'>").append(
					option.availableValues.map(function(availableValue){
						return $("<option>").val(availableValue).html(i18n('options.'+option.fullName+'.'+availableValue))
					})
				).val(option.value).change(function(){
					option.value=this.value
				}),
				" ",
				$inputCheckbox=$("<input type='checkbox' id='"+inputCheckboxId+"'>")
					.prop('checked',option.input)
					.change(function(){
						option.input=$(this).prop('checked')
					}),
				" "+i18n('options-output.input')+" ",
				$("<button type='button'>"+i18n('options-output.reset')+"</button>").click(function(){
					$select.val(option.defaultValue).change()
					$inputCheckbox.prop('checked',false).change()
				})
			)
		})
		optionClassWriters.set(Option.BiquadFilter,(option,writeOption,i18n,generateId)=>{
			const width=300
			const height=300
			let audioContext,biquadNode
			let frequencyArray,magnitudeArray,phaseArray
			const initAudioContext=()=>{
				audioContext=new (AudioContext || webkitAudioContext)
				biquadNode=audioContext.createBiquadFilter()
				frequencyArray=new Float32Array(width)
				magnitudeArray=new Float32Array(width)
				phaseArray=new Float32Array(width)
				const maxFreq=audioContext.sampleRate/2
				for (let i=0;i<width;i++) {
					frequencyArray[i]=maxFreq/width*i
				}
			}
			let shown=false
			let $magnitudeFigure, $phaseFigure
			let magnitudeCanvasContext, phaseCanvasContext
			return option.$=$("<fieldset>").append("<legend>"+i18n('options.'+option.fullName)+"</legend>").append(
				option.entries.map(writeOption),
				$("
").append(
					"Frequency response: ",
					$("<button type='button'>Show</button>").click(function(){
						if (!shown) {
							let $magnitudeCanvas, $phaseCanvas
							$(this).before(
								$magnitudeFigure=$("
").append(
									"
Magnitude
",
									$magnitudeCanvas=$(`<canvas width='${width}' height='${height}'></canvas>`)
								),
								$phaseFigure=$("
").append(
									"
Phase
",
									$phaseCanvas=$(`<canvas width='${width}' height='${height}'></canvas>`)
								)
							).text('Hide')
							shown=true
							magnitudeCanvasContext=$magnitudeCanvas[0].getContext('2d')
							phaseCanvasContext=$phaseCanvas[0].getContext('2d')
							if (!audioContext) {
								initAudioContext()
							}
							biquadNode.getFrequencyResponse(frequencyArray,magnitudeArray,phaseArray)
							const drawResponse=(canvasContext,array)=>{
								const min=Math.min(...array)
								const max=Math.max(...array)
								canvasContext.strokeStyle='#F00'
								canvasContext.beginPath()
								for (let i=0;i<width;i++) {
									const x=i
									//const y=height*(1-array[i]/max)
									const y=height*(1-(array[i]-min)/(max-min))
									if (i==0) {
										canvasContext.moveTo(x,y)
									} else {
										canvasContext.lineTo(x,y)
									}
								}
								canvasContext.stroke()
							}
							drawResponse(magnitudeCanvasContext,magnitudeArray)
							drawResponse(phaseCanvasContext,phaseArray)
						} else {
							$magnitudeFigure.remove()
							$phaseFigure.remove()
							$(this).text('Show')
							shown=false
						}
					})
				)
			)
		})
	}
}

module.exports=OptionsOutput
<commit_msg>update plots on input<commit_after>'use strict'

const debounce=require('crnx-base/fake-lodash/debounce')
const Option=require('./option-classes')
const BaseOptionsOutput=require('crnx-base/options-output')

class OptionsOutput extends BaseOptionsOutput {
	setOptionClassWriters(optionClassWriters) {
		super.setOptionClassWriters(optionClassWriters)
		optionClassWriters.set(Option.LiveNumber,(option,writeOption,i18n,generateId)=>{
			const p=option.precision
			const setInputAttrs=$input=>$input
				.attr('min',option.availableMin)
				.attr('max',option.availableMax)
				.attr('step',Math.pow(0.1,p).toFixed(p))
			const writeMinMaxInput=minOrMax=>setInputAttrs($("<input type='number' required>"))
				.val(option[minOrMax])
				.on('input change',function(){
					if (this.checkValidity()) {
						option[minOrMax]=parseFloat(this.value)
					}
				})
			const inputCheckboxId=generateId()
			let $inputCheckbox,$rangeMinInput,$rangeMaxInput
			const $output=optionClassWriters.get(Option.Number)(option,writeOption,i18n,generateId)
			$output.find('button').before(
				$inputCheckbox=$("<input type='checkbox' id='"+inputCheckboxId+"'>")
					.prop('checked',option.input)
					.change(function(){
						option.input=$(this).prop('checked')
					}),
				" "+i18n('options-output.input')+" ",
				option.$range=$("").append(
					i18n('options-output.range')+" ",
					$rangeMinInput=writeMinMaxInput('min'),
					" .. ",
					$rangeMaxInput=writeMinMaxInput('max')
				),
				" "
			).click(function(){
				$inputCheckbox.prop('checked',false).change()
				$rangeMinInput.val(option.defaultMin).change()
				$rangeMaxInput.val(option.defaultMax).change()
			})
			return $output
		})
		optionClassWriters.set(Option.LiveSelect,(option,writeOption,i18n,generateId)=>{
			const id=generateId()
			const inputCheckboxId=generateId()
			let $select,$inputCheckbox
			return option.$=$("
").append(
				""+i18n('options.'+option.fullName)+": ",
				$select=$("<select id='"+id+"'>").append(
					option.availableValues.map(function(availableValue){
						return $("<option>").val(availableValue).html(i18n('options.'+option.fullName+'.'+availableValue))
					})
				).val(option.value).change(function(){
					option.value=this.value
				}),
				" ",
				$inputCheckbox=$("<input type='checkbox' id='"+inputCheckboxId+"'>")
					.prop('checked',option.input)
					.change(function(){
						option.input=$(this).prop('checked')
					}),
				" "+i18n('options-output.input')+" ",
				$("<button type='button'>"+i18n('options-output.reset')+"</button>").click(function(){
					$select.val(option.defaultValue).change()
					$inputCheckbox.prop('checked',false).change()
				})
			)
		})
		optionClassWriters.set(Option.BiquadFilter,(option,writeOption,i18n,generateId)=>{
			const width=300
			const height=300
			let audioContext,biquadNode
			let frequencyArray,magnitudeArray,phaseArray
			const initAudioContext=()=>{
				audioContext=new (AudioContext || webkitAudioContext)
				biquadNode=audioContext.createBiquadFilter()
				frequencyArray=new Float32Array(width)
				magnitudeArray=new Float32Array(width)
				phaseArray=new Float32Array(width)
				const maxFreq=audioContext.sampleRate/2
				for (let i=0;i<width;i++) {
					frequencyArray[i]=maxFreq/width*i
				}
			}
			let shown=false
			let $magnitudeFigure, $phaseFigure
			let magnitudeCanvasContext, phaseCanvasContext
			const updatePlots=()=>{
				biquadNode.type=option.entries[0].value
				biquadNode.frequency.value=option.entries[1].value
				biquadNode.Q.value=Math.pow(10,option.entries[2].value)
				biquadNode.gain.value=option.entries[3].value
				biquadNode.detune.value=option.entries[4].value
				biquadNode.getFrequencyResponse(frequencyArray,magnitudeArray,phaseArray)
				const plotResponse=(canvasContext,array)=>{
					const min=Math.min(...array)
					const max=Math.max(...array)
					canvasContext.clearRect(0,0,width,height)
					canvasContext.strokeStyle='#F00'
					canvasContext.beginPath()
					for (let i=0;i<width;i++) {
						const x=i
						//const y=height*(1-array[i]/max)
						const y=height*(1-(array[i]-min)/(max-min))
						if (i==0) {
							canvasContext.moveTo(x,y)
						} else {
							canvasContext.lineTo(x,y)
						}
					}
					canvasContext.stroke()
				}
				plotResponse(magnitudeCanvasContext,magnitudeArray)
				plotResponse(phaseCanvasContext,phaseArray)
			}
			const delayedUpdatePlots=debounce(updatePlots,50)
			return option.$=$("<fieldset>").append("<legend>"+i18n('options.'+option.fullName)+"</legend>").append(
				option.entries.map(writeOption),
				$("
").append(
					"Frequency response: ",
					$("<button type='button'>Show</button>").click(function(){
						if (!shown) {
							let $magnitudeCanvas, $phaseCanvas
							$(this).before(
								$magnitudeFigure=$("
").append(
									"
Magnitude
",
									$magnitudeCanvas=$(`<canvas width='${width}' height='${height}'></canvas>`)
								),
								$phaseFigure=$("
").append(
									"
Phase
",
									$phaseCanvas=$(`<canvas width='${width}' height='${height}'></canvas>`)
								)
							).text('Hide')
							shown=true
							magnitudeCanvasContext=$magnitudeCanvas[0].getContext('2d')
							phaseCanvasContext=$phaseCanvas[0].getContext('2d')
							if (!audioContext) {
								initAudioContext()
							}
							updatePlots()
						} else {
							$magnitudeFigure.remove()
							$phaseFigure.remove()
							$(this).text('Show')
							shown=false
						}
					})
				)
			).on('input change',function(){
				if (shown) {
					delayedUpdatePlots()
				}
			})
		})
	}
}

module.exports=OptionsOutput
<|endoftext|>