<commit_before>/* eslint prefer-reflect: "off" */

import { event, select } from 'd3';
import debounce from 'lodash-es/debounce';
import { scroller } from '@scola/d3-scroller';
import Part from '../part';

export default class Scroller extends Part {
  constructor() {
    super();

    this._root = select('body')
      .append('div')
      .remove()
      .classed('scola scroller', true)
      .styles({
        'border-top': '1px solid',
        'border-top-color': 'inherit',
        'display': 'flex',
        'flex': 1,
        'height': '3em'
      });

    this._scroller = scroller();

    this._scroller.root().styles({
      'flex': 1
    });

    this._root.node()
      .appendChild(this._scroller.root().node());

    this._padding = this._root
      .append('div')
      .styles({
        'width': '1em'
      });

    this._handleScroll = debounce((e) => this._scroll(e), 100);
    this._bindScroller();
  }

  destroy() {
    this._unbindScroller();
    this._scroller.destroy();

    this._executeScroll.cancel();
    this._executeScroll = null;

    super.destroy();
  }

  scroller() {
    return this._scroller;
  }

  resize() {
    this._scroller
      .resize()
      .value(this._model.get(this._name));
  }

  _bindScroller() {
    this._scroller.root()
      .on('scroll.scola-list', () => this._handleScroll(event));
  }

  _unbindScroller() {
    this._scroller.root()
      .on('scroll.scola-list', null);
  }

  _scroll(scrollEvent) {
    this._model.set(this._name, scrollEvent.detail.value);
  }

  _set(setEvent) {
    if (setEvent.name !== this._name) {
      return;
    }

    this._scroller.value(setEvent.value, false);
  }
}
<commit_msg>Use new scroller interface<commit_after>/* eslint prefer-reflect: "off" */

import { select } from 'd3';
import { scroller } from '@scola/d3-scroller';
import Part from '../part';

export default class Scroller extends Part {
  constructor() {
    super();

    this._root = select('body')
      .append('div')
      .remove()
      .classed('scola scroller', true)
      .styles({
        'align-items': 'center',
        'border-top': '1px solid',
        'border-top-color': 'inherit',
        'display': 'flex',
        'flex': 1,
        'height': '3em'
      });

    this._scroller = scroller()
      .horizontal();

    this._scroller.root().styles({
      'flex': 1,
      'height': '2em'
    });

    this._root.node()
      .appendChild(this._scroller.root().node());

    this._padding = this._root
      .append('div')
      .styles({
        'width': '1em'
      });
  }

  scroller() {
    return this._scroller;
  }

  name(value = null) {
    value = this._scroller.name(value);
    return value === this._scroller ? this : value;
  }

  model(value, format = (v) => v) {
    this._scroller.model(value, format);
    return this;
  }

  domain(value = null) {
    value = this._scroller.domain(value);
    return value === this._scroller ? this : value;
  }

  step(value = null) {
    value = this._scroller.step(value);
    return value === this._scroller ? this : value;
  }

  ticks(value = null) {
    value = this._scroller.ticks(value);
    return value === this._scroller ? this : value;
  }

  tabindex(value = null) {
    value = this._scroller.tabindex(value);
    return value === this._scroller ? this : value;
  }

  resize() {
    this._scroller.resize();
    return this;
  }

  size(value = null) {
    if (value === null) {
      return this._root.style('width');
    }

    this._root.styles({
      'flex': 'none',
      'width': value
    });

    return this;
  }
}
<|endoftext|>


<commit_before>/*
 * Paper.js
 *
 * This file is part of Paper.js, a JavaScript Vector Graphics Library,
 * based on Scriptographer.org and designed to be largely API compatible.
 * http://paperjs.org/
 * http://scriptographer.org/
 *
 * Distributed under the MIT license. See LICENSE file for details.
 *
 * Copyright (c) 2011, Juerg Lehni & Jonathan Puckey
 * http://lehni.org/ & http://jonathanpuckey.com/
 *
 * All rights reserved.
 */

var PathItem = this.PathItem = Item.extend(/** @scope PathItem */{
	/**
	 * @name PathItem
	 * @class
	 * @extends Item
	 */

	// Note: The PathItem class is currently empty but holds the documentation
	// for all the methods that exist both on Path and CompoundPath.

	/**
	 * Smooth bezier curves without changing the amount of segments or their
	 * points, by only smoothing and adjusting their handle points, for both
	 * open ended and closed paths.
	 *
	 * @example {@paperscript}
	 * // Smoothing a closed shape:
	 *
	 * // Create a rectangular path with its top-left point at
	 * // {x: 30, y: 25} and a size of {width: 50, height: 50}:
	 * var path = new Path.Rectangle(new Point(30, 25), new Size(50, 50));
	 * path.strokeColor = 'black';
	 *
	 * // Select the path, so we can see its handles:
	 * path.selected = true;
	 *
	 * // Create a copy of the path and move it 100pt to the right:
	 * var copy = path.clone();
	 * copy.position.x += 100;
	 *
	 * // Smooth the segments of the copy:
	 * copy.smooth();
	 *
	 * @example {@paperscript height=220}
	 * var path = new Path();
	 * path.strokeColor = 'black';
	 *
	 * path.add(new Point(30, 50));
	 *
	 * var y = 5;
	 * var x = 3;
	 *
	 * for (var i = 0; i < 28; i++) {
	 *     y *= -1.1;
	 *     x *= 1.1;
	 *     path.lineBy(x, y);
	 * }
	 *
	 * // Create a copy of the path and move it 100pt down:
	 * var copy = path.clone();
	 * copy.position.y += 120;
	 *
	 * // Set its stroke color to red:
	 * copy.strokeColor = 'red';
	 *
	 * // Smooth the segments of the copy:
	 * copy.smooth();
	 */

	/**
	 * {@grouptitle Postscript Style Drawing Commands}
	 *
	 * If called on a {@link CompoundPath}, a new {@link Path} is created as a
	 * child and the point is added as its first segment. On a normal empty
	 * {@link Path}, the point is simply added as its first segment.
	 *
	 * @name PathItem#moveTo
	 * @function
	 * @param {Point} point
	 */

	// DOCS: Document lineTo
	/**
	 * @name PathItem#lineTo
	 * @function
	 * @param {Point} point
	 */

	/**
	 * Adds a cubic bezier curve to the path, defined by two handles and a to
	 * point.
	 *
	 * @name PathItem#cubicCurveTo
	 * @function
	 * @param {Point} handle1
	 * @param {Point} handle2
	 * @param {Point} to
	 */

	/**
	 * Adds a quadratic bezier curve to the path, defined by a handle and a to
	 * point.
	 *
	 * @name PathItem#quadraticCurveTo
	 * @function
	 * @param {Point} handle
	 * @param {Point} to
	 */

	// DOCS: Document PathItem#curveTo 'paramater' param.
	/**
	 * Draws a curve from the position of the last segment point in the path
	 * that goes through the specified {@code through} point, to the specified
	 * {@code to} point by adding one segment to the path.
	 *
	 * @name PathItem#curveTo
	 * @function
	 * @param {Point} through the point through which the curve should go
	 * @param {Point} to the point where the curve should end
	 * @param {Number} [parameter=0.5]
	 *
	 * @example {@paperscript height=300}
	 * // Interactive example. Click and drag in the view below:
	 *
	 * var myPath;
	 * function onMouseDrag(event) {
	 * 	// If we created a path before, remove it:
	 * 	if (myPath) {
	 * 	    myPath.remove();
	 * 	}
	 *
	 * 	// Create a new path and add a segment point to it
	 * 	// at {x: 150, y: 150):
	 * 	myPath = new Path();
	 * 	myPath.add(150, 150);
	 *
	 * 	// Draw a curve through the position of the mouse to 'toPoint'
	 * 	var toPoint = new Point(350, 150);
	 * 	myPath.curveTo(event.point, toPoint);
	 *
	 * 	// Select the path, so we can see its segments:
	 * 	myPath.selected = true;
	 * }
     *
	 * // When the mouse is released, deselect the path
	 * // and set its stroke-color to black:
	 * function onMouseUp(event) {
	 * 	myPath.selected = false;
	 * 	myPath.strokeColor = 'black';
	 * }
	 */

	/**
	 * Draws an arc from the position of the last segment point in the path that
	 * goes through the specified {@code through} point, to the specified
	 * {@code to} point by adding one or more segments to the path.
	 *
	 * @name PathItem#arcTo
	 * @function
	 * @param {Point} through the point where the arc should pass through
	 * @param {Point} to the point where the arc should end
	 *
	 * @example {@paperscript}
	 * var path = new Path();
	 * path.strokeColor = 'black';
	 *
	 * var firstPoint = new Point(30, 75);
	 * path.add(firstPoint);
	 *
	 * // The point through which we will create the arc:
	 * var throughPoint = new Point(40, 40);
	 *
	 * // The point at which the arc will end:
	 * var toPoint = new Point(130, 75);
	 *
	 * // Draw an arc through 'throughPoint' to 'toPoint'
	 * path.arcTo(throughPoint, toPoint);
	 *
	 * // Add a red circle shaped path at the position of 'throughPoint':
	 * var circle = new Path.Circle(throughPoint, 3);
	 * circle.fillColor = 'red';
	 *
	 * @example {@paperscript height=300}
	 * // Interactive example. Click and drag in the view below:
	 *
	 * var myPath;
	 * function onMouseDrag(event) {
	 * 	// If we created a path before, remove it:
	 * 	if (myPath) {
	 * 	    myPath.remove();
	 * 	}
	 *
	 * 	// Create a new path and add a segment point to it
	 * 	// at {x: 150, y: 150):
	 * 	myPath = new Path();
	 * 	myPath.add(150, 150);
	 *
	 * 	// Draw an arc through the position of the mouse to 'toPoint'
	 * 	var toPoint = new Point(350, 150);
	 * 	myPath.arcTo(event.point, toPoint);
	 *
	 * 	// Select the path, so we can see its segments:
	 * 	myPath.selected = true;
	 * }
	 *
	 * // When the mouse is released, deselect the path
	 * // and fill it with black.
	 * function onMouseUp(event) {
	 * 	myPath.selected = false;
	 * 	myPath.fillColor = 'black';
	 * }
	 */
	/**
	 * Draws an arc from the position of the last segment point in the path to
	 * the specified point by adding one or more segments to the path.
	 *
	 * @name PathItem#arcTo
	 * @function
	 * @param {Point} to the point where the arc should end
	 * @param {Boolean} [clockwise=true] specifies whether the arc should be
	 *        drawn in clockwise direction.
	 *
	 * @example {@paperscript}
	 * var path = new Path();
	 * path.strokeColor = 'black';
	 *
	 * path.add(new Point(30, 75));
	 * path.arcTo(new Point(130, 75));
	 *
	 * var path2 = new Path();
	 * path2.strokeColor = 'red';
	 * path2.add(new Point(180, 25));
	 *
	 * // To draw an arc in anticlockwise direction,
	 * // we pass 'false' as the second argument to arcTo:
	 * path2.arcTo(new Point(280, 25), false);
	 *
	 * @example {@paperscript height=300}
	 * // Interactive example. Click and drag in the view below:
	 * var myPath;
	 *
	 * // The mouse has to move at least 20 points before
	 * // the next mouse drag event is fired:
	 * tool.minDistance = 20;
	 *
	 * // When the user clicks, create a new path and add
	 * // the current mouse position to it as its first segment:
	 * function onMouseDown(event) {
	 * 	myPath = new Path();
	 * 	myPath.strokeColor = 'black';
	 * 	myPath.add(event.point);
	 * }
	 *
	 * // On each mouse drag event, draw an arc to the current
	 * // position of the mouse:
	 * function onMouseDrag(event) {
	 * 	myPath.arcTo(event.point);
	 * }
	 */

	/**
	 * Closes the path. When closed, Paper.js connects the first and last
	 * segments.
	 *
	 * @name PathItem#closePath
	 * @function
	 * @see Path#closed
	 */

	/**
	 * {@grouptitle Relative Drawing Commands}
	 *
	 * If called on a {@link CompoundPath}, a new {@link Path} is created as a
	 * child and the point is added as its first segment relative to the
	 * position of the last segment of the current path.
	 *
	 * @name PathItem#moveBy
	 * @function
	 * @param {Point} point
	 */

	/**
	 * Adds a segment relative to the last segment point of the path.
	 *
	 * @name PathItem#lineBy
	 * @function
	 * @param {Point} vector The vector which is added to the position of the
	 *        last segment of the path, to become the new segment.
	 *
	 * @example {@paperscript}
	 * var path = new Path();
	 * path.strokeColor = 'black';
	 *
	 * // Add a segment at {x: 50, y: 50}
	 * path.add(25, 25);
	 *
	 * // Add a segment relative to the last segment of the path.
	 * // 50 in x direction and 0 in y direction, becomes {x: 75, y: 25}
	 * path.lineBy(50, 0);
	 *
	 * // 0 in x direction and 50 in y direction, becomes {x: 75, y: 75}
	 * path.lineBy(0, 50);
	 *
	 * @example {@paperscript height=300}
	 * // Drawing a spiral using lineBy:
	 * var path = new Path();
	 * path.strokeColor = 'black';
	 *
	 * // Add the first segment at {x: 50, y: 50}
	 * path.add(view.center);
	 *
	 * // Loop 500 times:
	 * for (var i = 0; i < 500; i++) {
	 * 	// Create a vector with an ever increasing length
	 * 	// and an angle in increments of 45 degrees
	 * 	var vector = new Point({
	 * 	    angle: i * 45,
	 * 	    length: i / 2
	 * 	});
	 * 	// Add the vector relatively to the last segment point:
	 * 	path.lineBy(vector);
	 * }
	 *
	 * // Smooth the handles of the path:
	 * path.smooth();
	 *
	 * // Uncomment the following line and click on 'run' to see
	 * // the construction of the path:
	 * // path.selected = true;
	 */

	// DOCS: Document Path#curveBy
	/**
	 * @name PathItem#curveBy
	 * @function
	 * @param {Point} throughVector
	 * @param {Point} toVector
	 * @param {Number} [parameter=0.5]
	 */

	// DOCS: Document Path#arcBy
	/**
	 * @name PathItem#arcBy
	 * @function
	 * @param {Point} throughVector
	 * @param {Point} toVector
	 */
});
<commit_msg>Fix documentation for PathItem#smooth().<commit_after>/*
 * Paper.js
 *
 * This file is part of Paper.js, a JavaScript Vector Graphics Library,
 * based on Scriptographer.org and designed to be largely API compatible.
 * http://paperjs.org/
 * http://scriptographer.org/
 *
 * Distributed under the MIT license. See LICENSE file for details.
 *
 * Copyright (c) 2011, Juerg Lehni & Jonathan Puckey
 * http://lehni.org/ & http://jonathanpuckey.com/
 *
 * All rights reserved.
 */

