Latest Threat Research:SANDWORM_MODE: Shai-Hulud-Style npm Worm Hijacks CI Workflows and Poisons AI Toolchains.Details
Socket
Book a DemoInstallSign in
Socket

johnny-five

Package Overview
Dependencies
Maintainers
1
Versions
300
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

johnny-five - npm Package Compare versions

Comparing version
1.3.1
to
1.4.0
+109
lib/easing.js
const SI = 1.70158;
const SIO = 1.70158 * 1.525;
const SB = 7.5625;
const HALF = 0.5;
const {
PI,
cos,
sin,
sqrt,
} = Math;
const ease = {};
ease.linear = n => n;
ease.inQuad = n => n ** 2;
ease.outQuad = n => n * (2 - n);
ease.inOutQuad = n => {
n *= 2;
return n < 1 ?
HALF * n * n :
-HALF * (--n * (n - 2) - 1);
};
ease.inCube = n => n ** 3;
ease.outCube = n => --n * n * n + 1;
ease.inOutCube = n => {
n *= 2;
return n < 1 ?
HALF * n ** 3 :
HALF * ((n -= 2) * n * n + 2);
};
ease.inQuart = n => n ** 4;
ease.outQuart = n => 1 - (--n * n ** 3);
ease.inOutQuart = n => {
n *= 2;
return n < 1 ?
HALF * n ** 4 :
-HALF * ((n -= 2) * n ** 3 - 2);
};
ease.inQuint = n => n ** 5;
ease.outQuint = n => --n * n ** 4 + 1;
ease.inOutQuint = n => {
n *= 2;
return n < 1 ?
HALF * n ** 5 :
HALF * ((n -= 2) * n ** 4 + 2);
};
ease.inSine = n => 1 - cos(n * PI / 2);
ease.outSine = n => sin(n * PI / 2);
ease.inOutSine = n => HALF * (1 - cos(PI * n));
ease.inExpo = n => 0 === n ? 0 : 1024 ** (n - 1);
ease.outExpo = n => 1 === n ? n : 1 - 2 ** (-10 * n);
ease.inOutExpo = n => {
if (n === 0) { return 0; }
if (n === 1) { return 1; }
return (n *= 2) < 1 ?
HALF * (1024 ** (n - 1)) :
HALF * (-(2 ** (-10 * (n - 1))) + 2);
};
ease.inCirc = n => 1 - sqrt(1 - n * n);
ease.outCirc = n => sqrt(1 - (--n * n));
ease.inOutCirc = n => {
n *= 2;
return (n < 1) ?
-HALF * (sqrt(1 - n * n) - 1) :
HALF * (sqrt(1 - (n -= 2) * n) + 1);
};
ease.inBack = n => n * n * ((SI + 1) * n - SI);
ease.outBack = n => --n * n * ((SI + 1) * n + SI) + 1;
ease.inOutBack = n => {
return (n *= 2) < 1 ?
HALF * (n * n * ((SIO + 1) * n - SIO)) :
HALF * ((n -= 2) * n * ((SIO + 1) * n + SIO) + 2);
};
ease.outBounce = n => {
if (n < (1 / 2.75)) {
return SB * n * n;
} else if (n < (2 / 2.75)) {
return SB * (n -= (1.5 / 2.75)) * n + 0.75;
} else if (n < (2.5 / 2.75)) {
return SB * (n -= (2.25 / 2.75)) * n + 0.9375;
} else {
return SB * (n -= (2.625 / 2.75)) * n + 0.984375;
}
};
ease.inBounce = n => 1 - ease.outBounce(1 - n);
ease.inOutBounce = n => {
return n < HALF ?
ease.inBounce(n * 2) * HALF :
ease.outBounce(n * 2 - 1) * HALF + HALF;
};
const exportables = {};
Object.keys(ease).forEach(key => {
exportables[key.toLowerCase()] = ease[key];
});
module.exports = new Proxy({}, {
get(target, property) {
return exportables[property.replace(/([a-z])([A-Z])/g, "$1$2").toLowerCase()];
}
});
+383
-404

@@ -0,14 +1,11 @@