var PathItem = this.PathItem = Item.extend(/** @scope PathItem */{
	/**
	 * @name PathItem
	 * @class
	 * @extends Item
	 */

	// Note: The PathItem class is currently empty but holds the documentation
	// for all the methods that exist both on Path and CompoundPath.

	/**
	 * Smooth bezier curves without changing the amount of segments or their
	 * points, by only smoothing and adjusting their handle points, for both
	 * open ended and closed paths.
	 *
	 * @name PathItem#smooth
	 * @function
	 *
	 * @example {@paperscript}
	 * // Smoothing a closed shape:
	 *
	 * // Create a rectangular path with its top-left point at
	 * // {x: 30, y: 25} and a size of {width: 50, height: 50}:
	 * var path = new Path.Rectangle(new Point(30, 25), new Size(50, 50));
	 * path.strokeColor = 'black';
	 *
	 * // Select the path, so we can see its handles:
	 * path.selected = true;
	 *
	 * // Create a copy of the path and move it 100pt to the right:
	 * var copy = path.clone();
	 * copy.position.x += 100;
	 *
	 * // Smooth the segments of the copy:
	 * copy.smooth();
	 *
	 * @example {@paperscript height=220}
	 * var path = new Path();
	 * path.strokeColor = 'black';
	 *
	 * path.add(new Point(30, 50));
	 *
	 * var y = 5;
	 * var x = 3;
	 *
	 * for (var i = 0; i < 28; i++) {
	 *     y *= -1.1;
	 *     x *= 1.1;
	 *     path.lineBy(x, y);
	 * }
	 *
	 * // Create a copy of the path and move it 100pt down:
	 * var copy = path.clone();
	 * copy.position.y += 120;
	 *
	 * // Set its stroke color to red:
	 * copy.strokeColor = 'red';
	 *
	 * // Smooth the segments of the copy:
	 * copy.smooth();
	 */

	/**
	 * {@grouptitle Postscript Style Drawing Commands}
	 *
	 * If called on a {@link CompoundPath}, a new {@link Path} is created as a
	 * child and the point is added as its first segment. On a normal empty
	 * {@link Path}, the point is simply added as its first segment.
	 *
	 * @name PathItem#moveTo
	 * @function
	 * @param {Point} point
	 */

	// DOCS: Document lineTo
	/**
	 * @name PathItem#lineTo
	 * @function
	 * @param {Point} point
	 */

	/**
	 * Adds a cubic bezier curve to the path, defined by two handles and a to
	 * point.
	 *
	 * @name PathItem#cubicCurveTo
	 * @function
	 * @param {Point} handle1
	 * @param {Point} handle2
	 * @param {Point} to
	 */

	/**
	 * Adds a quadratic bezier curve to the path, defined by a handle and a to
	 * point.
	 *
	 * @name PathItem#quadraticCurveTo
	 * @function
	 * @param {Point} handle
	 * @param {Point} to
	 */

	// DOCS: Document PathItem#curveTo 'paramater' param.
	/**
	 * Draws a curve from the position of the last segment point in the path
	 * that goes through the specified {@code through} point, to the specified
	 * {@code to} point by adding one segment to the path.
	 *
	 * @name PathItem#curveTo
	 * @function
	 * @param {Point} through the point through which the curve should go
	 * @param {Point} to the point where the curve should end
	 * @param {Number} [parameter=0.5]
	 *
	 * @example {@paperscript height=300}
	 * // Interactive example. Click and drag in the view below:
	 *
	 * var myPath;
	 * function onMouseDrag(event) {
	 * 	// If we created a path before, remove it:
	 * 	if (myPath) {
	 * 	    myPath.remove();
	 * 	}
	 *
	 * 	// Create a new path and add a segment point to it
	 * 	// at {x: 150, y: 150):
	 * 	myPath = new Path();
	 * 	myPath.add(150, 150);
	 *
	 * 	// Draw a curve through the position of the mouse to 'toPoint'
	 * 	var toPoint = new Point(350, 150);
	 * 	myPath.curveTo(event.point, toPoint);
	 *
	 * 	// Select the path, so we can see its segments:
	 * 	myPath.selected = true;
	 * }
     *
	 * // When the mouse is released, deselect the path
	 * // and set its stroke-color to black:
	 * function onMouseUp(event) {
	 * 	myPath.selected = false;
	 * 	myPath.strokeColor = 'black';
	 * }
	 */

	/**
	 * Draws an arc from the position of the last segment point in the path that
	 * goes through the specified {@code through} point, to the specified
	 * {@code to} point by adding one or more segments to the path.
	 *
	 * @name PathItem#arcTo
	 * @function
	 * @param {Point} through the point where the arc should pass through
	 * @param {Point} to the point where the arc should end
	 *
	 * @example {@paperscript}
	 * var path = new Path();
	 * path.strokeColor = 'black';
	 *
	 * var firstPoint = new Point(30, 75);
	 * path.add(firstPoint);
	 *
	 * // The point through which we will create the arc:
	 * var throughPoint = new Point(40, 40);
	 *
	 * // The point at which the arc will end:
	 * var toPoint = new Point(130, 75);
	 *
	 * // Draw an arc through 'throughPoint' to 'toPoint'
	 * path.arcTo(throughPoint, toPoint);
	 *
	 * // Add a red circle shaped path at the position of 'throughPoint':
	 * var circle = new Path.Circle(throughPoint, 3);
	 * circle.fillColor = 'red';
	 *
	 * @example {@paperscript height=300}
	 * // Interactive example. Click and drag in the view below:
	 *
	 * var myPath;
	 * function onMouseDrag(event) {
	 * 	// If we created a path before, remove it:
	 * 	if (myPath) {
	 * 	    myPath.remove();
	 * 	}
	 *
	 * 	// Create a new path and add a segment point to it
	 * 	// at {x: 150, y: 150):
	 * 	myPath = new Path();
	 * 	myPath.add(150, 150);
	 *
	 * 	// Draw an arc through the position of the mouse to 'toPoint'
	 * 	var toPoint = new Point(350, 150);
	 * 	myPath.arcTo(event.point, toPoint);
	 *
	 * 	// Select the path, so we can see its segments:
	 * 	myPath.selected = true;
	 * }
	 *
	 * // When the mouse is released, deselect the path
	 * // and fill it with black.
	 * function onMouseUp(event) {
	 * 	myPath.selected = false;
	 * 	myPath.fillColor = 'black';
	 * }
	 */
	/**
	 * Draws an arc from the position of the last segment point in the path to
	 * the specified point by adding one or more segments to the path.
	 *
	 * @name PathItem#arcTo
	 * @function
	 * @param {Point} to the point where the arc should end
	 * @param {Boolean} [clockwise=true] specifies whether the arc should be
	 *        drawn in clockwise direction.
	 *
	 * @example {@paperscript}
	 * var path = new Path();
	 * path.strokeColor = 'black';
	 *
	 * path.add(new Point(30, 75));
	 * path.arcTo(new Point(130, 75));
	 *
	 * var path2 = new Path();
	 * path2.strokeColor = 'red';
	 * path2.add(new Point(180, 25));
	 *
	 * // To draw an arc in anticlockwise direction,
	 * // we pass 'false' as the second argument to arcTo:
	 * path2.arcTo(new Point(280, 25), false);
	 *
	 * @example {@paperscript height=300}
	 * // Interactive example. Click and drag in the view below:
	 * var myPath;
	 *
	 * // The mouse has to move at least 20 points before
	 * // the next mouse drag event is fired:
	 * tool.minDistance = 20;
	 *
	 * // When the user clicks, create a new path and add
	 * // the current mouse position to it as its first segment:
	 * function onMouseDown(event) {
	 * 	myPath = new Path();
	 * 	myPath.strokeColor = 'black';
	 * 	myPath.add(event.point);
	 * }
	 *
	 * // On each mouse drag event, draw an arc to the current
	 * // position of the mouse:
	 * function onMouseDrag(event) {
	 * 	myPath.arcTo(event.point);
	 * }
	 */

	/**
	 * Closes the path. When closed, Paper.js connects the first and last
	 * segments.
	 *
	 * @name PathItem#closePath
	 * @function
	 * @see Path#closed
	 */

	/**
	 * {@grouptitle Relative Drawing Commands}
	 *
	 * If called on a {@link CompoundPath}, a new {@link Path} is created as a
	 * child and the point is added as its first segment relative to the
	 * position of the last segment of the current path.
	 *
	 * @name PathItem#moveBy
	 * @function
	 * @param {Point} point
	 */

	/**
	 * Adds a segment relative to the last segment point of the path.
	 *
	 * @name PathItem#lineBy
	 * @function
	 * @param {Point} vector The vector which is added to the position of the
	 *        last segment of the path, to become the new segment.
	 *
	 * @example {@paperscript}
	 * var path = new Path();
	 * path.strokeColor = 'black';
	 *
	 * // Add a segment at {x: 50, y: 50}
	 * path.add(25, 25);
	 *
	 * // Add a segment relative to the last segment of the path.
	 * // 50 in x direction and 0 in y direction, becomes {x: 75, y: 25}
	 * path.lineBy(50, 0);
	 *
	 * // 0 in x direction and 50 in y direction, becomes {x: 75, y: 75}
	 * path.lineBy(0, 50);
	 *
	 * @example {@paperscript height=300}
	 * // Drawing a spiral using lineBy:
	 * var path = new Path();
	 * path.strokeColor = 'black';
	 *
	 * // Add the first segment at {x: 50, y: 50}
	 * path.add(view.center);
	 *
	 * // Loop 500 times:
	 * for (var i = 0; i < 500; i++) {
	 * 	// Create a vector with an ever increasing length
	 * 	// and an angle in increments of 45 degrees
	 * 	var vector = new Point({
	 * 	    angle: i * 45,
	 * 	    length: i / 2
	 * 	});
	 * 	// Add the vector relatively to the last segment point:
	 * 	path.lineBy(vector);
	 * }
	 *
	 * // Smooth the handles of the path:
	 * path.smooth();
	 *
	 * // Uncomment the following line and click on 'run' to see
	 * // the construction of the path:
	 * // path.selected = true;
	 */

	// DOCS: Document Path#curveBy
	/**
	 * @name PathItem#curveBy
	 * @function
	 * @param {Point} throughVector
	 * @param {Point} toVector
	 * @param {Number} [parameter=0.5]
	 */

	// DOCS: Document Path#arcBy
	/**
	 * @name PathItem#arcBy
	 * @function
	 * @param {Point} throughVector
	 * @param {Point} toVector
	 */
});
<|endoftext|>