const Emitter = require("events");
const ease = require("./easing");
const { cloneDeep, constrain } = require("./fn");
let temporal;
// TODO list
// Use functions as keyFrames
// Test metronomic on real animation
// Create jquery FX like queue
var Emitter = require("events").EventEmitter;
var util = require("util");
var ease = require("ease-component");
var Fn = require("./fn");
var temporal;
/**

@@ -20,30 +17,5 @@ * The max time we want to allow a temporal animation segment to run.

**/
var temporalTTL = 5000;
let temporalTTL = 5000;
/**
* Placeholders for Symbol
*/
Animation.keys = "@@keys";
Animation.normalize = "@@normalize";
Animation.render = "@@render";
/**
* Temporal will run up the CPU. temporalFallback is used
* for long running animations.
*/
Animation.TemporalFallback = function(animation) {
this.interval = setInterval(function() {
animation.loopFunction({
calledAt: Date.now()
});
}, animation.rate);
};
Animation.TemporalFallback.prototype.stop = function() {
if (this.interval) {
clearInterval(this.interval);
}
};
/**
* Animation

@@ -82,490 +54,497 @@ * @constructor

function Animation(target) {
class Animation extends Emitter {
constructor(target) {
super();
// Necessary to avoid loading temporal unless necessary
if (!temporal) {
temporal = require("temporal");
}
// Necessary to avoid loading temporal unless necessary
if (!temporal) {
temporal = require("temporal");
}
if (!(this instanceof Animation)) {
return new Animation(target);
Object.assign(this, new Animation.Segment());
this.defaultTarget = target || {};
}
/**
* Add an animation segment to the animation queue
* @param {Object} options Options: cuePoints, keyFrames, duration,
* easing, loop, metronomic, progress, fps, onstart, onpause,
* onstop, oncomplete, onloop
*/
enqueue(options = {}) {
/* istanbul ignore else */
if (typeof options.target === "undefined") {
options.target = this.defaultTarget;
}
Animation.Segment.call(this);
this.segments.push(options);
this.defaultTarget = target || {};
}
/* istanbul ignore if */
if (!this.paused && !this.isRunning) {
this.next();
}
util.inherits(Animation, Emitter);
return this;
}
/**
* Animation.Segment()
*
* Create a defaulted segment.
*
* Every property ever used on an animation segment
* MUST be listed here, otherwise properties will
* persist across segments. This default object is
* primarily for resetting state.
*
*/
Animation.Segment = function(options) {
this.cuePoints = [0, 1];
this.duration = 1000;
this.easing = "linear";
this.loop = false;
this.loopback = 0;
this.metronomic = false;
this.currentSpeed = 1;
this.progress = 0;
this.fps = 60;
this.rate = 1000 / 60;
this.paused = false;
this.isRunning = false;
this.segments = [];
this.onstart = null;
this.onpause = null;
this.onstop = null;
this.oncomplete = null;
this.onloop = null;
/**
* Plays next segment in queue
* Users need not call this. It's automatic
*/
next() {
if (options) {
Object.assign(this, options);
if (options.segments) {
this.segments = options.segments.slice();
if (this.isRunning) {
return this;
} else {
this.isRunning = true;
}
}
};
if (this.segments.length > 0) {
Object.assign(this, new Animation.Segment(this.segments.shift()));
this.paused = this.currentSpeed === 0 ? true : false;
/**
* Add an animation segment to the animation queue
* @param {Object} opts Options: cuePoints, keyFrames, duration,
* easing, loop, metronomic, progress, fps, onstart, onpause,
* onstop, oncomplete, onloop
*/
Animation.prototype.enqueue = function(opts) {
if (this.onstart) {
this.onstart();
}
opts = opts || {};
this.normalizeKeyframes();
/* istanbul ignore else */
if (typeof opts.target === "undefined") {
opts.target = this.defaultTarget;
}
if (this.reverse) {
this.currentSpeed *= -1;
}
this.segments.push(opts);
if (this.currentSpeed !== 0) {
this.play();
} else {
this.paused = true;
}
} else {
this.playLoop.stop();
}
/* istanbul ignore if */
if (!this.paused && !this.isRunning) {
this.next();
return this;
}
return this;
/**
* pause
*
* Pause animation while maintaining progress, speed and segment queue
*
*/
pause() {
};
this.emit("animation:pause");
/**
* Plays next segment in queue
* Users need not call this. It's automatic
*/
Animation.prototype.next = function() {
if (this.playLoop) {
this.playLoop.stop();
}
this.paused = true;
if (this.isRunning) {
return this;
} else {
this.isRunning = true;
if (this.onpause) {
this.onpause();
}
}
if (this.segments.length > 0) {
var segment = new Animation.Segment(this.segments.shift());
Object.assign(this, segment);
this.paused = this.currentSpeed === 0 ? true : false;
/**
* stop
*
* Stop all animations
*
*/
stop() {
if (this.onstart) {
this.onstart();
this.emit("animation:stop");
this.segments = [];
this.isRunning = false;
if (this.playLoop) {
this.playLoop.stop();
}
this.normalizeKeyframes();
if (this.reverse) {
this.currentSpeed *= -1;
if (this.onstop) {
this.onstop();
}
if (this.currentSpeed !== 0) {
this.play();
} else {
this.paused = true;
}
} else {
this.playLoop.stop();
}
return this;
};
/**
* speed
*
* Get or set the current playback speed
*
* @param {Number} speed
*
*/
speed(speed) {
/**
* pause
*
* Pause animation while maintaining progress, speed and segment queue
*
*/
if (typeof speed === "undefined") {
return this.currentSpeed;
} else {
this.currentSpeed = speed;
Animation.prototype.pause = function() {
// Find our timeline endpoints and refresh rate
this.scaledDuration = this.duration / Math.abs(this.currentSpeed);
this.startTime = Date.now() - this.scaledDuration * this.progress;
this.endTime = this.startTime + this.scaledDuration;
this.emit("animation:pause");
if (this.playLoop) {
this.playLoop.stop();
if (!this.paused) {
this.play();
}
return this;
}
}
this.paused = true;
if (this.onpause) {
this.onpause();
}
/**
* This function is called in each frame of our animation
* Users need not call this. It's automatic
*/
loopFunction({calledAt}) {
};
// Find the current timeline progress
const progress = this.calculateProgress(calledAt);
/**
* stop
*
* Stop all animations
*
*/
// Find the left and right cuePoints/keyFrames;
const indices = this.findIndices(progress);
Animation.prototype.stop = function() {
// call render function with tweened value
this.target[Animation.render](this.tweenedValue(indices, progress));
this.emit("animation:stop");
/**
* If this animation has been running in temporal for too long
* fall back to using setInterval so we don't melt the user's CPU
**/
if (calledAt > this.fallBackTime) {
this.fallBackTime = Infinity;
if (this.playLoop) {
this.playLoop.stop();
}
this.playLoop = new Animation.TemporalFallback(this);
}
this.segments = [];
this.isRunning = false;
if (this.playLoop) {
this.playLoop.stop();
}
// See if we have reached the end of the animation
/* istanbul ignore else */
if ((this.progress === 1 && !this.reverse) || (progress === this.loopback && this.reverse)) {
if (this.onstop) {
this.onstop();
}
if (this.loop || (this.metronomic && !this.reverse)) {
};
if (this.onloop) {
this.onloop();
}
/**
* speed
*
* Get or set the current playback speed
*
* @param {Number} speed
*
*/
if (this.metronomic) {
this.reverse = this.reverse ? false : true;
}
Animation.prototype.speed = function(speed) {
this.normalizeKeyframes();
this.progress = this.loopback;
this.startTime = Date.now() - this.scaledDuration * this.progress;
this.endTime = this.startTime + this.scaledDuration;
} else {
if (typeof speed === "undefined") {
return this.currentSpeed;
} else {
this.currentSpeed = speed;
this.isRunning = false;
// Find our timeline endpoints and refresh rate
this.scaledDuration = this.duration / Math.abs(this.currentSpeed);
this.startTime = Date.now() - this.scaledDuration * this.progress;
this.endTime = this.startTime + this.scaledDuration;
if (this.oncomplete) {
process.nextTick(this.oncomplete.bind(this));
}
if (!this.paused) {
this.play();
if (this.segments.length > 0) {
process.nextTick(() => { this.next(); });
} else {
this.stop();
}
}
}
return this;
}
};
/**
* This function is called in each frame of our animation
* Users need not call this. It's automatic
*/
/**
* play
*
* Start a segment
*/
play() {
const now = Date.now();
Animation.prototype.loopFunction = function(loop) {
// Find the current timeline progress
var progress = this.calculateProgress(loop.calledAt);
// Find the left and right cuePoints/keyFrames;
var indices = this.findIndices(progress);
// call render function with tweened value
this.target[Animation.render](this.tweenedValue(indices, progress));
/**
* If this animation has been running in temporal for too long
* fall back to using setInterval so we don't melt the user's CPU
**/
if (loop.calledAt > this.fallBackTime) {
this.fallBackTime = Infinity;
if (this.playLoop) {
this.playLoop.stop();
}
this.playLoop = new Animation.TemporalFallback(this);
}
// See if we have reached the end of the animation
/* istanbul ignore else */
if ((this.progress === 1 && !this.reverse) || (progress === this.loopback && this.reverse)) {
this.paused = false;
this.isRunning = true;
if (this.loop || (this.metronomic && !this.reverse)) {
// Find our timeline endpoints and refresh rate
this.scaledDuration = this.duration / Math.abs(this.currentSpeed);
this.startTime = now - this.scaledDuration * this.progress;
this.endTime = this.startTime + this.scaledDuration;
if (this.onloop) {
this.onloop();
}
// If our animation runs for more than 5 seconds switch to setTimeout
this.fallBackTime = now + temporalTTL;
this.frameCount = 0;
if (this.metronomic) {
this.reverse = this.reverse ? false : true;
}
this.normalizeKeyframes();
this.progress = this.loopback;
this.startTime = Date.now() - this.scaledDuration * this.progress;
this.endTime = this.startTime + this.scaledDuration;
} else {
this.isRunning = false;
if (this.oncomplete) {
process.nextTick(this.oncomplete.bind(this));
}
if (this.segments.length > 0) {
process.nextTick(() => { this.next(); });
} else {
this.stop();
}
/* istanbul ignore else */
if (this.fps) {
this.rate = 1000 / this.fps;
}
}
};
/**
* play
*
* Start a segment
*/
this.rate = this.rate | 0;
Animation.prototype.play = function() {
var now = Date.now();
if (this.playLoop) {
this.playLoop.stop();
this.playLoop = temporal.loop(this.rate, this.loopFunction.bind(this));
}
this.paused = false;
this.isRunning = true;
findIndices(progress) {
const indices = {
left: null,
right: null
};
// Find our timeline endpoints and refresh rate
this.scaledDuration = this.duration / Math.abs(this.currentSpeed);
this.startTime = now - this.scaledDuration * this.progress;
this.endTime = this.startTime + this.scaledDuration;
// Find our current before and after cuePoints
indices.right = this.cuePoints.findIndex(point => point >= progress);
// If our animation runs for more than 5 seconds switch to setTimeout
this.fallBackTime = now + temporalTTL;
this.frameCount = 0;
indices.left = indices.right === 0 ? /* istanbul ignore next */ 0 : indices.right - 1;
/* istanbul ignore else */
if (this.fps) {
this.rate = 1000 / this.fps;
return indices;
}
this.rate = this.rate | 0;
calculateProgress(calledAt) {
this.playLoop = temporal.loop(this.rate, this.loopFunction.bind(this));
};
let progress = (calledAt - this.startTime) / this.scaledDuration;
Animation.prototype.findIndices = function(progress) {
var indices = {
left: null,
right: null
};
if (progress > 1) {
progress = 1;
}
// Find our current before and after cuePoints
indices.right = this.cuePoints.findIndex(function(point) {
return point >= progress;
});
this.progress = progress;
indices.left = indices.right === 0 ? /* istanbul ignore next */ 0 : indices.right - 1;
if (this.reverse) {
progress = 1 - progress;
}
return indices;
};
// Ease the timeline
// to do: When reverse replace inFoo with outFoo and vice versa. skip inOutFoo
return constrain(ease[this.easing](progress), 0, 1);
}
Animation.prototype.calculateProgress = function(calledAt) {
tweenedValue(indices, progress) {
var progress = (calledAt - this.startTime) / this.scaledDuration;
const tween = {
duration: null,
progress: null
};
if (progress > 1) {
progress = 1;
}
const result = this.normalizedKeyFrames.map(keyFrame => {
const kIndices = {
left: null,
right: null
};
this.progress = progress;
// If the keyframe at indices.left is null, move left
for (kIndices.left = indices.left; kIndices.left > -1; kIndices.left--) {
/* istanbul ignore else */
if (keyFrame[kIndices.left] !== null) {
break;
}
}
if (this.reverse) {
progress = 1 - progress;
}
// If the keyframe at indices.right is null, move right
kIndices.right = keyFrame.findIndex((frame, index) =>
index >= indices.right && frame !== null
);
// Ease the timeline
// to do: When reverse replace inFoo with outFoo and vice versa. skip inOutFoo
progress = ease[this.easing](progress);
progress = Fn.constrain(progress, 0, 1);
// Find our progress for the current tween
tween.duration = this.cuePoints[kIndices.right] - this.cuePoints[kIndices.left];
tween.progress = (progress - this.cuePoints[kIndices.left]) / tween.duration;
return progress;
};
// Catch divide by zero
if (!Number.isFinite(tween.progress)) {
/* istanbul ignore next */
tween.progress = this.reverse ? 0 : 1;
}
Animation.prototype.tweenedValue = function(indices, progress) {
const left = keyFrame[kIndices.left];
const right = keyFrame[kIndices.right];
var tween = {
duration: null,
progress: null
};
// Apply tween easing to tween.progress
// to do: When reverse replace inFoo with outFoo and vice versa. skip inOutFoo
tween.progress = ease[right.easing](tween.progress);
var result = this.normalizedKeyFrames.map(function(keyFrame) {
// Note: "this" is bound to the animation object
// Calculate this tween value
let calcValue;
var memberIndices = {
left: null,
right: null
};
// If the keyframe at indices.left is null, move left
for (memberIndices.left = indices.left; memberIndices.left > -1; memberIndices.left--) {
/* istanbul ignore else */
if (keyFrame[memberIndices.left] !== null) {
break;
if (right.position) {
// This is a tuple
calcValue = right.position.map((value, index) => (value - left.position[index]) *
tween.progress + left.position[index]);
} else {
if (typeof right.value === "number" && typeof left.value === "number") {
calcValue = (right.value - left.value) * tween.progress + left.value;
} else {
calcValue = this.target[Animation.keys].reduce((accum, key) => {
accum[key] = (right.value[key] - left.value[key]) * tween.progress + left.value[key];
return accum;
}, {});
}
}
}
// If the keyframe at indices.right is null, move right
memberIndices.right = keyFrame.findIndex(function(frame, index) {
return index >= indices.right && frame !== null;
return calcValue;
});
// Find our progress for the current tween
tween.duration = this.cuePoints[memberIndices.right] - this.cuePoints[memberIndices.left];
tween.progress = (progress - this.cuePoints[memberIndices.left]) / tween.duration;
return result;
}
// Catch divide by zero
if (!Number.isFinite(tween.progress)) {
/* istanbul ignore next */
tween.progress = this.reverse ? 0 : 1;
}
// Make sure our keyframes conform to a standard
normalizeKeyframes() {
let previousVal;
let keyFrameSet = cloneDeep(this.keyFrames);
const cuePoints = this.cuePoints;
var left = keyFrame[memberIndices.left],
right = keyFrame[memberIndices.right];
// Run through the target's normalization
keyFrameSet = this.target[Animation.normalize](keyFrameSet);
// Apply tween easing to tween.progress
// to do: When reverse replace inFoo with outFoo and vice versa. skip inOutFoo
tween.progress = ease[right.easing](tween.progress);
// keyFrames can be passed as a single dimensional array if
// there is just one servo/device. If the first element is not an
// array, nest keyFrameSet so we only have to deal with one format
if (!Array.isArray(keyFrameSet[0])) {
keyFrameSet = [keyFrameSet];
}
// Calculate this tween value
var calcValue;
keyFrameSet.forEach(function(keyFrames) {
if (right.position) {
// This is a tuple
calcValue = right.position.map(function(value, index) {
return (value - left.position[index]) *
tween.progress + left.position[index];
});
} else {
if (typeof right.value === "number" && typeof left.value === "number") {
calcValue = (right.value - left.value) * tween.progress + left.value;
} else {
calcValue = this.target[Animation.keys].reduce(function(accum, key) {
accum[key] = (right.value[key] - left.value[key]) * tween.progress + left.value[key];
return accum;
}, {});
// Pad the right side of keyFrames arrays with null
for (let i = keyFrames.length; i < cuePoints.length; i++) {
keyFrames.push(null);
}
}
return calcValue;
}, this);
keyFrames.forEach((keyFrame, i, source) => {
return result;
};
if (keyFrame !== null) {
// Make sure our keyframes conform to a standard
Animation.prototype.normalizeKeyframes = function() {
// keyFrames need to be converted to objects
if (typeof keyFrame !== "object") {
keyFrame = {
step: keyFrame,
easing: "linear"
};
}
var previousVal,
keyFrameSet = Fn.cloneDeep(this.keyFrames),
cuePoints = this.cuePoints;
// Replace step values
if (typeof keyFrame.step !== "undefined") {
keyFrame.value = keyFrame.step === false ?
previousVal : previousVal + keyFrame.step;
}
// Run through the target's normalization
keyFrameSet = this.target[Animation.normalize](keyFrameSet);
// Set a default easing function
if (!keyFrame.easing) {
keyFrame.easing = "linear";
}
// keyFrames can be passed as a single dimensional array if
// there is just one servo/device. If the first element is not an
// array, nest keyFrameSet so we only have to deal with one format
if (!Array.isArray(keyFrameSet[0])) {
keyFrameSet = [keyFrameSet];
}
// Copy value from another frame
/* istanbul ignore if */
if (typeof keyFrame.copyValue !== "undefined") {
keyFrame.value = source[keyFrame.copyValue].value;
}
keyFrameSet.forEach(function(keyFrames) {
// Copy everything from another keyframe in this array
/* istanbul ignore if */
if (keyFrame.copyFrame) {
keyFrame = source[keyFrame.copyFrame];
}
// Pad the right side of keyFrames arrays with null
for (var i = keyFrames.length; i < cuePoints.length; i++) {
keyFrames.push(null);
}
previousVal = keyFrame.value;
keyFrames.forEach(function(keyFrame, i, source) {
} else {
if (keyFrame !== null) {
if (i === source.length - 1) {
keyFrame = {
value: previousVal,
easing: "linear"
};
} else {
keyFrame = null;
}
// keyFrames need to be converted to objects
if (typeof keyFrame !== "object") {
keyFrame = {
step: keyFrame,
easing: "linear"
};
}
source[i] = keyFrame;
// Replace step values
if (typeof keyFrame.step !== "undefined") {
keyFrame.value = keyFrame.step === false ?
previousVal : previousVal + keyFrame.step;
}
}, this);
});
// Set a default easing function
if (!keyFrame.easing) {
keyFrame.easing = "linear";
}
this.normalizedKeyFrames = keyFrameSet;
// Copy value from another frame
/* istanbul ignore if */
if (typeof keyFrame.copyValue !== "undefined") {
keyFrame.value = source[keyFrame.copyValue].value;
}
return this;
}
}
// Copy everything from another keyframe in this array
/* istanbul ignore if */
if (keyFrame.copyFrame) {
keyFrame = source[keyFrame.copyFrame];
}
previousVal = keyFrame.value;
/**
* Placeholders for Symbol
*/
Animation.keys = "@@keys";
Animation.normalize = "@@normalize";
Animation.render = "@@render";
} else {
/**
* Temporal will run up the CPU. temporalFallback is used
* for long running animations.
*/
Animation.TemporalFallback = class {
constructor(animation) {
this.interval = setInterval(() => {
animation.loopFunction({
calledAt: Date.now()
});
}, animation.rate);
}
stop() {
if (this.interval) {
clearInterval(this.interval);
}
}
};
if (i === source.length - 1) {
keyFrame = {
value: previousVal,
easing: "linear"
};
} else {
keyFrame = null;
}
/**
* Animation.Segment()
*
* Create a defaulted segment.
*
* Every property ever used on an animation segment
* MUST be listed here, otherwise properties will
* persist across segments. This default object is
* primarily for resetting state.
*
*/
Animation.Segment = class {
constructor(options) {
this.cuePoints = [0, 1];
this.duration = 1000;
this.easing = "linear";
this.loop = false;
this.loopback = 0;
this.metronomic = false;
this.currentSpeed = 1;
this.progress = 0;
this.fps = 60;
this.rate = 1000 / 60;
this.paused = false;
this.isRunning = false;
this.segments = [];
this.onstart = null;
this.onpause = null;
this.onstop = null;
this.oncomplete = null;
this.onloop = null;
if (options) {
Object.assign(this, options);
if (options.segments) {
this.segments = options.segments.slice();
}
source[i] = keyFrame;
}
}
};
}, this);
});
this.normalizedKeyFrames = keyFrameSet;
return this;
};
module.exports = Animation;

@@ -42,6 +42,3 @@ var IS_TEST_MODE = !!process.env.IS_TEST_MODE;

// via known path pattern match.
serialport.list(function(err, result) {
// serialport.list() will never result in an error.
// On failure, an empty array is returned. (#768)
serialport.list().then(result => {
var ports = result.filter(function(val) {

@@ -52,3 +49,3 @@ var available = true;

// ttyUSB#, cu.usbmodem#, COM#
if (!rport.test(val.comName)) {
if (!rport.test(val.path)) {
available = false;

@@ -58,3 +55,3 @@ }

// Don't allow already used/encountered usb device paths
if (Serial.used.includes(val.comName)) {
if (Serial.used.includes(val.path)) {
available = false;

@@ -65,3 +62,3 @@ }

}).map(function(val) {
return val.comName;
return val.path;
});

@@ -108,3 +105,3 @@

callback.call(this, ports[0]);
}.bind(this));
});
},

@@ -111,0 +108,0 @@

+169
-190

@@ -1,16 +0,12 @@

var Board = require("./board");
var EVS = require("./evshield");
var within = require("./mixins/within");
var Fn = require("./fn");
var Emitter = require("events").EventEmitter;
var util = require("util");
var priv = new Map();
// var int16 = Fn.int16;
var uint16 = Fn.uint16;
var toFixed = Fn.toFixed;
const Board = require("./board");
const EVS = require("./evshield");
const within = require("./mixins/within");
const { uint16, toFixed, scale } = require("./fn");
const Emitter = require("events");
const priv = new Map();
var Controllers = {
const Controllers = {
DEFAULT: {
initialize: {
value: function(opts, dataHandler) {
value(options, dataHandler) {
this.io.pinMode(this.pin, this.io.MODES.ANALOG);

@@ -21,4 +17,4 @@ this.io.analogRead(this.pin, dataHandler);

toIntensityLevel: {
value: function(raw) {
return toFixed(Fn.scale(raw, 0, 1023, 0, 100) / 100, 2);
value(raw) {
return toFixed(scale(raw, 0, 1023, 0, 100) / 100, 2);
}

@@ -29,13 +25,13 @@ }

initialize: {
value: function(opts, dataHandler) {
var state = priv.get(this);
value(options, dataHandler) {
const state = priv.get(this);
if (opts.mode) {
opts.mode = opts.mode.toUpperCase();
if (options.mode) {
options.mode = options.mode.toUpperCase();
}
state.mode = opts.mode === "REFLECTED" ? EVS.Type_EV3_LIGHT_REFLECTED : EVS.Type_EV3_LIGHT;
state.mode = options.mode === "REFLECTED" ? EVS.Type_EV3_LIGHT_REFLECTED : EVS.Type_EV3_LIGHT;
state.shield = EVS.shieldPort(opts.pin);
state.ev3 = new EVS(Object.assign(opts, {
state.shield = EVS.shieldPort(options.pin);
state.ev3 = new EVS(Object.assign(options, {
io: this.io

@@ -45,4 +41,4 @@ }));

state.ev3.write(state.shield, 0x81 + state.shield.offset, state.mode);
state.ev3.read(state.shield, EVS.Light, EVS.Light_Bytes, function(data) {
var value = data[0] | (data[1] << 8);
state.ev3.read(state.shield, EVS.Light, EVS.Light_Bytes, (data) => {
const value = data[0] | (data[1] << 8);
dataHandler(value);

@@ -53,3 +49,3 @@ });

toIntensityLevel: {
value: function(raw) {
value(raw) {
return toFixed(raw / 100, 2);

@@ -61,19 +57,18 @@ }

initialize: {
value: function(opts, dataHandler) {
var state = priv.get(this);
value(options, dataHandler) {
const state = priv.get(this);
if (opts.mode) {
opts.mode = opts.mode.toUpperCase();
if (options.mode) {
options.mode = options.mode.toUpperCase();
}
state.mode = opts.mode === "REFLECTED" ? EVS.Type_NXT_LIGHT_REFLECTED : EVS.Type_NXT_LIGHT;
state.mode = options.mode === "REFLECTED" ? EVS.Type_NXT_LIGHT_REFLECTED : EVS.Type_NXT_LIGHT;
state.shield = EVS.shieldPort(opts.pin);
state.ev3 = new EVS(Object.assign(opts, {
state.shield = EVS.shieldPort(options.pin);
state.ev3 = new EVS(Object.assign(options, {
io: this.io
}));
state.ev3.setup(state.shield, state.mode);
state.ev3.read(state.shield, state.shield.analog, EVS.Analog_Bytes, function(data) {
var value = data[0] | (data[1] << 8);
dataHandler(value);
state.ev3.read(state.shield, state.shield.analog, EVS.Analog_Bytes, (data) => {
dataHandler(data[0] | (data[1] << 8));
});

@@ -83,4 +78,4 @@ }

toIntensityLevel: {
value: function(raw) {
return toFixed(Fn.scale(raw, 0, 1023, 100, 0) / 100, 2);
value(raw) {
return toFixed(scale(raw, 0, 1023, 100, 0) / 100, 2);
}

@@ -103,12 +98,9 @@ }

initialize: {
value: function(opts, dataHandler) {
var address = opts.address || 0x39;
var command = function(byte) {
// byte | 0b10000000;
return byte | 0x80;
};
value(options, dataHandler) {
const address = options.address || 0x39;
const command = byte => byte | 0b10000000;
opts.address = address;
options.address = address;
this.io.i2cConfig(opts);
this.io.i2cConfig(options);

@@ -133,31 +125,31 @@ // Page 15

// Integration time scaling factors
var LUX_SCALE = 14; // scale by (2 ** 14)
var RATIO_SCALE = 9; // scale ratio by (2 ** 9)
const LUX_SCALE = 14; // scale by (2 ** 14)
const RATIO_SCALE = 9; // scale ratio by (2 ** 9)
// Page 24
// T, FN, and CL Package coefficients
var K1T = 0x0040; // 0.125 * (2 ** RATIO_SCALE)
var B1T = 0x01F2; // 0.0304 * (2 ** LUX_SCALE)
var M1T = 0x01BE; // 0.0272 * (2 ** LUX_SCALE)
var K2T = 0x0080; // 0.250 * (2 ** RATIO_SCALE)
var B2T = 0x0214; // 0.0325 * (2 ** LUX_SCALE)
var M2T = 0x02D1; // 0.0440 * (2 ** LUX_SCALE)
var K3T = 0x00C0; // 0.375 * (2 ** RATIO_SCALE)
var B3T = 0x023F; // 0.0351 * (2 ** LUX_SCALE)
var M3T = 0x037B; // 0.0544 * (2 ** LUX_SCALE)
var K4T = 0x0100; // 0.50 * (2 ** RATIO_SCALE)
var B4T = 0x0270; // 0.0381 * (2 ** LUX_SCALE)
var M4T = 0x03FE; // 0.0624 * (2 ** LUX_SCALE)
var K5T = 0x0138; // 0.61 * (2 ** RATIO_SCALE)
var B5T = 0x016F; // 0.0224 * (2 ** LUX_SCALE)
var M5T = 0x01FC; // 0.0310 * (2 ** LUX_SCALE)
var K6T = 0x019A; // 0.80 * (2 ** RATIO_SCALE)
var B6T = 0x00D2; // 0.0128 * (2 ** LUX_SCALE)
var M6T = 0x00FB; // 0.0153 * (2 ** LUX_SCALE)
var K7T = 0x029A; // 1.3 * (2 ** RATIO_SCALE)
var B7T = 0x0018; // 0.00146 * (2 ** LUX_SCALE)
var M7T = 0x0012; // 0.00112 * (2 ** LUX_SCALE)
var K8T = 0x029A; // 1.3 * (2 ** RATIO_SCALE)
var B8T = 0x0000; // 0.000 * (2 ** LUX_SCALE)
var M8T = 0x0000; // 0.000 * (2 ** LUX_SCALE)
const K1T = 0x0040; // 0.125 * (2 ** RATIO_SCALE)
const B1T = 0x01F2; // 0.0304 * (2 ** LUX_SCALE)
const M1T = 0x01BE; // 0.0272 * (2 ** LUX_SCALE)
const K2T = 0x0080; // 0.250 * (2 ** RATIO_SCALE)
const B2T = 0x0214; // 0.0325 * (2 ** LUX_SCALE)
const M2T = 0x02D1; // 0.0440 * (2 ** LUX_SCALE)
const K3T = 0x00C0; // 0.375 * (2 ** RATIO_SCALE)
const B3T = 0x023F; // 0.0351 * (2 ** LUX_SCALE)
const M3T = 0x037B; // 0.0544 * (2 ** LUX_SCALE)
const K4T = 0x0100; // 0.50 * (2 ** RATIO_SCALE)
const B4T = 0x0270; // 0.0381 * (2 ** LUX_SCALE)
const M4T = 0x03FE; // 0.0624 * (2 ** LUX_SCALE)
const K5T = 0x0138; // 0.61 * (2 ** RATIO_SCALE)
const B5T = 0x016F; // 0.0224 * (2 ** LUX_SCALE)
const M5T = 0x01FC; // 0.0310 * (2 ** LUX_SCALE)
const K6T = 0x019A; // 0.80 * (2 ** RATIO_SCALE)
const B6T = 0x00D2; // 0.0128 * (2 ** LUX_SCALE)
const M6T = 0x00FB; // 0.0153 * (2 ** LUX_SCALE)
const K7T = 0x029A; // 1.3 * (2 ** RATIO_SCALE)
const B7T = 0x0018; // 0.00146 * (2 ** LUX_SCALE)
const M7T = 0x0012; // 0.00112 * (2 ** LUX_SCALE)
const K8T = 0x029A; // 1.3 * (2 ** RATIO_SCALE)
const B8T = 0x0000; // 0.000 * (2 ** LUX_SCALE)
const M8T = 0x0000; // 0.000 * (2 ** LUX_SCALE)

@@ -200,4 +192,4 @@ // Auto-gain thresholds

var GAIN_1X = 0x00;
var GAIN_16X = 0x10;
const GAIN_1X = 0x00;
const GAIN_16X = 0x10;

@@ -208,3 +200,3 @@ // var TI_13MS = 0x00;

var TintMs = [
const TintMs = [
// 0, TI_13MS

@@ -218,3 +210,3 @@ 13,

var TintDelayMs = [
const TintDelayMs = [
// 0, TI_13MS

@@ -234,3 +226,3 @@ 15,

var chScales = [
const chScales = [
// 0, TI_13MS

@@ -245,15 +237,15 @@ 0x07517,

// Gain and Tint defaults;
var gain = GAIN_16X;
var TintIndex = 0;
var Tint = TintMs[TintIndex];
var lux = 0;
let gain = GAIN_16X;
let TintIndex = 0;
let Tint = TintMs[TintIndex];
let lux = 0;
// if (typeof opts.gain !== "undefined") {
// if (typeof options.gain !== "undefined") {
// isAutoGain = false;
// gain = opts.gain;
// gain = options.gain;
// }
// if (typeof opts.integration !== "undefined") {
// if (typeof options.integration !== "undefined") {
// isAutoGain = false;
// Tint = opts.integration;
// Tint = options.integration;
// }

@@ -265,6 +257,6 @@

gain: {
get: function() {
get() {
return gain;
},
set: function(value) {
set(value) {
if (value !== GAIN_1X && value !== GAIN_16X) {

@@ -279,6 +271,6 @@ throw new RangeError("Invalid gain. Expected one of: 0, 16");

integration: {
get: function() {
get() {
return Tint;
},
set: function(value) {
set(value) {
TintIndex = TintMs.indexOf(value);

@@ -296,3 +288,3 @@

lux: {
get: function() {
get() {
return lux;

@@ -318,17 +310,17 @@ }

var read = function() {
setTimeout(function() {
const read = () => {
setTimeout(() => {
// Page 19
// Read ADC Channels Using Read Word Protocol − RECOMMENDED
this.io.i2cReadOnce(address, command(this.REGISTER.READ), 4, function(data) {
this.io.i2cReadOnce(address, command(this.REGISTER.READ), 4, (data) => {
// Page 23 - 28
// Simplified Lux Calculation
var ch0 = uint16(data[1], data[0]);
var ch1 = uint16(data[3], data[2]);
var b = 0;
var m = 0;
let ch0 = uint16(data[1], data[0]);
let ch1 = uint16(data[3], data[2]);
let b = 0;
let m = 0;
// Page 26
// CalculateLux(...)
var chScale = chScales[TintIndex];
let chScale = chScales[TintIndex];

@@ -345,3 +337,3 @@

var ratio1 = 0;
let ratio1 = 0;

@@ -354,3 +346,3 @@ if (ch0) {

var ratio = (ratio1 + 1) >> 1;
const ratio = (ratio1 + 1) >> 1;

@@ -384,3 +376,3 @@ if (ratio >= 0 && ratio <= K1T) {

var temp = (ch0 * b) - (ch1 * m);
let temp = (ch0 * b) - (ch1 * m);

@@ -400,4 +392,4 @@ if (temp < 0) {

});
}.bind(this), TintDelayMs[TintIndex]);
}.bind(this);
}, TintDelayMs[TintIndex]);
};

@@ -408,3 +400,3 @@ read();

toLux: {
value: function(raw) {
value(raw) {
return raw;

@@ -414,4 +406,4 @@ },

toIntensityLevel: {
value: function(raw) {
return toFixed(Fn.scale(raw, 0, 17000, 0, 100) / 100, 2);
value(raw) {
return toFixed(scale(raw, 0, 17000, 0, 100) / 100, 2);
},

@@ -427,19 +419,16 @@ },

initialize: {
value: function(opts, dataHandler) {
var address = opts.address || 0x23;
var mode = opts.mode || 0x10;
opts.address = address;
this.io.i2cConfig(opts);
value(options, dataHandler) {
const address = options.address || 0x23;
const mode = options.mode || 0x10;
options.address = address;
this.io.i2cConfig(options);
this.io.i2cWrite(address, mode);
var read = function() {
setTimeout(function() {
this.io.i2cReadOnce(address, 2, function(data) {
var raw = data[0];
raw <<= 8;
raw |= data[1];
dataHandler(raw);
const read = () => {
setTimeout(() => {
this.io.i2cReadOnce(address, 2, (data) => {
dataHandler((data[0] << 8) | data[1]);
read();
});
}.bind(this), 120);
}.bind(this);
}, 120);
};
read();

@@ -449,3 +438,3 @@ },

toLux: {
value: function(raw) {
value(raw) {
// Page 2

@@ -457,4 +446,4 @@ // H-Resolution Mode Resolution rHR - 1 - lx

toIntensityLevel: {
value: function(raw) {
return toFixed(Fn.scale(raw / 1.2, 0, 65535, 0, 100) / 100, 2);
value(raw) {
return toFixed(scale(raw / 1.2, 0, 65535, 0, 100) / 100, 2);
},

@@ -474,88 +463,78 @@ },

function Light(opts) {
class Light extends Emitter {
constructor(options) {
super();
if (!(this instanceof Light)) {
return new Light(opts);
}
let controller = null;
let raw = 0;
let last = 0;
const freq = options.freq || 25;
var controller = null;
var state = {};
var raw = 0;
var last = 0;
var freq = opts.freq || 25;
Board.Component.call(
this, options = Board.Options(options)
);
Board.Component.call(
this, opts = Board.Options(opts)
);
if (typeof options.controller === "string") {
controller = Controllers[options.controller];
} else {
controller = options.controller || Controllers.DEFAULT;
}
if (typeof opts.controller === "string") {
controller = Controllers[opts.controller];
} else {
controller = opts.controller || Controllers.DEFAULT;
}
Board.Controller.call(this, controller, options);
Board.Controller.call(this, controller, opts);
if (!this.toIntensityLevel) {
this.toIntensityLevel = options.toIntensityLevel || (x => x);
}
if (!this.toIntensityLevel) {
this.toIntensityLevel = opts.toIntensityLevel || function(x) {
return x;
};
}
if (!this.toLux) {
this.toLux = options.toLux || (x => x);
}
if (!this.toLux) {
this.toLux = opts.toLux || function(x) {
return x;
};
}
Object.defineProperties(this, {
value: {
get: function() {
return raw;
Object.defineProperties(this, {
value: {
get() {
return raw;
},
},
},
level: {
get: function() {
return this.toIntensityLevel(raw);
level: {
get() {
return this.toIntensityLevel(raw);
},
},
},
});
});
priv.set(this, state);
priv.set(this, {});
/* istanbul ignore else */
if (typeof this.initialize === "function") {
this.initialize(opts, function(data) {
raw = data;
});
}
/* istanbul ignore else */
if (typeof this.initialize === "function") {
this.initialize(options, data => raw = data);
}
if (typeof this.lux === "undefined") {
Object.defineProperty(this, "lux", {
get: function() {
return this.toLux(raw);
},
});
}
if (typeof this.lux === "undefined") {
Object.defineProperty(this, "lux", {
get() {
return this.toLux(raw);
},
});
}
var data = {
level: 0,
lux: 0,
};
const data = {
level: 0,
lux: 0,
};
setInterval(function() {
data.level = this.level;
data.lux = this.lux;
setInterval(() => {
data.level = this.level;
data.lux = this.lux;
this.emit("data", data);
this.emit("data", data);
if (raw !== last) {
last = raw;
this.emit("change", data);
}
}.bind(this), freq);
if (raw !== last) {
last = raw;
this.emit("change", data);
}
}, freq);
}
}
util.inherits(Light, Emitter);
Object.assign(Light.prototype, within);

@@ -565,3 +544,3 @@

/* istanbul ignore else */
if (!!process.env.IS_TEST_MODE) {
if (process.env.IS_TEST_MODE) {
Light.Controllers = Controllers;

@@ -568,0 +547,0 @@ Light.purge = function() {

@@ -122,2 +122,32 @@ var Board = require("./board");

},
PCA9685_Hybrid: {
setPWM: {
writable: true,
value: function(pin, speed) {
var state = priv.get(this);
state.expander.analogWrite(pin, speed);
}
},
initialize: {
value: function(opts) {
var state = priv.get(this);
this.address = opts.address || 0x40;
this.pwmRange = opts.pwmRange || [0, 4080];
this.frequency = opts.frequency || 50;
state.expander = Expander.get({
address: this.address,
controller: "PCA9685",
bus: this.bus,
pwmRange: this.pwmRange,
frequency: this.frequency,
});
this.pins.pwm = state.expander.normalize(this.pins.pwm);
}
}
},
EVS_EV3: {

@@ -1155,6 +1185,20 @@ initialize: {

},
PICAR_V: {
A: {
controller: "PCA9685_Hybrid",
pins: {
pwm: 4,
dir: "GPIO17"
}
},
B: {
controller: "PCA9685_Hybrid",
pins: {
pwm: 5,
dir: "GPIO27"
}
}
}
};
/**

@@ -1161,0 +1205,0 @@ * Motors()

{
"name": "johnny-five",
"description": "The JavaScript Robotics and Hardware Programming Framework. Use with: Arduino (all models), Electric Imp, Beagle Bone, Intel Galileo & Edison, Linino One, Pinoccio, pcDuino3, Raspberry Pi, Particle/Spark Core & Photon, Tessel 2, TI Launchpad and more!",
"version": "1.3.1",
"version": "1.4.0",
"homepage": "https://johnny-five.io",

@@ -155,3 +155,2 @@ "author": "Rick Waldron <waldron.rick@gmail.com>",

"color-convert": "~1.2.2",
"ease-component": "latest",
"lodash.clonedeep": "^4.3.0",

@@ -164,4 +163,4 @@ "lodash.debounce": "^4.0.3",

"browser-serialport": "latest",
"firmata": "^2.0.0",
"serialport": "^7.1.5"
"firmata": "^2.2.0",
"serialport": "^8.0.5"
},

@@ -173,5 +172,5 @@ "devDependencies": {

"coveralls": "^3.0.2",
"grunt": "^1.0.3",
"grunt": "^1.0.4",
"grunt-cli": "^1.2.0",
"grunt-contrib-jshint": "^1.1.0",
"grunt-contrib-jshint": "^2.1.0",
"grunt-contrib-nodeunit": "^2.0.0",

@@ -183,3 +182,3 @@ "grunt-contrib-watch": "^1.1.0",

"mock-firmata": "latest",
"nyc": "^12.0.2",
"nyc": "^14.1.1",
"optimist": "^0.6.1",

@@ -186,0 +185,0 @@ "sinon": "~1.10.2"