<commit_before>define([
    'require',
    '../logging',
    '../patterns'
], function(require) {
    var log = require('../logging').getLogger('edit');

    // Grab a element of the editor controls
    var init = function($el, opts) {
        var $form    = $el.parents('form'),
            $ctrls   = $('.editor-ctrls'),
            buttons  = {};

        // ensure form is ajaxified
        var ajaxify = require('../patterns').ajaxify.init;
        ajaxify($form);

        buttons.bold                = $ctrls.find('.strong');
        buttons.italic              = $ctrls.find('.emphasised');
        buttons.insertorderedlist   = $ctrls.find('.list-ordered');
        buttons.insertunorderedlist = $ctrls.find('.list-unordered');
        //buttons.insertparagraph     = $ctrls.find('.paragraph');
        buttons.clear               = $ctrls.find('.clear');
        buttons.inserth1            = $ctrls.find('.header_1');
        buttons.inserth2            = $ctrls.find('.header_2');
        buttons.inserth3            = $ctrls.find('.header_3');
        buttons.upload_image        = $ctrls.find('.upload_image');
        buttons.link_image          = $ctrls.find('.link_image');

        var button_handler = {
            'bold'                  : function(){ document.execCommand('bold'); },

            'italic'                : function(){ document.execCommand('italic'); },

            'insertparagraph'       : function(){ return 0; },

            'insertorderedlist'     : function(){ document.execCommand('insertorderedlist'); },

            'insertunorderedlist'   : function(){ document.execCommand('insertunorderedlist'); },

            'inserth1'              : function(){ wrap_selection('h1') },

            'inserth2'              : function(){ wrap_selection('h2') },

            'inserth3'              : function(){ wrap_selection('h3') },

            'clear'                 : function(){
                var selection_node = $(window.getSelection().anchorNode);
                document.execCommand('removeformat');
                if (is_contenteditable(selection_node)) {
                    selection_node.unwrap();
                    if (!$(selection_node).parent().is("p")) {
                        $(selection_node).wrap('<p>');
                    }
                }
            },

            'upload_image'          : function(){ document.execCommand(); },

            'link_image'            : function(){
                var source = prompt('URL of Image');
                if(source) {
                    document.execCommand('insertImage', false, source);
                }
            }
        };

        // utility method for determining if something is contenteditable
        var is_contenteditable = function(element) {
            mapped_elements = $(element).parents().map(function() {
                if ($(this).is("[contenteditable='true']")) {
                    return true;
                }
            }).get();

            // mapped_element[0] is the closest parent
            // in the document tree -- If it's true
            // the element will inherit contentEditable
            if (mapped_elements[0]) { return true; }
            return false;
        };

        // simply replaces
        // rather than toggles, but
        // theres no reason you couldn't have
        // it do both!
        var wrap_selection = function(wrap_html){
            var selection_node = $(window.getSelection().anchorNode);
            if(is_contenteditable(selection_node)) {
                // You just want to unwrap if your
                // parent is already selected
                if(selection_node.parent().is(wrap_html)) {
                    selection_node.unwrap();
                }
                else {
                    selection_node.wrap("<" + wrap_html + ">");
                }
            }
            // wrap() normally breaks contentEditable
            // this is a hacky replacement
            selection_node.attr('contenteditable', true);
        };

        // lame helper that
        // toggles the class,
        // triggers the handler
        var button_click = function(element) {
            buttons[element].toggleClass('selected');
            button_handler[element]();
        };

        // bind click to button_click()/1
        for (var key in buttons) {
            buttons[key].click(function(element){
                return function() {
                    log.debug('clicked', element);
                    button_click(element);
                };
            }(key));
        }

        // Enables contentEditable
        $('form').attr('contenteditable','true');

        var setstate = function(selection) {
            var markup = selection.markupEffectiveAtStart;
            if (!markup) return;
            $ctrls.find('*').removeClass('selected');
            $.each(markup, function(idx, el) {
                var tag = el.nodeName.toLowerCase(),
                    $button = buttons[tag];
                if ($button) $button.addClass('selected');
                log.debug('selected', tag);
            });
        };
    };

    var pattern = {
        markup_trigger: 'form textarea.edit',
        initialised_class: 'edit',
        init: init
    };
    return pattern;
});
<commit_msg>"convert" textarea into contenteditable div and copy back before form serialize<commit_after>define([
    'require',
    '../logging',
    '../patterns'
], function(require) {
    var log = require('../logging').getLogger('edit');

    var text2div = function($el) {
        // hide textarea
        $el.hide();

        // make sure textarea has an id
        // XXX: generate something proper
        var id = $el.attr('id');
        if (!id) {
            id = "my-id-that-should-be-replaced-by-something-unique";
            $el.attr({id: id});
        }

        // create contenteditable div
        var editid = 'edit-' + id,
            $edit = $('<div id="' + editid + '" contenteditable="true"/>').insertAfter($el);
        $edit.html($el.val());
        $edit.attr({style: 'min-height: 50px'});

        // ensure form is ajaxified and copy content back before serialize
        var ajaxify = require('../patterns').ajaxify.init,
            $form    = $el.parents('form');
        ajaxify($form);
        $form.on('form-pre-serialize', function() {
            $el.html($edit.html());
        });

        return $edit;
    };

    // Grab a element of the editor controls
    var init = function($el, opts) {
        // copy textarea content to div and hide textarea
        var $edit = text2div($el);

        var $ctrls   = $('.editor-ctrls'),
            buttons  = {};

        buttons.bold                = $ctrls.find('.strong');
        buttons.italic              = $ctrls.find('.emphasised');
        buttons.insertorderedlist   = $ctrls.find('.list-ordered');
        buttons.insertunorderedlist = $ctrls.find('.list-unordered');
        //buttons.insertparagraph     = $ctrls.find('.paragraph');
        buttons.clear               = $ctrls.find('.clear');
        buttons.inserth1            = $ctrls.find('.header_1');
        buttons.inserth2            = $ctrls.find('.header_2');
        buttons.inserth3            = $ctrls.find('.header_3');
        buttons.upload_image        = $ctrls.find('.upload_image');
        buttons.link_image          = $ctrls.find('.link_image');

        var button_handler = {
            'bold'                  : function(){ document.execCommand('bold'); },

            'italic'                : function(){ document.execCommand('italic'); },

            'insertparagraph'       : function(){ return 0; },

            'insertorderedlist'     : function(){ document.execCommand('insertorderedlist'); },

            'insertunorderedlist'   : function(){ document.execCommand('insertunorderedlist'); },

            'inserth1'              : function(){ wrap_selection('h1') },

            'inserth2'              : function(){ wrap_selection('h2') },

            'inserth3'              : function(){ wrap_selection('h3') },

            'clear'                 : function(){
                var selection_node = $(window.getSelection().anchorNode);
                document.execCommand('removeformat');
                if (is_contenteditable(selection_node)) {
                    selection_node.unwrap();
                    if (!$(selection_node).parent().is("p")) {
                        $(selection_node).wrap('<p>');
                    }
                }
            },

            'upload_image'          : function(){ document.execCommand(); },

            'link_image'            : function(){
                var source = prompt('URL of Image');
                if(source) {
                    document.execCommand('insertImage', false, source);
                }
            }
        };

        // utility method for determining if something is contenteditable
        var is_contenteditable = function(element) {
            mapped_elements = $(element).parents().map(function() {
                if ($(this).is("[contenteditable='true']")) {
                    return true;
                }
            }).get();

            // mapped_element[0] is the closest parent
            // in the document tree -- If it's true
            // the element will inherit contentEditable
            if (mapped_elements[0]) { return true; }
            return false;
        };

        // simply replaces
        // rather than toggles, but
        // theres no reason you couldn't have
        // it do both!
        var wrap_selection = function(wrap_html){
            var selection_node = $(window.getSelection().anchorNode);
            if(is_contenteditable(selection_node)) {
                // You just want to unwrap if your
                // parent is already selected
                if(selection_node.parent().is(wrap_html)) {
                    selection_node.unwrap();
                }
                else {
                    selection_node.wrap("<" + wrap_html + ">");
                }
            }
            // wrap() normally breaks contentEditable
            // this is a hacky replacement
            selection_node.attr('contenteditable', true);
        };

        // lame helper that
        // toggles the class,
        // triggers the handler
        var button_click = function(element) {
            buttons[element].toggleClass('selected');
            button_handler[element]();
        };

        // bind click to button_click()/1
        for (var key in buttons) {
            buttons[key].click(function(element){
                return function() {
                    log.debug('clicked', element);
                    button_click(element);
                };
            }(key));
        }

        // Enables contentEditable
        $('form').attr('contenteditable','true');

        var setstate = function(selection) {
            var markup = selection.markupEffectiveAtStart;
            if (!markup) return;
            $ctrls.find('*').removeClass('selected');
            $.each(markup, function(idx, el) {
                var tag = el.nodeName.toLowerCase(),
                    $button = buttons[tag];
                if ($button) $button.addClass('selected');
                log.debug('selected', tag);
            });
        };
    };

    var pattern = {
        markup_trigger: 'form textarea.edit',
        initialised_class: 'edit',
        init: init
    };
    return pattern;
});
<|endoftext|>


<commit_before>function getStorageValue(item, default_value){
    var retVal = localStorage.getItem(item);
    //console.log('value' + item + ': ' + String(retVal));
    if (retVal === null || retVal == 'undefined' || retVal == 'null'){
        retVal = default_value;
    }
    return retVal;
}

/**
 * Overwrites obj1's values with obj2's and adds obj2's if non existent in obj1
 * @param obj1
 * @param obj2
 * @returns obj3 a new object based on obj1 and obj2
 * Similar to ES6 Object.assign() but without side effects and works with older js implementations
 * See
 *    * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
 *    * http://stackoverflow.com/questions/171251/how-can-i-merge-properties-of-two-javascript-objects-dynamically
 */
function merge_options(obj1, obj2){
    var obj3 = {};
    var attrname;

    for (attrname in obj1) { obj3[attrname] = obj1[attrname]; }
    for (attrname in obj2) { obj3[attrname] = obj2[attrname]; }
    return obj3;
}

Pebble.addEventListener('showConfiguration', function(e) {
    /* DEBUG test old settings are handled and cleaned up */
    /*
            localStorage.setItem('background_color', '0000FF');
            localStorage.setItem('time_color', 'FFFF00');
            localStorage.setItem('vibrate_disconnect', true);
    */
    /* DEBUG */

    var URL = 'http://clach04.github.io/pebble/watchface_framework/slate/index.html';

    var default_dict = {
        version_settings: 0,  // Bump this when ever config storage changes
        // http://developer.getpebble.com/tools/color-picker/
        background_color: '000000',  // GColorBlack
        time_color: 'FFFFFF',  // GColorWhite
        vibrate_disconnect: 0
    };
    var stored_dict_str = getStorageValue('stored_dict', null);
    console.log('stored_dict_str: ' + stored_dict_str);
    var stored_dict;
    if (stored_dict_str === null)
    {
        stored_dict = {};
    }
    else
    {
        stored_dict = JSON.parse(stored_dict_str);
    }
    console.log('stored_dict: ' + JSON.stringify(stored_dict));
    console.log('default_dict: ' + JSON.stringify(default_dict));

    /* Handle old config - not stored in dict */
    console.log('Checking old (non-dict) config items');
    var old_config_names = ['background_color', 'time_color', 'vibrate_disconnect'];
    for (var i in old_config_names)
    {
        var config_name=old_config_names[i];
        var tmp_setting;
        console.log('i=' + i + '=' + config_name);
        tmp_setting = getStorageValue(config_name, null);
        if (tmp_setting !== null)
        {
            default_dict[config_name] = tmp_setting;
            console.log('about to remove old localStorage item ' + config_name);
            localStorage.removeItem(config_name);
        }
    }

    var configuration = merge_options(default_dict, stored_dict);
    if (Pebble.getActiveWatchInfo)
    {
        /*
        ** SDK v3 or higher.
        ** E.g. Basalt, Chalk, or Aplite with 3.8+
        */
        var info = Pebble.getActiveWatchInfo();
        console.log('Pebble info: ' + JSON.stringify(info));
        configuration.pebble_platform = info.platform;
        configuration.pebble_model = info.model;
        configuration.pebble_language = info.language;
        /*
        ** Useful info:
            platform: aplite|basalt|chalk
            model: qemu_platform_aplite|qemu_platform_basalt|qemu_platform_chalk|pebble_time_black

        Seen in emulator:
        {
            "platform": "aplite",
            "model": "qemu_platform_aplite",
            "firmware": {
                "major": 3,
                "suffix": "",
                "minor": 3,
                "patch": 1
            },
            "language": "en_US"
        }
        {
            "platform": "basalt",
            "model": "qemu_platform_basalt",
            "firmware": {
                "major": 3,
                "suffix": "",
                "minor": 3,
                "patch": 1
            },
            "language": "en_US"
        }
        {
            "platform": "chalk",
            "model": "qemu_platform_chalk",
            "firmware": {
                "major": 3,
                "suffix": "",
                "minor": 3,
                "patch": 1
            },
            "language": "en_US"
        }
        */
    }
    else
    {
        /*
        ** Info NOT available with SDK pre v3. Probably old Aplite.
        ** NOTE emulator with 3.8 SDK for aplite does have this info.
        */
    }
    console.log('configuration: ' + JSON.stringify(configuration));

    var param_array = [];
    var temp_str;
    var config_key;
    for (config_key in configuration)
    {
        console.log('config_key: ' + config_key + '=' + configuration[config_key]);
        temp_str = config_key + '=' + encodeURIComponent(configuration[config_key]);
        param_array.push(temp_str);
    }
    var URL_with_params = URL + '?' + param_array.join('&');
    console.log('Configuration window opened. ' + URL_with_params);
    Pebble.openURL(URL_with_params);
});

Pebble.addEventListener('webviewclosed',
    function(e) {
        console.log('e.response: ' + e.response);
        console.log('e.response.length: ' + e.response.length);
        try {
            var configuration = JSON.parse(decodeURIComponent(e.response));
            var vibrate_disconnect = 0;

            console.log('dictionary to validate ' + JSON.stringify(configuration));
            /*
            ** configuration can from an external web site, this is not trustworthy
            ** and so needs validation. This avoids needing to add lots of
            ** protection to the C code on the Pebble.
            */
            // TODO as configuration is untrusted and may be missing values, merge in from default_dict

            /* even though we don't realy trust `configuration`, store it in local phone storage */
            // TODO store dictionary instead - which stores number differently
            console.log('store config on phone');
            localStorage.setItem('stored_dict', JSON.stringify(configuration));

            // Send to Pebble
            if ('vibrate_disconnect' in configuration)
            {
                switch (configuration.vibrate_disconnect) {
                    case true:
                    case 'true':
                    case 'True':
                    case 'TRUE':
                    case 1:
                    case '1':
                    case 'on':
                        vibrate_disconnect = 1;
                        break;
                    default:
                        vibrate_disconnect = 0;
                        break;
                }
            }
            var dictionary = {
              "KEY_TIME_COLOR": parseInt(configuration.time_color, 16),
              "KEY_BACKGROUND_COLOR": parseInt(configuration.background_color, 16),
              "KEY_VIBRATE_ON_DISCONNECT": vibrate_disconnect
            };

            console.log('dictionary to send to Pebble' + JSON.stringify(dictionary));
            Pebble.sendAppMessage(dictionary,
                function(e) {
                    console.log("Configuration sent to Pebble successfully!");
                },
                function(e) {
                    console.log("Error sending configuration info to Pebble!");
                }
            );
        } catch (ex) {
            // If we have SyntaxError JSON is invalid, anything unknown!?
            if (ex instanceof SyntaxError) {
                console.log('Probably Cancelled');
            } else {
                throw ex;
                // See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SyntaxError
            }
        }
    }
);
<commit_msg>Move defaults and URL to globals at head of js src<commit_after>var URL = 'http://clach04.github.io/pebble/watchface_framework/slate/index.html';

var default_dict = {
    version_settings: 0,  // Bump this when ever config storage changes
    // http://developer.getpebble.com/tools/color-picker/
    background_color: '000000',  // GColorBlack
    time_color: 'FFFFFF',  // GColorWhite
    vibrate_disconnect: 0
};

function getStorageValue(item, default_value){
    var retVal = localStorage.getItem(item);
    //console.log('value' + item + ': ' + String(retVal));
    if (retVal === null || retVal == 'undefined' || retVal == 'null'){
        retVal = default_value;
    }
    return retVal;
}

/**
 * Overwrites obj1's values with obj2's and adds obj2's if non existent in obj1
 * @param obj1
 * @param obj2
 * @returns obj3 a new object based on obj1 and obj2
 * Similar to ES6 Object.assign() but without side effects and works with older js implementations
 * See
 *    * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
 *    * http://stackoverflow.com/questions/171251/how-can-i-merge-properties-of-two-javascript-objects-dynamically
 */
function merge_options(obj1, obj2){
    var obj3 = {};
    var attrname;

    for (attrname in obj1) { obj3[attrname] = obj1[attrname]; }
    for (attrname in obj2) { obj3[attrname] = obj2[attrname]; }
    return obj3;
}

Pebble.addEventListener('showConfiguration', function(e) {
    /* DEBUG test old settings are handled and cleaned up */
    /*
            localStorage.setItem('background_color', '0000FF');
            localStorage.setItem('time_color', 'FFFF00');
            localStorage.setItem('vibrate_disconnect', true);
    */
    /* DEBUG */

    var stored_dict_str = getStorageValue('stored_dict', null);
    console.log('stored_dict_str: ' + stored_dict_str);
    var stored_dict;
    if (stored_dict_str === null)
    {
        stored_dict = {};
    }
    else
    {
        stored_dict = JSON.parse(stored_dict_str);
    }
    console.log('stored_dict: ' + JSON.stringify(stored_dict));
    console.log('default_dict: ' + JSON.stringify(default_dict));

    /* Handle old config - not stored in dict */
    console.log('Checking old (non-dict) config items');
    var old_config_names = ['background_color', 'time_color', 'vibrate_disconnect'];
    for (var i in old_config_names)
    {
        var config_name=old_config_names[i];
        var tmp_setting;
        console.log('i=' + i + '=' + config_name);
        tmp_setting = getStorageValue(config_name, null);
        if (tmp_setting !== null)
        {
            default_dict[config_name] = tmp_setting;
            console.log('about to remove old localStorage item ' + config_name);
            localStorage.removeItem(config_name);
        }
    }

    var configuration = merge_options(default_dict, stored_dict);
    if (Pebble.getActiveWatchInfo)
    {
        /*
        ** SDK v3 or higher.
        ** E.g. Basalt, Chalk, or Aplite with 3.8+
        */
        var info = Pebble.getActiveWatchInfo();
        console.log('Pebble info: ' + JSON.stringify(info));
        configuration.pebble_platform = info.platform;
        configuration.pebble_model = info.model;
        configuration.pebble_language = info.language;
        /*
        ** Useful info:
            platform: aplite|basalt|chalk
            model: qemu_platform_aplite|qemu_platform_basalt|qemu_platform_chalk|pebble_time_black

        Seen in emulator:
        {
            "platform": "aplite",
            "model": "qemu_platform_aplite",
            "firmware": {
                "major": 3,
                "suffix": "",
                "minor": 3,
                "patch": 1
            },
            "language": "en_US"
        }
        {
            "platform": "basalt",
            "model": "qemu_platform_basalt",
            "firmware": {
                "major": 3,
                "suffix": "",
                "minor": 3,
                "patch": 1
            },
            "language": "en_US"
        }
        {
            "platform": "chalk",
            "model": "qemu_platform_chalk",
            "firmware": {
                "major": 3,
                "suffix": "",
                "minor": 3,
                "patch": 1
            },
            "language": "en_US"
        }
        */
    }
    else
    {
        /*
        ** Info NOT available with SDK pre v3. Probably old Aplite.
        ** NOTE emulator with 3.8 SDK for aplite does have this info.
        */
    }
    console.log('configuration: ' + JSON.stringify(configuration));

    var param_array = [];
    var temp_str;
    var config_key;
    for (config_key in configuration)
    {
        console.log('config_key: ' + config_key + '=' + configuration[config_key]);
        temp_str = config_key + '=' + encodeURIComponent(configuration[config_key]);
        param_array.push(temp_str);
    }
    var URL_with_params = URL + '?' + param_array.join('&');
    console.log('Configuration window opened. ' + URL_with_params);
    Pebble.openURL(URL_with_params);
});

Pebble.addEventListener('webviewclosed',
    function(e) {
        console.log('e.response: ' + e.response);
        console.log('e.response.length: ' + e.response.length);
        try {
            var configuration = JSON.parse(decodeURIComponent(e.response));
            var vibrate_disconnect = 0;

            console.log('dictionary to validate ' + JSON.stringify(configuration));
            /*
            ** configuration can from an external web site, this is not trustworthy
            ** and so needs validation. This avoids needing to add lots of
            ** protection to the C code on the Pebble.
            */
            // TODO as configuration is untrusted and may be missing values, merge in from default_dict

            /* even though we don't realy trust `configuration`, store it in local phone storage */
            // TODO store dictionary instead - which stores number differently
            console.log('store config on phone');
            localStorage.setItem('stored_dict', JSON.stringify(configuration));

            // Send to Pebble
            if ('vibrate_disconnect' in configuration)
            {
                switch (configuration.vibrate_disconnect) {
                    case true:
                    case 'true':
                    case 'True':
                    case 'TRUE':
                    case 1:
                    case '1':
                    case 'on':
                        vibrate_disconnect = 1;
                        break;
                    default:
                        vibrate_disconnect = 0;
                        break;
                }
            }
            var dictionary = {
              "KEY_TIME_COLOR": parseInt(configuration.time_color, 16),
              "KEY_BACKGROUND_COLOR": parseInt(configuration.background_color, 16),
              "KEY_VIBRATE_ON_DISCONNECT": vibrate_disconnect
            };

            console.log('dictionary to send to Pebble' + JSON.stringify(dictionary));
            Pebble.sendAppMessage(dictionary,
                function(e) {
                    console.log("Configuration sent to Pebble successfully!");
                },
                function(e) {
                    console.log("Error sending configuration info to Pebble!");
                }
            );
        } catch (ex) {
            // If we have SyntaxError JSON is invalid, anything unknown!?
            if (ex instanceof SyntaxError) {
                console.log('Probably Cancelled');
            } else {
                throw ex;
                // See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SyntaxError
            }
        }
    }
);
<|endoftext|>


<commit_before>/**
 * This Vue filters are widely used throughtout the application.
 * They're could be reused importing this module:
 *
 * import { VueFiltersMixin } from "lib/shared";
 *
 * and then, in a Vue App, like so:
 *
 * mixins: [VueFiltersMixin],
 *
 */
export const translate = (value = {}) => {
  const lang = I18n.locale || "es";
  // Look for the language key, fallback to the first found key
  return (Object.prototype.hasOwnProperty.call(value, lang) && value[lang]) ? value[lang] : value[Object.keys(value)[0]];
}

export const money = (value, opts = {}) => {
  const lang = I18n.locale || "es";
  const options = { style: "currency", currency: "EUR", ...opts }
  return value !== undefined && value !== null ? parseFloat(value).toLocaleString(lang, options) : undefined;
}

export const date = (value, opts = {}) => {
  const lang = I18n.locale || "es";
  return value instanceof Date ? value.toLocaleDateString(lang, opts) : new Date(value).toLocaleDateString(lang, opts);
}

export const truncate = (value, opts = {}) => {
  debugger

  const omission = opts['omission'] || '...';
  const length = opts['length'] || 30;

  return `${value.substring(0, length)}${omission}`
}

export const VueFiltersMixin = {
  methods: {
    translate,
    money,
    date,
    truncate
  },
  filters: {
    translate,
    money,
    date,
    truncate
  }
};
<commit_msg>Missing debugger<commit_after>/**
 * This Vue filters are widely used throughtout the application.
 * They're could be reused importing this module:
 *
 * import { VueFiltersMixin } from "lib/shared";
 *
 * and then, in a Vue App, like so:
 *
 * mixins: [VueFiltersMixin],
 *
 */
export const translate = (value = {}) => {
  const lang = I18n.locale || "es";
  // Look for the language key, fallback to the first found key
  return (Object.prototype.hasOwnProperty.call(value, lang) && value[lang]) ? value[lang] : value[Object.keys(value)[0]];
}

export const money = (value, opts = {}) => {
  const lang = I18n.locale || "es";
  const options = { style: "currency", currency: "EUR", ...opts }
  return value !== undefined && value !== null ? parseFloat(value).toLocaleString(lang, options) : undefined;
}

export const date = (value, opts = {}) => {
  const lang = I18n.locale || "es";
  return value instanceof Date ? value.toLocaleDateString(lang, opts) : new Date(value).toLocaleDateString(lang, opts);
}

export const truncate = (value, opts = {}) => {
  const omission = opts['omission'] || '...';
  const length = opts['length'] || 30;

  return `${value.substring(0, length)}${omission}`
}

export const VueFiltersMixin = {
  methods: {
    translate,
    money,
    date,
    truncate
  },
  filters: {
    translate,
    money,
    date,
    truncate
  }
};
<|endoftext|>


<commit_before>import template from './project-manage.html';

// @ngInject
class ProjectManageController {
  constructor(projectsService,
              currentStateService,
              customersService,
              certificationsService,
              WorkspaceService,
              ncUtilsFlash,
              $rootScope,
              $state,
              $q) {
    this.projectsService = projectsService;
    this.currentStateService = currentStateService;
    this.customersService = customersService;
    this.certificationsService = certificationsService;
    this.WorkspaceService = WorkspaceService;
    this.ncUtilsFlash = ncUtilsFlash;
    this.$rootScope = $rootScope;
    this.$state = $state;
    this.$q = $q;
  }

  $onInit() {
    this.workspace = this.WorkspaceService.getWorkspace().workspace;
    this.canManage = false;
    this.projectModel = {};
    this.loading = true;
    this.$q.all([
      this.currentStateService.getCustomer().then(customer => {
        this.customer = customer;
      }),
      this.customersService.isOwnerOrStaff().then(canManage => {
        this.canManage = canManage;
      }),
      this.certificationsService.getAll().then(certifications => {
        this.certifications = certifications;
      }),
      this.loadProject(),
    ]).then(() => {
      this.projectCertifications = angular.copy(this.project.certifications);
      this.certificationsList = this.project.certifications.map(x => x.name).join(', ');
    }).finally(() => this.loading = false);
  }

  loadProject() {
    if (this.project) {
      this.projectModel = angular.copy(this.project);
      return;
    }
    return this.currentStateService.getProject().then(project => {
      this.project = project;
      this.projectModel = angular.copy(project);
    });
  }

  saveProject() {
    if (this.ProjectForm.$invalid) {
      return this.$q.reject();
    }
    return this.updateProject();
  }

  updateProject() {
    return this.projectsService.updateProject(this.project.url, {
      name: this.projectModel.name,
      description: this.projectModel.description,
    }).then(response => {
      const project = response.data;
      this.ncUtilsFlash.success(gettext('Project has been updated.'));

      let item = this.customer.projects.filter(item => item.uuid === project.uuid)[0];
      item.name = project.name;

      if (this.workspace === 'organization') {
        this.$rootScope.$broadcast('refreshProjectList');
        return;
      }
      // TODO: Migrate to Redux and make code DRY
      this.currentStateService.setProject(project);
      this.WorkspaceService.setWorkspace({
        customer: this.customer,
        project: project,
        hasCustomer: true,
        workspace: 'project',
      });
    }, response => {
      this.errors = response.data;
    });
  }

  updateCertifications() {
    function mapItems(items) {
      return items.map(item => ({url: item.url}));
    }
    const oldItems = mapItems(this.project.certifications);
    const newItems = mapItems(this.projectCertifications);
    if (angular.equals(oldItems, newItems)) {
      return this.$q.resolve();
    } else {
      return this.projectsService.updateCertifications(this.project.url, newItems).then(response => {
        let item = this.customer.projects.filter(item => item.uuid === this.project.uuid)[0];
        item.certifications = this.project.certifications;
        this.projectsService.clearAllCacheForCurrentEndpoint();

        if (this.workspace === 'organization') {
          this.$rootScope.$broadcast('refreshProjectList');
          return;
        }
      });
    }
  }

  removeProject() {
    var confirmDelete = confirm(gettext('Confirm deletion?'));
    if (!confirmDelete) {
      return this.$q.reject();
    }
    this.currentStateService.setProject(null);
    return this.project.$delete().then(() => {
      this.projectsService.clearAllCacheForCurrentEndpoint();
      return this.projectsService.getFirst().then(project => {
        this.currentStateService.setProject(project);
        this.$rootScope.$broadcast('refreshProjectList', {model: this.project, remove: true});
      });
    }, () => {
      this.currentStateService.setProject(this.project);
    }).then(() => {
      this.currentStateService.reloadCurrentCustomer(customer => {
        this.$rootScope.$broadcast('checkQuotas:refresh');
        this.$rootScope.$broadcast('customerBalance:refresh');
        this.$state.go('organization.projects', {uuid: customer.uuid});
      });
    });
  }
}

const projectManage = {
  template: template,
  controller: ProjectManageController,
  bindings: {
    project: '<'
  }
};

export default projectManage;
<commit_msg>Fix lint [WAL-700]<commit_after>import template from './project-manage.html';

// @ngInject
class ProjectManageController {
  constructor(projectsService,
              currentStateService,
              customersService,
              certificationsService,
              WorkspaceService,
              ncUtilsFlash,
              $rootScope,
              $state,
              $q) {
    this.projectsService = projectsService;
    this.currentStateService = currentStateService;
    this.customersService = customersService;
    this.certificationsService = certificationsService;
    this.WorkspaceService = WorkspaceService;
    this.ncUtilsFlash = ncUtilsFlash;
    this.$rootScope = $rootScope;
    this.$state = $state;
    this.$q = $q;
  }

  $onInit() {
    this.workspace = this.WorkspaceService.getWorkspace().workspace;
    this.canManage = false;
    this.projectModel = {};
    this.loading = true;
    this.$q.all([
      this.currentStateService.getCustomer().then(customer => {
        this.customer = customer;
      }),
      this.customersService.isOwnerOrStaff().then(canManage => {
        this.canManage = canManage;
      }),
      this.certificationsService.getAll().then(certifications => {
        this.certifications = certifications;
      }),
      this.loadProject(),
    ]).then(() => {
      this.projectCertifications = angular.copy(this.project.certifications);
      this.certificationsList = this.project.certifications.map(x => x.name).join(', ');
    }).finally(() => this.loading = false);
  }

  loadProject() {
    if (this.project) {
      this.projectModel = angular.copy(this.project);
      return;
    }
    return this.currentStateService.getProject().then(project => {
      this.project = project;
      this.projectModel = angular.copy(project);
    });
  }

  saveProject() {
    if (this.ProjectForm.$invalid) {
      return this.$q.reject();
    }
    return this.updateProject();
  }

  updateProject() {
    return this.projectsService.updateProject(this.project.url, {
      name: this.projectModel.name,
      description: this.projectModel.description,
    }).then(response => {
      const project = response.data;
      this.ncUtilsFlash.success(gettext('Project has been updated.'));

      let item = this.customer.projects.filter(item => item.uuid === project.uuid)[0];
      item.name = project.name;

      if (this.workspace === 'organization') {
        this.$rootScope.$broadcast('refreshProjectList');
        return;
      }
      // TODO: Migrate to Redux and make code DRY
      this.currentStateService.setProject(project);
      this.WorkspaceService.setWorkspace({
        customer: this.customer,
        project: project,
        hasCustomer: true,
        workspace: 'project',
      });
    }, response => {
      this.errors = response.data;
    });
  }

  updateCertifications() {
    function mapItems(items) {
      return items.map(item => ({url: item.url}));
    }
    const oldItems = mapItems(this.project.certifications);
    const newItems = mapItems(this.projectCertifications);
    if (angular.equals(oldItems, newItems)) {
      return this.$q.resolve();
    } else {
      return this.projectsService.updateCertifications(this.project.url, newItems).then(() => {
        this.projectsService.clearAllCacheForCurrentEndpoint();

        if (this.workspace === 'organization') {
          this.$rootScope.$broadcast('refreshProjectList');
          return;
        }
      });
    }
  }

  removeProject() {
    var confirmDelete = confirm(gettext('Confirm deletion?'));
    if (!confirmDelete) {
      return this.$q.reject();
    }
    this.currentStateService.setProject(null);
    return this.project.$delete().then(() => {
      this.projectsService.clearAllCacheForCurrentEndpoint();
      return this.projectsService.getFirst().then(project => {
        this.currentStateService.setProject(project);
        this.$rootScope.$broadcast('refreshProjectList', {model: this.project, remove: true});
      });
    }, () => {
      this.currentStateService.setProject(this.project);
    }).then(() => {
      this.currentStateService.reloadCurrentCustomer(customer => {
        this.$rootScope.$broadcast('checkQuotas:refresh');
        this.$rootScope.$broadcast('customerBalance:refresh');
        this.$state.go('organization.projects', {uuid: customer.uuid});
      });
    });
  }
}

const projectManage = {
  template: template,
  controller: ProjectManageController,
  bindings: {
    project: '<'
  }
};

export default projectManage;
<|endoftext|>


<commit_before>(function () {
    'use strict';

    /* ngInject */
    function PeopleTabController($log, ACSAggregate, ACSVariables) {
        var ctl = this;

        initialize();

        function initialize() {
            ctl.acsVariables = ACSVariables;
            ctl.sumData = ACSAggregate.sum(ctl.data);
        }
    }

    /* ngInject */
    function imlsTabPeople() {
        var module = {
            restrict: 'E',
            templateUrl: 'scripts/views/museum/tab-people-partial.html',
            scope: {
                data: '='
            },
            controller: 'PeopleTabController',
            controllerAs: 'people',
            bindToController: true
        };
        return module;
    }

    angular.module('imls.views.museum')
    .controller('PeopleTabController', PeopleTabController)
    .directive('imlsTabPeople', imlsTabPeople);

})();
<commit_msg>Watch people tab source data for changes and update charts<commit_after>(function () {
    'use strict';

    /* ngInject */
    function PeopleTabController($log, $scope, ACSAggregate, ACSVariables) {
        var ctl = this;

        initialize();

        function initialize() {
            ctl.acsVariables = ACSVariables;
            onSumDataChanged(ctl.data);

            $scope.$watch(function () { return ctl.data; }, onSumDataChanged);
        }

        function onSumDataChanged(newData) {
            $log.info('PeopleTabController.onSumDataChanged', newData);
            if (newData && newData.headers && newData.data) {
                ctl.data = newData;
                ctl.sumData = ACSAggregate.sum(ctl.data);
            }
        }
    }

    /* ngInject */
    function imlsTabPeople() {
        var module = {
            restrict: 'E',
            templateUrl: 'scripts/views/museum/tab-people-partial.html',
            scope: {
                data: '='
            },
            controller: 'PeopleTabController',
            controllerAs: 'people',
            bindToController: true
        };
        return module;
    }

    angular.module('imls.views.museum')
    .controller('PeopleTabController', PeopleTabController)
    .directive('imlsTabPeople', imlsTabPeople);

})();
<|endoftext|>


<commit_before>(function(a){a.fn.dynamicoptions=function(e){var b=a.extend({},e);return this.each(function(){field=''+b.label+"";field+='
Add
';a(this).append(field);var f=function(d){var c="";if(typeof d==="object")c=d[0];field="";field+="
";field+='X';field+='<input type="text" class="tag-name" value="'+unescape(c)+'" />';field+='<input type="text" class="tag-value" value="'+
unescape(c)+'" />';field+="
";return field};a("#add-"+b.id).live("click",function(){a(this).parent().before(f());return false});a(".remove").live("click",function(){a(this).parent("div").animate({opacity:"hide",height:"hide",marginBottom:"0px"},"fast",function(){a(this).remove()});return false})})}})(jQuery);
<commit_msg>Update the minified version of the dynamic options handling plugin.<commit_after>(function(a){a.fn.dynamicoptions=function(g){var b=a.extend({},g);return this.each(function(){var e=function(c){field="";field+="
";field+='X';var d=0;for(var h in b.fields){value=c?c[d]:"";field+='<input type="text" name='+h+' class="tagfield" value="'+unescape(value)+'" />';d+=1}field+="
";return field};field=''+b.label+"";var f=JSON.parse(b.initial);for(var i in f)field+=e(f[i]);field+='
Add
';a(this).append(field);a("#add-"+b.id).live("click",function(){a(this).parent().before(e());return false});a(".remove").live("click",function(){a(this).parent("div").animate({opacity:"hide",height:"hide",marginBottom:"0px"},"fast",function(){a(this).remove()});return false})})}})(jQuery);
<|endoftext|>


<commit_before>import 'pixi.js';
import { TIMELINE_MAX_STEPS } from '../../constants';

const MAX_SPRITES_PER_STEP = 1500;

export default class VesselsOverlay extends google.maps.OverlayView {

  constructor(map) {
    super();

    this.map = map;
    this.setMap(map);
  }

  onAdd() {
    this._build();
    // Add the element to the "overlayLayer" pane.
    const panes = this.getPanes();
    panes.overlayLayer.appendChild(this.container);
  }

  _build() {
    this.container = document.createElement('div');
    this.container.style.position = 'absolute';

    const rect = this._getCanvasRect();
    this.renderer = new PIXI.WebGLRenderer(rect.width, rect.height, { transparent: true });

    this.canvas = this.renderer.view;
    this.canvas.style.position = 'absolute';
    this.canvas.style.border = '1px solid green';

    // this.stage = new PIXI.Container();
    this.stage = new PIXI.ParticleContainer(TIMELINE_MAX_STEPS * MAX_SPRITES_PER_STEP, { scale: true, position: true });
    this.stage.blendMode = PIXI.BLEND_MODES.SCREEN;

    this.container.appendChild(this.canvas);

    this.mainVesselTexture = PIXI.Texture.fromCanvas(this._getVesselTemplate(5, 0.15));

    this.spritesPool = [];
    this.timeIndexDelta = 0;

    this.debugTexts = [];
  }

  _getVesselTemplate(radius, blurFactor) {
    const tplCanvas = document.createElement('canvas');
    const tplCtx = tplCanvas.getContext('2d');
    const x = radius;
    const y = radius;
    tplCanvas.width = tplCanvas.height = radius * 2;

    if (blurFactor === 1) {
      tplCtx.beginPath();
      tplCtx.arc(x, y, radius, 0, 2 * Math.PI, false);
      tplCtx.fillStyle = 'rgba(255, 255, 237, 0.5)';
      tplCtx.fill();
    } else {
 //      'rgb(48, 149, 255)',
 // +      'rgb(136, 251, 255)',
 // +      'rgb(255, 248, 150)',
 // +      'rgb(255, 220, 45)'
      const gradient = tplCtx.createRadialGradient(x, y, radius * blurFactor, x, y, radius);
      gradient.addColorStop(0, 'rgba(255,255,255,1)');
      gradient.addColorStop(0.1, 'rgba(136, 251, 255,1)');
      gradient.addColorStop(0.2, 'rgba(255, 248, 150,1)');
      gradient.addColorStop(1, 'rgba(48, 149, 255, 0)');
      tplCtx.fillStyle = gradient;
      tplCtx.fillRect(0, 0, 2 * radius, 2 * radius);
    }
    return tplCanvas;
  }

  onRemove() {
    this.container.parentNode.removeChild(this.div_);
    this.container = null;
  }

  repositionCanvas() {
    if (!this.container) return;

    const rect = this._getCanvasRect();

    this.container.style.left = `${rect.x}px`;
    this.container.style.top = `${rect.y}px`;
    this.renderer.resize(rect.width, rect.height);
    this.canvas.style.width = `${rect.width}px`;
    this.canvas.style.height = `${rect.height}px`;
  }

  _getCanvasRect() {
    const overlayProjection = this.getProjection();

    const mapBounds = this.map.getBounds();
    const sw = overlayProjection.fromLatLngToDivPixel(mapBounds.getSouthWest());
    const ne = overlayProjection.fromLatLngToDivPixel(mapBounds.getNorthEast());

    return {
      x: sw.x,
      y: ne.y,
      width: ne.x - sw.x,
      height: sw.y - ne.y
    };
  }

  draw() {}

  render(tiles, startIndex, endIndex) {
    if (!this.stage) return;
    // this.debugTexts.forEach(text => {
    //   this.stage.removeChild(text);
    // });
    // this.debugTexts = [];

    const newTimeIndexDelta = endIndex - startIndex;

    if (this.timeIndexDelta !== newTimeIndexDelta) {
      this._resizeSpritesPool(newTimeIndexDelta, this.timeIndexDelta);
      this.timeIndexDelta = newTimeIndexDelta;
    }

    this.numSprites = 0;
    tiles.forEach(tile => {
      // console.log('tile')
      const bounds = tile.getBoundingClientRect();
      // const text = new PIXI.Text('This is a pixi text', { fontFamily: 'Arial', fontSize: 14, fill: 0xff1010 });
      // text.position.x = bounds.left;
      // text.position.y = bounds.top;
      // this.stage.addChild(text);
      // this.debugTexts.push(text);

      if (bounds.left === 0 && bounds.top === 0) {
        console.log('tile at 0,0');
      }
      this._dumpTileVessels(startIndex, endIndex, tile.data, bounds.left, bounds.top);
    });

    // console.log(this.numSprites)

    this.renderer.render(this.stage);
  }

  _dumpTileVessels(startIndex, endIndex, data, offsetX, offsetY) {
    if (!data) {
      return;
    }

    for (let timeIndex = startIndex; timeIndex < endIndex; timeIndex ++) {
      const frame = data[timeIndex];

      if (!frame) continue;

      for (let index = 0, len = frame.x.length; index < len; index++) {
        const sprite = this.spritesPool[this.numSprites];
        // const weight = playbackData.weight[i];
        const value = frame.value[index];
        // const value = Math.min(5, Math.max(1, Math.round(weight / 30)));
        // allValues += value;

        sprite.position.x = offsetX + frame.x[index];
        sprite.position.y = offsetY + frame.y[index];
        sprite.scale.set(value);

        this.numSprites++;
      }
    }

    // hide unused sprites
    // for (let i = this.numSprites, poolSize = this.spritesPool.length; i < poolSize; i++) {
    //   this.spritesPool[i].visible = false;
    // }
  }

  _addSprites(num) {
    console.log('add' + num);
    for (let i = 0; i < num; i++) {
      const vessel = new PIXI.Sprite(this.mainVesselTexture);
      vessel.anchor.x = vessel.anchor.y = 0.5;
      // ParticlesContainer does not support .visible, so we just move the sprite out of the viewport
      vessel.x = -100;
      // vessel.blendMode = PIXI.BLEND_MODES.SCREEN;
      // vessel.filters=  [new PIXI.filters.BlurFilter(10,10)]
      this.spritesPool.push(vessel);
      this.stage.addChild(vessel);
    }
  }

  _resizeSpritesPool(newTimeIndexDelta, prevTimeIndexDelta) {
    const totalPoolSize = this.spritesPool.length;
    const delta = newTimeIndexDelta - prevTimeIndexDelta;
    const deltaSprites = Math.abs(delta) * MAX_SPRITES_PER_STEP;

    if (Math.abs(delta) === 1) {
      // because of the way dates are rounded, the range length can vary of one day even if the user didnt change range
      // in that case skip resizing sprites pool, avoiding doing this in the middle of on animation
      // console.log(delta, 'skip resizing sprites pool')
      return;
    }
    // console.log(delta, 'resizing sprites pool')

    if (delta > 0) {
      const finalPoolSize = newTimeIndexDelta * MAX_SPRITES_PER_STEP;
      const toAdd = finalPoolSize - totalPoolSize;
      if (toAdd > 0) this._addSprites(toAdd);
    } else {
      const startRemovingAt = totalPoolSize - deltaSprites;
      for (let i = startRemovingAt; i < totalPoolSize; i++) {
        // this is actually insanely costly - keep this in RAM and be done with it
        // this.stage.removeChild(this.spritesPool[i]);
      }
      // this.spritesPool.splice(- deltaSprites);
    }

    // disable all sprites
    const newTotalPoolSize = this.spritesPool.length;

    for (let i = 0; i < newTotalPoolSize; i++) {
      // ParticlesContainer does not support .visible, so we just move the sprite out of the viewport
      this.spritesPool[i].x = -100;
    }

    return delta;
  }
}
<commit_msg>bigger sprites pool<commit_after>import 'pixi.js';
import { TIMELINE_MAX_STEPS } from '../../constants';

const MAX_SPRITES_PER_STEP = 2000;

export default class VesselsOverlay extends google.maps.OverlayView {

  constructor(map) {
    super();

    this.map = map;
    this.setMap(map);
  }

  onAdd() {
    this._build();
    // Add the element to the "overlayLayer" pane.
    const panes = this.getPanes();
    panes.overlayLayer.appendChild(this.container);
  }

  _build() {
    this.container = document.createElement('div');
    this.container.style.position = 'absolute';

    const rect = this._getCanvasRect();
    this.renderer = new PIXI.WebGLRenderer(rect.width, rect.height, { transparent: true });

    this.canvas = this.renderer.view;
    this.canvas.style.position = 'absolute';
    this.canvas.style.border = '1px solid green';

    // this.stage = new PIXI.Container();
    this.stage = new PIXI.ParticleContainer(TIMELINE_MAX_STEPS * MAX_SPRITES_PER_STEP, { scale: true, position: true });
    this.stage.blendMode = PIXI.BLEND_MODES.SCREEN;

    this.container.appendChild(this.canvas);

    this.mainVesselTexture = PIXI.Texture.fromCanvas(this._getVesselTemplate(5, 0.15));

    this.spritesPool = [];
    this.timeIndexDelta = 0;

    this.debugTexts = [];
  }

  _getVesselTemplate(radius, blurFactor) {
    const tplCanvas = document.createElement('canvas');
    const tplCtx = tplCanvas.getContext('2d');
    const x = radius;
    const y = radius;
    tplCanvas.width = tplCanvas.height = radius * 2;

    if (blurFactor === 1) {
      tplCtx.beginPath();
      tplCtx.arc(x, y, radius, 0, 2 * Math.PI, false);
      tplCtx.fillStyle = 'rgba(255, 255, 237, 0.5)';
      tplCtx.fill();
    } else {
 //      'rgb(48, 149, 255)',
 // +      'rgb(136, 251, 255)',
 // +      'rgb(255, 248, 150)',
 // +      'rgb(255, 220, 45)'
      const gradient = tplCtx.createRadialGradient(x, y, radius * blurFactor, x, y, radius);
      gradient.addColorStop(0, 'rgba(255,255,255,1)');
      gradient.addColorStop(0.1, 'rgba(136, 251, 255,1)');
      gradient.addColorStop(0.2, 'rgba(255, 248, 150,1)');
      gradient.addColorStop(1, 'rgba(48, 149, 255, 0)');
      tplCtx.fillStyle = gradient;
      tplCtx.fillRect(0, 0, 2 * radius, 2 * radius);
    }
    return tplCanvas;
  }

  onRemove() {
    this.container.parentNode.removeChild(this.div_);
    this.container = null;
  }

  repositionCanvas() {
    if (!this.container) return;

    const rect = this._getCanvasRect();

    this.container.style.left = `${rect.x}px`;
    this.container.style.top = `${rect.y}px`;
    this.renderer.resize(rect.width, rect.height);
    this.canvas.style.width = `${rect.width}px`;
    this.canvas.style.height = `${rect.height}px`;
  }

  _getCanvasRect() {
    const overlayProjection = this.getProjection();

    const mapBounds = this.map.getBounds();
    const sw = overlayProjection.fromLatLngToDivPixel(mapBounds.getSouthWest());
    const ne = overlayProjection.fromLatLngToDivPixel(mapBounds.getNorthEast());

    return {
      x: sw.x,
      y: ne.y,
      width: ne.x - sw.x,
      height: sw.y - ne.y
    };
  }

  draw() {}

  render(tiles, startIndex, endIndex) {
    if (!this.stage) return;
    // this.debugTexts.forEach(text => {
    //   this.stage.removeChild(text);
    // });
    // this.debugTexts = [];

    const newTimeIndexDelta = endIndex - startIndex;

    if (this.timeIndexDelta !== newTimeIndexDelta) {
      this._resizeSpritesPool(newTimeIndexDelta, this.timeIndexDelta);
      this.timeIndexDelta = newTimeIndexDelta;
    }

    this.numSprites = 0;
    tiles.forEach(tile => {
      // console.log('tile')
      const bounds = tile.getBoundingClientRect();
      // const text = new PIXI.Text('This is a pixi text', { fontFamily: 'Arial', fontSize: 14, fill: 0xff1010 });
      // text.position.x = bounds.left;
      // text.position.y = bounds.top;
      // this.stage.addChild(text);
      // this.debugTexts.push(text);

      if (bounds.left === 0 && bounds.top === 0) {
        console.log('tile at 0,0');
      }
      this._dumpTileVessels(startIndex, endIndex, tile.data, bounds.left, bounds.top);
    });

    // console.log(this.numSprites)

    this.renderer.render(this.stage);
  }

  _dumpTileVessels(startIndex, endIndex, data, offsetX, offsetY) {
    if (!data) {
      return;
    }

    for (let timeIndex = startIndex; timeIndex < endIndex; timeIndex ++) {
      const frame = data[timeIndex];

      if (!frame) continue;

      for (let index = 0, len = frame.x.length; index < len; index++) {
        const sprite = this.spritesPool[this.numSprites];
        // const weight = playbackData.weight[i];
        const value = frame.value[index];
        // const value = Math.min(5, Math.max(1, Math.round(weight / 30)));
        // allValues += value;

        sprite.position.x = offsetX + frame.x[index];
        sprite.position.y = offsetY + frame.y[index];
        sprite.scale.set(value);

        this.numSprites++;
      }
    }

    // hide unused sprites
    // for (let i = this.numSprites, poolSize = this.spritesPool.length; i < poolSize; i++) {
    //   this.spritesPool[i].visible = false;
    // }
  }

  _addSprites(num) {
    console.log('add' + num);
    for (let i = 0; i < num; i++) {
      const vessel = new PIXI.Sprite(this.mainVesselTexture);
      vessel.anchor.x = vessel.anchor.y = 0.5;
      // ParticlesContainer does not support .visible, so we just move the sprite out of the viewport
      vessel.x = -100;
      // vessel.blendMode = PIXI.BLEND_MODES.SCREEN;
      // vessel.filters=  [new PIXI.filters.BlurFilter(10,10)]
      this.spritesPool.push(vessel);
      this.stage.addChild(vessel);
    }
  }

  _resizeSpritesPool(newTimeIndexDelta, prevTimeIndexDelta) {
    const totalPoolSize = this.spritesPool.length;
    const delta = newTimeIndexDelta - prevTimeIndexDelta;
    const deltaSprites = Math.abs(delta) * MAX_SPRITES_PER_STEP;

    if (Math.abs(delta) === 1) {
      // because of the way dates are rounded, the range length can vary of one day even if the user didnt change range
      // in that case skip resizing sprites pool, avoiding doing this in the middle of on animation
      // console.log(delta, 'skip resizing sprites pool')
      return;
    }
    // console.log(delta, 'resizing sprites pool')

    if (delta > 0) {
      const finalPoolSize = newTimeIndexDelta * MAX_SPRITES_PER_STEP;
      const toAdd = finalPoolSize - totalPoolSize;
      if (toAdd > 0) this._addSprites(toAdd);
    } else {
      const startRemovingAt = totalPoolSize - deltaSprites;
      for (let i = startRemovingAt; i < totalPoolSize; i++) {
        // this is actually insanely costly - keep this in RAM and be done with it
        // this.stage.removeChild(this.spritesPool[i]);
      }
      // this.spritesPool.splice(- deltaSprites);
    }

    // disable all sprites
    const newTotalPoolSize = this.spritesPool.length;

    for (let i = 0; i < newTotalPoolSize; i++) {
      // ParticlesContainer does not support .visible, so we just move the sprite out of the viewport
      this.spritesPool[i].x = -100;
    }

    return delta;
  }
}
<|endoftext|>


<commit_before>import Ember from 'ember';
import { moduleForComponent, test } from 'ember-qunit';
import hbs from 'htmlbars-inline-precompile';
import startMirage, { createUsers } from '../../helpers/setup-mirage-for-integration';
import Table from 'ember-light-table';
import createClickEvent from '../../helpers/create-click-event';
import Columns from '../../helpers/table-columns';

const {
  run
} = Ember;

moduleForComponent('lt-body', 'Integration | Component | lt body', {
  integration: true,
  beforeEach() {
    this.set('sharedOptions', {
      fixedHeader: false,
      fixedFooter: false
    });

    startMirage(this.container);
  }
});

test('it renders', function(assert) {
  this.render(hbs `{{lt-body sharedOptions=sharedOptions}}`);
  assert.equal(this.$().text().trim(), '');
});

test('row selection - enable or disable', function(assert) {
  this.set('table', new Table(Columns, createUsers(1)));
  this.set('canSelect', false);

  this.render(hbs `{{lt-body table=table sharedOptions=sharedOptions canSelect=canSelect}}`);

  let row = this.$('tr:first');

  assert.ok(!row.hasClass('is-selectable'));
  assert.ok(!row.hasClass('is-selected'));
  row.click();
  assert.ok(!row.hasClass('is-selected'));

  this.set('canSelect', true);

  assert.ok(row.hasClass('is-selectable'));
  assert.ok(!row.hasClass('is-selected'));
  row.click();
  assert.ok(row.hasClass('is-selected'));
});

test('row selection - ctrl-click to modify selection', function(assert) {
  this.set('table', new Table(Columns, createUsers(5)));

  this.render(hbs `{{lt-body table=table sharedOptions=sharedOptions canSelect=true multiSelect=true}}`);

  let firstRow = this.$('tr:first');
  let middleRow = this.$('tr:nth-of-type(3)');
  let lastRow = this.$('tr:last');

  assert.equal(this.$('tbody > tr').length, 5);

  firstRow.click();
  assert.equal(this.$('tr.is-selected').length, 1, 'clicking a row selects it');

  lastRow.trigger(createClickEvent({
    shiftKey: true
  }));
  assert.equal(this.$('tr.is-selected').length, 5, 'shift-clicking another row selects it and all rows between');

  middleRow.trigger(createClickEvent({
    ctrlKey: true
  }));
  assert.equal(this.$('tr.is-selected').length, 4, 'ctrl-clicking a selected row deselects it');

  firstRow.click();
  assert.equal(this.$('tr.is-selected').length, 0, 'clicking a selected row deselects all rows');
});

test('row selection - click to modify selection', function(assert) {
  this.set('table', new Table(Columns, createUsers(5)));

  this.render(hbs `{{lt-body table=table sharedOptions=sharedOptions canSelect=true multiSelect=true multiSelectRequiresKeyboard=false}}`);

  let firstRow = this.$('tr:first');
  let middleRow = this.$('tr:nth-of-type(3)');
  let lastRow = this.$('tr:last');

  assert.equal(this.$('tbody > tr').length, 5);

  firstRow.click();
  assert.equal(this.$('tr.is-selected').length, 1, 'clicking a row selects it');

  lastRow.trigger(createClickEvent({
    shiftKey: true
  }));
  assert.equal(this.$('tr.is-selected').length, 5, 'shift-clicking another row selects it and all rows between');

  middleRow.click();
  assert.equal(this.$('tr.is-selected').length, 4, 'clicking a selected row deselects it without affecting other selected rows');

  middleRow.click();
  assert.equal(this.$('tr.is-selected').length, 5, 'clicking a deselected row selects it without affecting other selected rows');
});

test('row expansion', function(assert) {
  this.set('table', new Table(Columns, createUsers(2)));
  this.set('canExpand', false);

  this.render(hbs `
    {{#lt-body table=table sharedOptions=sharedOptions canSelect=false canExpand=canExpand multiRowExpansion=false as |b|}}
      {{#b.expanded-row}} Hello {{/b.expanded-row}}
    {{/lt-body}}
  `);

  let row = this.$('tr:first');

  assert.ok(!row.hasClass('is-expandable'));
  row.click();
  assert.equal(this.$('tr.lt-expanded-row').length, 0);
  assert.equal(this.$('tbody > tr').length, 2);
  assert.equal(this.$('tr.lt-expanded-row').text().trim(), '');

  this.set('canExpand', true);

  assert.ok(row.hasClass('is-expandable'));
  row.click();
  assert.equal(this.$('tr.lt-expanded-row').length, 1);
  assert.equal(this.$('tbody > tr').length, 3);
  assert.equal(row.next().text().trim(), 'Hello');

  row = this.$('tr:last');
  assert.ok(row.hasClass('is-expandable'));
  row.click();
  assert.equal(this.$('tr.lt-expanded-row').length, 1);
  assert.equal(this.$('tbody > tr').length, 3);
  assert.equal(row.next().text().trim(), 'Hello');
});

test('row expansion - multiple', function(assert) {
  this.set('table', new Table(Columns, createUsers(2)));
  this.render(hbs `
    {{#lt-body table=table sharedOptions=sharedOptions canExpand=true as |b|}}
      {{#b.expanded-row}} Hello {{/b.expanded-row}}
    {{/lt-body}}
  `);

  let rows = this.$('tr');
  assert.equal(rows.length, 2);

  rows.each((i, r) => {
    let row = $(r);
    assert.ok(row.hasClass('is-expandable'));
    row.click();
    assert.equal(row.next().text().trim(), 'Hello');
  });

  assert.equal(this.$('tr.lt-expanded-row').length, 2);
});

test('row actions', function(assert) {
  assert.expect(2);

  this.set('table', new Table(Columns, createUsers(1)));
  this.on('onRowClick', (row) => assert.ok(row));
  this.on('onRowDoubleClick', (row) => assert.ok(row));
  this.render(hbs `{{lt-body table=table sharedOptions=sharedOptions onRowClick=(action 'onRowClick') onRowDoubleClick=(action 'onRowDoubleClick')}}`);

  let row = this.$('tr:first');
  row.click();
  row.dblclick();
});

test('hidden rows', function(assert) {
  this.set('table', new Table(Columns, createUsers(5)));

  this.render(hbs `{{lt-body table=table sharedOptions=sharedOptions}}`);

  assert.equal(this.$('tbody > tr').length, 5);

  run(() => {
    this.get('table.rows').objectAt(0).set('hidden', true);
    this.get('table.rows').objectAt(1).set('hidden', true);
  });

  assert.equal(this.$('tbody > tr').length, 3);

  run(() => {
    this.get('table.rows').objectAt(0).set('hidden', false);
  });

  assert.equal(this.$('tbody > tr').length, 4);
});

test('overwrite', function(assert) {
  this.set('table', new Table(Columns, createUsers(5)));

  this.render(hbs `
    {{#lt-body table=table sharedOptions=sharedOptions overwrite=true as |columns rows|}}
      {{columns.length}}, {{rows.length}}
    {{/lt-body}}
  `);

  assert.equal(this.$().text().trim(), '6, 5');
});
<commit_msg>tests/lt-body: add tests for scaffolding<commit_after>import Ember from 'ember';
import { moduleForComponent, test } from 'ember-qunit';
import hbs from 'htmlbars-inline-precompile';
import startMirage, { createUsers } from '../../helpers/setup-mirage-for-integration';
import Table from 'ember-light-table';
import createClickEvent from '../../helpers/create-click-event';
import Columns from '../../helpers/table-columns';

const {
  run
} = Ember;

moduleForComponent('lt-body', 'Integration | Component | lt body', {
  integration: true,
  beforeEach() {
    this.set('sharedOptions', {
      fixedHeader: false,
      fixedFooter: false
    });

    startMirage(this.container);
  }
});

test('it renders', function(assert) {
  this.render(hbs `{{lt-body sharedOptions=sharedOptions}}`);
  assert.equal(this.$().text().trim(), '');
});

test('row selection - enable or disable', function(assert) {
  this.set('table', new Table(Columns, createUsers(1)));
  this.set('canSelect', false);

  this.render(hbs `{{lt-body table=table sharedOptions=sharedOptions canSelect=canSelect}}`);

  let row = this.$('tr:first');

  assert.ok(!row.hasClass('is-selectable'));
  assert.ok(!row.hasClass('is-selected'));
  row.click();
  assert.ok(!row.hasClass('is-selected'));

  this.set('canSelect', true);

  assert.ok(row.hasClass('is-selectable'));
  assert.ok(!row.hasClass('is-selected'));
  row.click();
  assert.ok(row.hasClass('is-selected'));
});

test('row selection - ctrl-click to modify selection', function(assert) {
  this.set('table', new Table(Columns, createUsers(5)));

  this.render(hbs `{{lt-body table=table sharedOptions=sharedOptions canSelect=true multiSelect=true}}`);

  let firstRow = this.$('tr:first');
  let middleRow = this.$('tr:nth-of-type(3)');
  let lastRow = this.$('tr:last');

  assert.equal(this.$('tbody > tr').length, 5);

  firstRow.click();
  assert.equal(this.$('tr.is-selected').length, 1, 'clicking a row selects it');

  lastRow.trigger(createClickEvent({
    shiftKey: true
  }));
  assert.equal(this.$('tr.is-selected').length, 5, 'shift-clicking another row selects it and all rows between');

  middleRow.trigger(createClickEvent({
    ctrlKey: true
  }));
  assert.equal(this.$('tr.is-selected').length, 4, 'ctrl-clicking a selected row deselects it');

  firstRow.click();
  assert.equal(this.$('tr.is-selected').length, 0, 'clicking a selected row deselects all rows');
});

test('row selection - click to modify selection', function(assert) {
  this.set('table', new Table(Columns, createUsers(5)));

  this.render(hbs `{{lt-body table=table sharedOptions=sharedOptions canSelect=true multiSelect=true multiSelectRequiresKeyboard=false}}`);

  let firstRow = this.$('tr:first');
  let middleRow = this.$('tr:nth-of-type(3)');
  let lastRow = this.$('tr:last');

  assert.equal(this.$('tbody > tr').length, 5);

  firstRow.click();
  assert.equal(this.$('tr.is-selected').length, 1, 'clicking a row selects it');

  lastRow.trigger(createClickEvent({
    shiftKey: true
  }));
  assert.equal(this.$('tr.is-selected').length, 5, 'shift-clicking another row selects it and all rows between');

  middleRow.click();
  assert.equal(this.$('tr.is-selected').length, 4, 'clicking a selected row deselects it without affecting other selected rows');

  middleRow.click();
  assert.equal(this.$('tr.is-selected').length, 5, 'clicking a deselected row selects it without affecting other selected rows');
});

test('row expansion', function(assert) {
  this.set('table', new Table(Columns, createUsers(2)));
  this.set('canExpand', false);

  this.render(hbs `
    {{#lt-body table=table sharedOptions=sharedOptions canSelect=false canExpand=canExpand multiRowExpansion=false as |b|}}
      {{#b.expanded-row}} Hello {{/b.expanded-row}}
    {{/lt-body}}
  `);

  let row = this.$('tr:first');

  assert.ok(!row.hasClass('is-expandable'));
  row.click();
  assert.equal(this.$('tr.lt-expanded-row').length, 0);
  assert.equal(this.$('tbody > tr').length, 2);
  assert.equal(this.$('tr.lt-expanded-row').text().trim(), '');

  this.set('canExpand', true);

  assert.ok(row.hasClass('is-expandable'));
  row.click();
  assert.equal(this.$('tr.lt-expanded-row').length, 1);
  assert.equal(this.$('tbody > tr').length, 3);
  assert.equal(row.next().text().trim(), 'Hello');

  row = this.$('tr:last');
  assert.ok(row.hasClass('is-expandable'));
  row.click();
  assert.equal(this.$('tr.lt-expanded-row').length, 1);
  assert.equal(this.$('tbody > tr').length, 3);
  assert.equal(row.next().text().trim(), 'Hello');
});

test('row expansion - multiple', function(assert) {
  this.set('table', new Table(Columns, createUsers(2)));
  this.render(hbs `
    {{#lt-body table=table sharedOptions=sharedOptions canExpand=true as |b|}}
      {{#b.expanded-row}} Hello {{/b.expanded-row}}
    {{/lt-body}}
  `);

  let rows = this.$('tr');
  assert.equal(rows.length, 2);

  rows.each((i, r) => {
    let row = $(r);
    assert.ok(row.hasClass('is-expandable'));
    row.click();
    assert.equal(row.next().text().trim(), 'Hello');
  });

  assert.equal(this.$('tr.lt-expanded-row').length, 2);
});

test('row actions', function(assert) {
  assert.expect(2);

  this.set('table', new Table(Columns, createUsers(1)));
  this.on('onRowClick', (row) => assert.ok(row));
  this.on('onRowDoubleClick', (row) => assert.ok(row));
  this.render(hbs `{{lt-body table=table sharedOptions=sharedOptions onRowClick=(action 'onRowClick') onRowDoubleClick=(action 'onRowDoubleClick')}}`);

  let row = this.$('tr:first');
  row.click();
  row.dblclick();
});

test('hidden rows', function(assert) {
  this.set('table', new Table(Columns, createUsers(5)));

  this.render(hbs `{{lt-body table=table sharedOptions=sharedOptions}}`);

  assert.equal(this.$('tbody > tr').length, 5);

  run(() => {
    this.get('table.rows').objectAt(0).set('hidden', true);
    this.get('table.rows').objectAt(1).set('hidden', true);
  });

  assert.equal(this.$('tbody > tr').length, 3);

  run(() => {
    this.get('table.rows').objectAt(0).set('hidden', false);
  });

  assert.equal(this.$('tbody > tr').length, 4);
});

test('scaffolding', function(assert) {
  const users = createUsers(1);
  this.set('table', new Table(Columns, users));

  this.render(hbs `{{lt-body table=table sharedOptions=sharedOptions enableScaffolding=true}}`);

  const scaffoldingRow = this.$('tr:eq(0)');
  const userRow = this.$('tr:eq(1)');
  const userCells = userRow.children('.lt-cell');

  assert.ok(
    scaffoldingRow.hasClass('lt-scaffolding-row'),
    'the first row of the <tbody> is a scaffolding row'
  );
  assert.ok(
    !userRow.hasClass('lt-scaffolding-row'),
    'the second row of the <tbody> is not a scaffolding row'
  );

  assert.equal(
    userRow.attr('style'),
    null,
    'the second row of the <tbody> has no `style` attribute'
  );

  assert.ok(
    Columns
      .map((c, i) => {
        const configuredWidth = Number.parseInt(c.width, 10);
        const actualWidth = userCells.eq(i).width();

        return configuredWidth ? configuredWidth === actualWidth : true;
      })
      .every(Boolean),
    'the first actual data row has the correct widths'
  );
});

test('overwrite', function(assert) {
  this.set('table', new Table(Columns, createUsers(5)));

  this.render(hbs `
    {{#lt-body table=table sharedOptions=sharedOptions overwrite=true as |columns rows|}}
      {{columns.length}}, {{rows.length}}
    {{/lt-body}}
  `);

  assert.equal(this.$().text().trim(), '6, 5');
});
<|endoftext|>


<commit_before>(function() {
  'use strict';

  if (typeof(mocha) === 'object') {
    // Configure Mocha
    mocha.setup('bdd');
    mocha.fullTrace();
    mocha.checkLeaks();
    mocha.globals(['$0', '$1', '$2', '$3', '$4', '$5']);
  }

  // Set global settings for all tests
  beforeEach(function() {
    this.currentTest.timeout(2000);
    this.currentTest.slow(100);
  });

})();
<commit_msg>Added ga globals to Mocha config<commit_after>(function() {
  'use strict';

  if (typeof(mocha) === 'object') {
    // Configure Mocha
    mocha.setup('bdd');
    mocha.fullTrace();
    mocha.checkLeaks();
    mocha.globals(['$0', '$1', '$2', '$3', '$4', '$5', 'ga', 'gaplugins', 'gaGlobal']);
  }

  // Set global settings for all tests
  beforeEach(function() {
    this.currentTest.timeout(2000);
    this.currentTest.slow(100);
  });

})();
<|endoftext|>