mirror of
https://github.com/Tonejs/Tone.js
synced 2024-11-16 08:38:00 +00:00
6604 lines
172 KiB
JavaScript
6604 lines
172 KiB
JavaScript
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
|
|
var manager = require('./lib/core/manager');
|
|
var domUtils = require('./lib/utils/dom');
|
|
var drawingUtils = require('./lib/utils/drawing');
|
|
var mathUtils = require('./lib/utils/math');
|
|
var extend = require('extend');
|
|
|
|
/************************************************
|
|
* INSTANTIATE NX MANAGER AND CREATE ELEMENTS *
|
|
************************************************/
|
|
|
|
window.nx = new manager();
|
|
window.nx.onload = function() {};
|
|
window.nx = extend(window.nx,domUtils)
|
|
window.nx = extend(window.nx,drawingUtils)
|
|
window.nx = extend(window.nx,mathUtils)
|
|
|
|
/* this onload function turns canvases into nexus elements,
|
|
* using the canvas's id as its var name */
|
|
|
|
window.onload = function() {
|
|
|
|
nx.addStylesheet();
|
|
|
|
// get all canvases on the page and add them to the manager
|
|
var allcanvi = document.getElementsByTagName("canvas");
|
|
for (i=0;i<allcanvi.length;i++) nx.transform(allcanvi[i]);
|
|
|
|
if (nx.isTouchDevice) {
|
|
document.addEventListener("touchmove", nx.blockMove, true);
|
|
document.addEventListener("touchstart", nx.blockMove, true);
|
|
}
|
|
|
|
nx.onload();
|
|
|
|
nx.startPulse();
|
|
|
|
};
|
|
},{"./lib/core/manager":2,"./lib/utils/dom":4,"./lib/utils/drawing":5,"./lib/utils/math":6,"extend":41}],2:[function(require,module,exports){
|
|
|
|
/**
|
|
@title NexusUI API
|
|
@overview NexusUI is a JavaScript toolkit for easily creating musical interfaces in web browsers. Interfaces are rendered on HTML5 canvases and are ideal for web audio projects, mobile apps, or for sending OSC to external audio applications like Max.
|
|
@author Ben Taylor, Jesse Allison, Yemin Oh, Sébastien Piquemal
|
|
@copyright © 2011-2014
|
|
@license MIT
|
|
*/
|
|
|
|
|
|
var timingUtils = require('../utils/timing');
|
|
var EventEmitter = require('events').EventEmitter;
|
|
var util = require('util');
|
|
var transmit = require('../utils/transmit');
|
|
|
|
|
|
var manager = module.exports = function() {
|
|
|
|
/**
|
|
|
|
@class nx
|
|
@description Central nexusUI manager with shared utility functions for all nexusUI objects
|
|
|
|
*/
|
|
|
|
EventEmitter.apply(this)
|
|
this.widgets = new Object();
|
|
|
|
/** @property {integer} throttlePeriod Throttle time in ms (for nx.throttle). */
|
|
this.throttlePeriod = 20;
|
|
this.elemTypeArr = new Array();
|
|
this.aniItems = new Array();
|
|
/** @property {boolean} showLabels Whether or not to draw an automatic text label on each interface component. */
|
|
this.showLabels = false;
|
|
this.starttime = new Date().getTime();
|
|
if (transmit) {
|
|
/**
|
|
@method sendsTo
|
|
@param {string or function} [destination] Protocol for transmitting data from interfaces (i.e. "js", "ajax", "ios", "max", or "node"). Also accepts custom functions.
|
|
```js
|
|
nx.sendsTo("ajax")
|
|
|
|
// or
|
|
|
|
nx.sendsTo(function(data) {
|
|
//define a custom transmission function
|
|
})
|
|
```
|
|
*/
|
|
this.sendsTo = transmit.setGlobalTransmit;
|
|
/**
|
|
@method setAjaxPath
|
|
@param {string} [path] If sending via AJAX, define the path to ajax destination
|
|
*/
|
|
this.setAjaxPath = transmit.setAjaxPath;
|
|
/** @property {string} destination NexusUI's transmission protocol (i.e. "js" or "ajax"). Defaults to "js". We recommend setting this property using nx.sendsTo() which ensures that all widgets receive this setting. */
|
|
this.destination = "js";
|
|
/** @property {string} ajaxPath If sending via AJAX, the destination path. Defaults to "lib/nexusOSCRelay.php". We recommend setting this property using nx.setAjaxPath() which ensures that all widgets receive this setting. */
|
|
this.ajaxPath = "lib/nexusOSCRelay.php";
|
|
}
|
|
|
|
/** @property {boolean} isTouchDevice Returns true if page is loaded on a touch device. */
|
|
this.isTouchDevice = ('ontouchstart' in document.documentElement)? true:false;
|
|
this.metas = document.getElementsByTagName('meta');
|
|
|
|
/** @property {boolean} globalWidgets Whether or not to instantiate a global variable for each widget (i.e. button1). Defaults to true. Designers of other softwares who wish to keep nexusUI entirely encapsulated in the nx object may set this property to false. In that case, all widgets are accessible in nx.widgets */
|
|
this.globalWidgets = true;
|
|
}
|
|
|
|
util.inherits(manager, EventEmitter)
|
|
|
|
|
|
/**
|
|
@method add
|
|
Adds a NexusUI element to the webpage. This will create an HTML5 canvas and draw the interface on it.
|
|
@param {string} [type] NexusUI widget type (i.e. "dial").
|
|
@param {object} [settings] (Optional.) Extra settings for the new widget. This settings object may have any of the following properties: x (integer in px), y, w (width), h (height), name (widget's OSC name and canvas ID), parent (the ID of the element you wish to add the canvas into). If no settings are provided, the element will be at default size and appended to the body of the HTML document.
|
|
*/
|
|
manager.prototype.add = function(type, args) {
|
|
//args may have optional properties: x, y, w, h, name, parent
|
|
|
|
if(type) {
|
|
var canv = document.createElement("canvas");
|
|
canv.setAttribute('nx', type);
|
|
if (args) {
|
|
if (args.x || args.y) {
|
|
canv.style.position = "absolute";
|
|
}
|
|
if (args.x) {
|
|
canv.style.left = args.x + "px";
|
|
}
|
|
if (args.y) {
|
|
canv.style.top = args.y + "px";
|
|
}
|
|
if (args.w) {
|
|
canv.style.width = args.w;
|
|
canv.width = args.w;
|
|
}
|
|
if (args.h) {
|
|
canv.style.height = args.h;
|
|
canv.height = args.h;
|
|
}
|
|
if (args.parent) {
|
|
var parent;
|
|
if (args.parent instanceof HTMLElement){
|
|
parent = args.parent;
|
|
} else if (args.parent instanceof jQuery){
|
|
parent = args.parent[0];
|
|
} else {
|
|
parent = document.getElementById(args.parent);
|
|
}
|
|
}
|
|
if (args.name) {
|
|
canv.id = args.name
|
|
}
|
|
}
|
|
if (!parent) {
|
|
var parent = document.body
|
|
}
|
|
parent.appendChild(canv);
|
|
return this.transform(canv);
|
|
}
|
|
}
|
|
|
|
/** @method transform
|
|
Transform an existing canvas into a NexusUI widget.
|
|
@param {string} [canvasID] The ID of the canvas to be transformed.
|
|
@param {string} [type] (Optional.) Specify which type of widget the canvas will become. If no type is given, the canvas must have an nx attribute with a valid widget type.
|
|
*/
|
|
manager.prototype.transform = function(canvas, type) {
|
|
for (var key in nx.widgets) {
|
|
if (nx.widgets[key].canvasID == canvas.id) {
|
|
return;
|
|
}
|
|
}
|
|
if (type) {
|
|
var nxType = type;
|
|
} else {
|
|
var nxType = canvas.getAttribute("nx");
|
|
}
|
|
|
|
if (!nxType) {
|
|
return;
|
|
}
|
|
var elemCount = 0;
|
|
var newObj;
|
|
|
|
/* find out how many of the same elem type have come before
|
|
i.e. nx.elemTypeArr will look like [ dial, dial, toggle, toggle ]
|
|
allowing you to count how many dials already exist on the page
|
|
and give your new dial the appropriate index and id: dial3 */
|
|
|
|
for (j=0;j<this.elemTypeArr.length;j++) {
|
|
if (this.elemTypeArr[j] === nxType) {
|
|
elemCount++;
|
|
}
|
|
}
|
|
|
|
// add your new nexus element type to the element list
|
|
this.elemTypeArr.push(nxType);
|
|
|
|
// check to see if it has a pre-given ID
|
|
// and use that as its id if so
|
|
if (!canvas.id) {
|
|
var idNum = elemCount + 1;
|
|
canvas.id = nxType + idNum;
|
|
}
|
|
|
|
if(nxType) {
|
|
try {
|
|
var newObj = new (require('../widgets')[nxType])(canvas.id);
|
|
} catch (err) {
|
|
console.log(nxType);
|
|
}
|
|
}
|
|
|
|
this.widgets[newObj.canvasID] = newObj;
|
|
if (this.globalWidgets) {
|
|
window[newObj.canvasID] = this.widgets[newObj.canvasID]
|
|
}
|
|
|
|
newObj.init();
|
|
return newObj;
|
|
}
|
|
|
|
/** @method transmit
|
|
The "output" instructions for sending a widget's data to another application or to a JS callback. Inherited by each widget and executed when each widget is interacted with or its value changes. Set using nx.sendsTo() to ensure that all widgets inherit the new function correctly.
|
|
@param {object} [data] The data to be transmitted. Each property of the object will become its own OSC message. (This works with objects nested to up to 2 levels).
|
|
*/
|
|
|
|
manager.prototype.transmit = function(data) {
|
|
this.makeOSC(this.emit, data);
|
|
this.emit('*',data);
|
|
}
|
|
|
|
/**
|
|
@method colorize
|
|
@param {string} [aspect] Which part of ui to change, i.e. "accent" "fill", "border"
|
|
@param {string} [color] Hex or rgb color code
|
|
Change the color of all nexus objects, by aspect ([fill, accent, border, accentborder]
|
|
|
|
```js
|
|
nx.colorize("#00ff00") // changes the accent color by default
|
|
nx.colorize("border", "#000000") // changes the border color
|
|
```
|
|
|
|
**/
|
|
manager.prototype.colorize = function(aspect, newCol) {
|
|
|
|
if (!newCol) {
|
|
// just sending in a color value colorizes the accent
|
|
newCol = aspect;
|
|
aspect = "accent";
|
|
}
|
|
|
|
this.colors[aspect] = newCol;
|
|
|
|
for (var key in this.widgets) {
|
|
this.widgets[key].colors[aspect] = newCol;
|
|
this.widgets[key].draw();
|
|
}
|
|
}
|
|
|
|
|
|
/** @method setThrottlePeriod
|
|
Set throttle time of nx.throttle, which controls rapid network transmissions of widget data.
|
|
@param {integer} [throttle time] Throttle time in milliseconds.
|
|
*/
|
|
manager.prototype.setThrottlePeriod = function(newThrottle) {
|
|
this.throttlePeriod = newThrottle;
|
|
for (var key in this.widgets) {
|
|
this.widgets[key].throttlePeriod = this.throttlePeriod;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* GUI
|
|
*/
|
|
|
|
/** @property {object} colors The interface's color settings. Set with nx.colorize(). */
|
|
manager.prototype.colors = {
|
|
"accent": "#ff5500",
|
|
"fill": "#eee",
|
|
"border": "#bbb",
|
|
"black": "#000",
|
|
"white": "#FFF"
|
|
};
|
|
|
|
/** @method startPulse
|
|
Start an animation interval for animated widgets (calls nx.pulse() every 30 ms). Executed by default when NexusUI loads.
|
|
*/
|
|
manager.prototype.startPulse = function() {
|
|
this.pulseInt = setInterval("nx.pulse()", 30);
|
|
}
|
|
|
|
/** @method stopPulse
|
|
Stop the animation pulse interval.
|
|
*/
|
|
manager.prototype.stopPulse = function() {
|
|
clearInterval(this.pulseInt);
|
|
}
|
|
|
|
/** @method pulse
|
|
Animation pulse which executes all functions stored in the nx.aniItems array.
|
|
*/
|
|
manager.prototype.pulse = function() {
|
|
for (var i=0;i<this.aniItems.length;i++) {
|
|
this.aniItems[i]();
|
|
}
|
|
}
|
|
|
|
manager.prototype.addAni = function(fn) {
|
|
|
|
}
|
|
|
|
manager.prototype.removeAni = function(fn) {
|
|
this.aniItems.splice(this.aniItems.indexOf(fn));
|
|
}
|
|
|
|
manager.prototype.addStylesheet = function() {
|
|
var htmlstr = '<style>'
|
|
+ 'select {'
|
|
+ 'background: transparent;'
|
|
+ '-webkit-appearance: none;'
|
|
+ 'width: 150px;'
|
|
+ 'padding: 5px 5px;'
|
|
+ 'font-size: 16px;'
|
|
+ 'color:#888;'
|
|
+ 'border: solid 2px #CCC;'
|
|
+ 'border-radius: 6;'
|
|
+ 'outline: black;'
|
|
+ 'cursor:pointer;'
|
|
+ 'background-color:#F7F7F7;'
|
|
+ 'font-family:gill sans;'
|
|
+ '}'
|
|
+ ''
|
|
+ 'canvas { '
|
|
+ 'cursor:pointer;'
|
|
+ 'border-radius:5px;'
|
|
+ 'moz-border-radius:5px;'
|
|
+ 'webkit-border-radius:5px;'
|
|
+ 'box-sizing:border-box;'
|
|
+ '-moz-box-sizing:border-box;'
|
|
+ '-webkit-box-sizing:border-box;'
|
|
+ '}'
|
|
+ '</style>';
|
|
|
|
document.head.innerHTML = document.head.innerHTML + htmlstr
|
|
}
|
|
|
|
/** @method setViewport
|
|
Set mobile viewport scale (similar to a zoom)
|
|
@param {integer} [scale] Zoom ratio (i.e. 0.5, 1, 2) */
|
|
manager.prototype.setViewport = function(scale) {
|
|
for (i=0; i<this.metas.length; i++) {
|
|
if (this.metas[i].name == "viewport") {
|
|
this.metas[i].content = "minimum-scale="+scale+", maximum-scale="+scale;
|
|
}
|
|
}
|
|
}
|
|
|
|
/** @method setLabels
|
|
Tell all widgets whether or not draw text labels on widgets
|
|
@param {boolean} [on/off] true to add labels, false to remove labels
|
|
*/
|
|
manager.prototype.setLabels = function(onoff) {
|
|
if (onoff=="on") {
|
|
this.showLabels = true;
|
|
} else {
|
|
this.showLabels = false;
|
|
}
|
|
for (var key in this.widgets) {
|
|
this.widgets[key].draw()
|
|
}
|
|
}
|
|
|
|
|
|
manager.prototype.blockMove = function(e) {
|
|
if (e.target.tagName == 'CANVAS') {
|
|
e.preventDefault();
|
|
e.stopPropogation();
|
|
}
|
|
}
|
|
},{"../utils/timing":7,"../utils/transmit":8,"../widgets":16,"events":36,"util":40}],3:[function(require,module,exports){
|
|
var EventEmitter = require('events').EventEmitter;
|
|
var util = require('util');
|
|
var domUtils = require('../utils/dom');
|
|
var drawingUtils = require('../utils/drawing');
|
|
var timingUtils = require('../utils/timing');
|
|
var transmit = require('../utils/transmit');
|
|
|
|
|
|
|
|
var widget = module.exports = function (target) {
|
|
EventEmitter.apply(this)
|
|
this.preClick = this.preClick.bind(this)
|
|
this.preMove = this.preMove.bind(this)
|
|
this.preRelease = this.preRelease.bind(this)
|
|
this.preTouch = this.preTouch.bind(this)
|
|
this.preTouchMove = this.preTouchMove.bind(this)
|
|
this.preTouchRelease = this.preTouchRelease.bind(this)
|
|
|
|
/**
|
|
|
|
@class widget
|
|
All NexusUI interface widgets inherit from the widget class. The properties and methods of the widget class are usable by any NexusUI interface.
|
|
|
|
*/
|
|
|
|
/** @property {string} canvasID ID attribute of the interface's HTML5 canvas */
|
|
this.canvasID = target;
|
|
/** @property {string} oscPath OSC prefix for this interface. By default this is populated using the canvas ID (i.e. an ID of dial1 has OSC path /dial1) */
|
|
this.oscPath = "/"+target;
|
|
if (!document.getElementById(target)) {
|
|
var newcanv = document.createElement("canvas")
|
|
newcanv.id = target;
|
|
document.body.appendChild(newcanv)
|
|
}
|
|
/** @property {DOM element} canvas The widget's HTML5 canvas */
|
|
this.canvas = document.getElementById(target);
|
|
/** @property {HTML5 drawing context} context The canvas's drawing context */
|
|
this.context = this.canvas.getContext("2d");
|
|
this.canvas.height = window.getComputedStyle(document.getElementById(target), null).getPropertyValue("height").replace("px","");
|
|
this.canvas.width = window.getComputedStyle(document.getElementById(target), null).getPropertyValue("width").replace("px","");
|
|
/** @property {integer} height The widget canvas's computed height in pixels */
|
|
this.height = parseInt(window.getComputedStyle(document.getElementById(target), null).getPropertyValue("height").replace("px",""));
|
|
/** @property {integer} width The widget canvas's computed width in pixels */
|
|
this.width = parseInt(window.getComputedStyle(document.getElementById(target), null).getPropertyValue("width").replace("px",""));
|
|
if (!this.defaultSize) {
|
|
/** @property {object} defaultSize The widget's default size if not defined with HTML/CSS style. (Has properties 'width' and 'height', both in pixels) */
|
|
this.defaultSize = { width: 100, height: 100 };
|
|
}
|
|
if (this.width==300 && this.height==150) {
|
|
this.canvas.width = this.defaultSize.width*2;
|
|
this.canvas.height = this.defaultSize.height*2;
|
|
this.width = this.defaultSize.width;
|
|
this.height = this.defaultSize.height;
|
|
} else {
|
|
var proxyw = this.width;
|
|
var proxyh = this.height;
|
|
this.canvas.width = proxyw*2;
|
|
this.canvas.height = proxyh*2;
|
|
this.width = proxyw;
|
|
this.height = proxyh;
|
|
}
|
|
this.canvas.style.width = this.canvas.width/2+"px";
|
|
this.canvas.style.height = this.canvas.height/2+"px";
|
|
this.context.scale(2,2)
|
|
/** @property {object} offset The widget's computed offset from the top left of the document. (Has properties 'top' and 'left', both in pixels) */
|
|
this.offset = domUtils.findPosition(this.canvas);
|
|
/** @property {object} center The center of the widget's canvas. A 100x100 widget would have a center at 50x50. (Has properties 'x' and 'y', both in pixels) */
|
|
this.center = {
|
|
x: this.width/2,
|
|
y: this.height/2
|
|
};
|
|
//drawing
|
|
/** @property {integer} lineWidth The default line width for drawing (default is 2 pixels). In many widgets, this is overwritten to suite the widget. However it does dictate the border width on most widgets. */
|
|
this.lineWidth = 2;
|
|
this.context.lineWidth = this.lineWidth;
|
|
/** @property {object} colors A widget's individual color scheme. Inherited from nx.colors. (Has properties "accent", "fill", "border", "black", and "white") */
|
|
this.colors = new Object();
|
|
// define colors individually so they are not pointers to nx.colors
|
|
// this way each object can have its own color scheme
|
|
this.colors.accent = nx.colors.accent;
|
|
this.colors.fill = nx.colors.fill;
|
|
this.colors.border = nx.colors.border;
|
|
this.colors.accentborder = nx.colors.accentborder;
|
|
this.colors.black = nx.colors.black;
|
|
this.colors.white = nx.colors.white;
|
|
this.colors.highlight = nx.colors.highlight;
|
|
//interaction
|
|
/** @property {object} clickPos The most recent mouse/touch position when interating with a widget. (Has properties x and y) */
|
|
this.clickPos = {x: 0, y: 0};
|
|
/** @property {array} clickPos.touches If multitouch, an array of touch positions */
|
|
this.clickPos.touches = new Array();
|
|
/** @property {boolean} clicked Whether or not the widget is currently clicked */
|
|
this.clicked = false;
|
|
this.value = 0;
|
|
/**
|
|
@property {object} val An object containing the core interactive values of the widget, which are also the widget's data output.
|
|
*/
|
|
this.val = new Object();
|
|
this.pval = new Object();
|
|
this.nodePos = new Array();
|
|
/** @property {object} deltaMove Difference between the current touch/mouse position and the previous touch/mouse position, in pixels. */
|
|
this.deltaMove = new Object();
|
|
this.throttlePeriod = nx.throttlePeriod;
|
|
this.throttle = timingUtils.throttle;
|
|
/** @property {boolean} label Whether or not to draw a text label this widget. */
|
|
this.label = false;
|
|
this.hasMoved = false;
|
|
//recording
|
|
/** @property {boolean} isRecording Whether or not this widget's output is being recorded to a "remix" widget */
|
|
this.isRecording = false;
|
|
this.tapeNum = 0;
|
|
this.recorder = null;
|
|
//transmission
|
|
if (transmit) {
|
|
/** @method sendsTo
|
|
Set the transmission protocol for this widget individually
|
|
@param {string or function} [destination] Protocol for transmitting data from this widget (i.e. "js", "ajax", "ios", "max", or "node"). Also accepts custom functions.
|
|
```js
|
|
dial1.sendsTo("ajax")
|
|
|
|
// or
|
|
|
|
dial1.sendsTo(function(data) {
|
|
//define a custom transmission function
|
|
})
|
|
```
|
|
*/
|
|
this.sendsTo = transmit.setWidgetTransmit;
|
|
this.destination = "js";
|
|
}
|
|
this.events = new Object();
|
|
|
|
// Setup interaction
|
|
if (nx.isTouchDevice) {
|
|
this.canvas.ontouchstart = this.preTouch;
|
|
this.canvas.ontouchmove = this.preTouchMove;
|
|
this.canvas.ontouchend = this.preTouchRelease;
|
|
} else {
|
|
this.canvas.addEventListener('mousedown', this.preClick, false);
|
|
}
|
|
|
|
}
|
|
util.inherits(widget, EventEmitter)
|
|
|
|
/** @method transmit
|
|
The "output" instructions for sending the widget's data to another application or to a JS callback. Inherited from nx.transmit and executed when each widget is interacted with or during animation. Set using .sendsTo() to use our built-in transmission defintions.
|
|
@param {object} [data] The data to be transmitted. Each property of the object will become its own OSC message if sending via "ajax" or "max7" protocols. (This works with objects nested to up to 2 levels).
|
|
*/
|
|
widget.prototype.transmit = nx.transmit;
|
|
|
|
/** @method makeOSC
|
|
Loops through an object (i.e. a widget's data), creates OSC path/value pairs, and executes a callback function with these two arguments.
|
|
@param {function} [callback] A function defining the action to be taken with each OSC path/value pair. This function should have two parameters, path (string) and data (type depends on widget data type).
|
|
@param {object} [data] The data as an object, to be broken into individual OSC messages.
|
|
*/
|
|
widget.prototype.makeOSC = function(action, data) {
|
|
this.action = action;
|
|
if ((typeof data == "object") && (data !== null)) {
|
|
for (var key in data) {
|
|
if ((typeof data[key] == "object") && (data[key] !== null)) {
|
|
for (var key2 in data[key]) {
|
|
this.action(key+"/"+key2, data[key][key2])
|
|
}
|
|
} else {
|
|
this.action(key, data[key])
|
|
}
|
|
}
|
|
} else if (typeof data == "number" || typeof data == "string") {
|
|
this.action('value', data)
|
|
}
|
|
}
|
|
|
|
// getoffset is useful as an API for others
|
|
// otherwise they would have to write
|
|
// dial1.offset = utils.findPosition()
|
|
// now it is simply:
|
|
// dial1.getOffset()
|
|
|
|
/** @method getOffset
|
|
Recalculate the computed offset of the widget's canvas and store it in widget.offset. This is useful if a widget has been moved after being created.
|
|
*/
|
|
widget.prototype.getOffset = function() {
|
|
this.offset = domUtils.findPosition(this.canvas)
|
|
}
|
|
|
|
widget.prototype.preClick = function(e) {
|
|
this.offset = domUtils.findPosition(this.canvas)
|
|
document.addEventListener("mousemove", this.preMove, false);
|
|
document.addEventListener("mouseup", this.preRelease, false);
|
|
this.clickPos = domUtils.getCursorPosition(e, this.offset);
|
|
this.clicked = true;
|
|
this.deltaMove.x = 0;
|
|
this.deltaMove.y = 0;
|
|
this.click(e);
|
|
document.body.style.userSelect = "none";
|
|
document.body.style.mozUserSelect = "none";
|
|
document.body.style.webkitUserSelect = "none";
|
|
}
|
|
|
|
widget.prototype.preMove = function(e) {
|
|
var newClickPos = domUtils.getCursorPosition(e, this.offset);
|
|
this.deltaMove.y = newClickPos.y - this.clickPos.y;
|
|
this.deltaMove.x = newClickPos.x - this.clickPos.x;
|
|
this.clickPos = newClickPos;
|
|
this.move(e);
|
|
}
|
|
|
|
widget.prototype.preRelease = function(e) {
|
|
|
|
document.removeEventListener("mousemove", this.preMove, false);
|
|
document.removeEventListener("mouseup", this.preRelease, false);
|
|
this.clicked = false;
|
|
this.release();
|
|
document.body.style.userSelect = "text";
|
|
document.body.style.mozUserSelect = "text";
|
|
document.body.style.webkitUserSelect = "text";
|
|
}
|
|
|
|
widget.prototype.preTouch = function(e) {
|
|
this.clickPos = domUtils.getTouchPosition(e, this.offset);
|
|
this.clicked = true;
|
|
this.deltaMove.x = 0;
|
|
this.deltaMove.y = 0;
|
|
this.touch(e);
|
|
}
|
|
|
|
widget.prototype.preTouchMove = function(e) {
|
|
if (this.clicked) {
|
|
var newClickPos = domUtils.getTouchPosition(e, this.offset);
|
|
this.deltaMove.y = newClickPos.y - this.clickPos.y;
|
|
this.deltaMove.x = newClickPos.x - this.clickPos.x;
|
|
this.clickPos = newClickPos;
|
|
this.touchMove(e);
|
|
}
|
|
}
|
|
|
|
widget.prototype.preTouchRelease = function(e) {
|
|
if (e.targetTouches.length>=1) {
|
|
var newClickPos = domUtils.getTouchPosition(e, this.offset);
|
|
this.clickPos = newClickPos;
|
|
} else {
|
|
this.clicked = false;
|
|
}
|
|
this.touchRelease();
|
|
}
|
|
|
|
|
|
/** @method init
|
|
Initialize or re-initialize the widget. Defined separately within each widget.
|
|
*/
|
|
|
|
/** @method draw
|
|
Draw the widget onto the canvas.
|
|
*/
|
|
widget.prototype.draw = function() {
|
|
}
|
|
|
|
|
|
/** @method click
|
|
Executes when the widget is clicked on
|
|
*/
|
|
widget.prototype.click = function() {
|
|
}
|
|
|
|
|
|
/** @method move
|
|
Executes on drag (mouse moves while clicked).
|
|
*/
|
|
widget.prototype.move = function() {
|
|
}
|
|
|
|
|
|
/** @method release
|
|
Executes when the mouse releases after having clicked on the widget.
|
|
*/
|
|
widget.prototype.release = function() {
|
|
}
|
|
|
|
/** @method touch
|
|
Executes when the widget is touched on a touch device.
|
|
*/
|
|
widget.prototype.touch = function() {
|
|
this.click();
|
|
}
|
|
|
|
/** @method touchMove
|
|
Executes on drag (touch then move) on a touch device
|
|
*/
|
|
widget.prototype.touchMove = function() {
|
|
this.move();
|
|
}
|
|
|
|
/** @method touchRelease
|
|
Executes when the touch releases after having touched the widget.
|
|
*/
|
|
widget.prototype.touchRelease = function() {
|
|
this.release();
|
|
}
|
|
|
|
widget.prototype.adjustSizeIfDefault = function() {
|
|
if (this.width==300 && this.height==150) {
|
|
this.canvas.width = this.defaultSize.width;
|
|
this.canvas.height = this.defaultSize.height;
|
|
this.width = this.defaultSize.width;
|
|
this.height = this.defaultSize.height;
|
|
}
|
|
}
|
|
|
|
widget.prototype.makeRoundedBG = function() {
|
|
this.bgLeft = this.lineWidth;
|
|
this.bgRight = this.width - this.lineWidth;
|
|
this.bgTop = this.lineWidth;
|
|
this.bgBottom = this.height - this.lineWidth;
|
|
this.bgHeight = this.bgBottom - this.lineWidth;
|
|
this.bgWidth = this.bgRight - this.lineWidth;
|
|
|
|
drawingUtils.makeRoundRect(this.context, this.bgLeft, this.bgTop, this.bgWidth, this.bgHeight);
|
|
}
|
|
|
|
/** @method erase
|
|
Erase the widget's canvas.
|
|
*/
|
|
widget.prototype.erase = function() {
|
|
this.context.clearRect(0,0,this.width,this.height);
|
|
}
|
|
|
|
widget.prototype.hideCursor = function() {
|
|
this.canvas.style.cursor = "none";
|
|
}
|
|
|
|
widget.prototype.showCursor = function() {
|
|
this.canvas.style.cursor = "auto";
|
|
}
|
|
|
|
// allow us to get the constructor function name programatically
|
|
//i.e. if element is a dial, this function will return "dial"
|
|
|
|
/** @method getName
|
|
Returns the widget's constructor function name (i.e. "dial")
|
|
*/
|
|
widget.prototype.getName = function() {
|
|
var funcNameRegex = /function (.{1,})\(/;
|
|
var results = (funcNameRegex).exec((this).constructor.toString());
|
|
return (results && results.length > 1) ? results[1] : "";
|
|
}
|
|
|
|
/** @method set
|
|
Manually set a widget's value (that is, set any properties of a widget's .val). See widget.val or the .val property of individual widgets for more info.
|
|
@param {object} [data] Parameter/value pairs in object notation.
|
|
@param {boolean} [transmit] (optional) Whether or not to transmit new value after being set.
|
|
Sets the value of an object.
|
|
|
|
```js
|
|
position1.set({
|
|
x: 100,
|
|
y: 250
|
|
})
|
|
```
|
|
|
|
An optional second argument decides whether the object then transmits its new value.
|
|
```js
|
|
button1.set({
|
|
press: 100
|
|
}, true)
|
|
```
|
|
*/
|
|
widget.prototype.set = function(data, transmit) {
|
|
|
|
if (typeof this.val == "object" && this.val !== "null") {
|
|
if (typeof data == "object" && data !== "null") {
|
|
for (var key in data) {
|
|
this.val[key] = data[key];
|
|
}
|
|
}
|
|
} else if (typeof this.val == "string" || typeof this.val == "number") {
|
|
if (typeof data == "object" && data !== "null") {
|
|
this.val = data["value"];
|
|
this.draw();
|
|
} else if (typeof data == "string" || typeof data == "number") {
|
|
this.val = data;
|
|
}
|
|
}
|
|
this.draw();
|
|
|
|
if (transmit) {
|
|
this.transmit(this.val)
|
|
}
|
|
}
|
|
|
|
/** @method destroy
|
|
Remove the widget object, canvas, and all related event listeners from the document.
|
|
*/
|
|
widget.prototype.destroy = function() {
|
|
var type = nx.elemTypeArr.indexOf(this.getName())
|
|
nx.elemTypeArr.splice(type,1)
|
|
|
|
this.canvas.ontouchmove = null;
|
|
this.canvas.ontouchend = null;
|
|
this.canvas.onclick = null;
|
|
this.canvas.onmousemove = null;
|
|
this.canvas.onmouseoff = null;
|
|
document.removeEventListener("mousemove", this.preMove, false);
|
|
document.removeEventListener("mouseup", this.preRelease, false);
|
|
|
|
var elemToKill = document.getElementById(this.canvasID)
|
|
if (elemToKill) {
|
|
elemToKill.parentNode.removeChild(elemToKill);
|
|
}
|
|
|
|
this.customDestroy();
|
|
|
|
var id = this.canvasID
|
|
delete nx.widgets[id];
|
|
delete window[id];
|
|
|
|
}
|
|
|
|
widget.prototype.customDestroy = function() {
|
|
|
|
}
|
|
|
|
widget.prototype.wrapText = function(text, x, y, maxWidth, lineHeight) {
|
|
if (text) {
|
|
var words = text.split(' ');
|
|
var line = '';
|
|
|
|
for(var n = 0; n < words.length; n++) {
|
|
var testLine = line + words[n] + ' ';
|
|
var metrics = this.context.measureText(testLine);
|
|
var testWidth = metrics.width;
|
|
if (testWidth > maxWidth && n > 0) {
|
|
this.context.fillText(line, x, y);
|
|
line = words[n] + ' ';
|
|
y += lineHeight;
|
|
}
|
|
else {
|
|
line = testLine;
|
|
}
|
|
}
|
|
this.context.fillText(line, x, y);
|
|
}
|
|
}
|
|
|
|
widget.prototype.drawLabel = function() {
|
|
if (this.showLabels) {
|
|
with(this.context) {
|
|
globalAlpha = 0.9;
|
|
fillStyle = this.colors.fill;
|
|
fillRect(this.width-100,this.height-20,100,20);
|
|
globalAlpha = 1;
|
|
beginPath();
|
|
fillStyle = this.colors.border;
|
|
font = "bold 15px courier";
|
|
textAlign = "center";
|
|
fillText(this.oscPath,this.width-50,this.height-5);
|
|
textAlign = "left";
|
|
closePath();
|
|
}
|
|
}
|
|
}
|
|
|
|
/** @method saveCanv
|
|
Download the widget's current graphical state as an image (png).
|
|
*/
|
|
widget.prototype.saveCanv = function() {
|
|
var data = this.canvas.toDataURL("image/png").replace("image/png", "image/octet-stream");
|
|
window.location.href = data
|
|
}
|
|
},{"../utils/dom":4,"../utils/drawing":5,"../utils/timing":7,"../utils/transmit":8,"events":36,"util":40}],4:[function(require,module,exports){
|
|
|
|
/** @class utils
|
|
Shared utility functions. These functions are exposed as methods of nx in NexusUI projects, i.e. .mtof() here can be accessed in your project with nx.mtof().
|
|
*/
|
|
|
|
|
|
/** @method findPosition
|
|
Returns the offset of an HTML element. Returns an object with 'top' and 'left' properties.
|
|
@param {DOM element} [element]
|
|
```js
|
|
var button1Offset = nx.findPosition(button1.canvas)
|
|
```
|
|
*/
|
|
exports.findPosition = function(element) {
|
|
var body = document.body,
|
|
win = document.defaultView,
|
|
docElem = document.documentElement,
|
|
box = document.createElement('div');
|
|
box.style.paddingLeft = box.style.width = "1px";
|
|
body.appendChild(box);
|
|
var isBoxModel = box.offsetWidth == 2;
|
|
body.removeChild(box);
|
|
box = element.getBoundingClientRect();
|
|
var clientTop = docElem.clientTop || body.clientTop || 0,
|
|
clientLeft = docElem.clientLeft || body.clientLeft || 0,
|
|
scrollTop = win.pageYOffset || isBoxModel && docElem.scrollTop || body.scrollTop,
|
|
scrollLeft = win.pageXOffset || isBoxModel && docElem.scrollLeft || body.scrollLeft;
|
|
return {
|
|
top : box.top + scrollTop - clientTop,
|
|
left: box.left + scrollLeft - clientLeft
|
|
};
|
|
}
|
|
|
|
exports.getCursorPosition = function(e, canvas_offset) {
|
|
var x;
|
|
var y;
|
|
if (e.pageX != undefined && e.pageY != undefined) {
|
|
x = e.pageX;
|
|
y = e.pageY;
|
|
} else {
|
|
x = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
|
|
y = e.clientY + document.body.scrollTop + document.documentElement.scrollTop;
|
|
}
|
|
x -= canvas_offset.left;
|
|
y -= canvas_offset.top;
|
|
var click_position = {x: x, y: y};
|
|
click_position.touches = [ {x: x, y: y } ];
|
|
return click_position;
|
|
}
|
|
|
|
exports.getTouchPosition = function(e, canvas_offset) {
|
|
var x;
|
|
var y;
|
|
x = e.targetTouches[0].pageX;
|
|
y = e.targetTouches[0].pageY;
|
|
x -= canvas_offset.left;
|
|
y -= canvas_offset.top;
|
|
var click_position = {x: x, y: y};
|
|
|
|
click_position.touches = new Array();
|
|
for (var i=0;i<e.targetTouches.length;i++) {
|
|
click_position.touches.push({
|
|
x: e.targetTouches[i].pageX - canvas_offset.left,
|
|
y: e.targetTouches[i].pageY - canvas_offset.top
|
|
});
|
|
}
|
|
click_position.changed = new Array();
|
|
for (var i=0;i<e.changedTouches.length;i++) {
|
|
click_position.changed.push({
|
|
x: e.changedTouches[i].pageX - canvas_offset.left,
|
|
y: e.changedTouches[i].pageY - canvas_offset.top
|
|
});
|
|
}
|
|
return click_position;
|
|
}
|
|
},{}],5:[function(require,module,exports){
|
|
var math = require('./math')
|
|
|
|
/** @method randomColor
|
|
Returns a random color string in rgb format
|
|
*/
|
|
exports.randomColor = function() {
|
|
return "rgb(" + math.random(250) + "," + math.random(250) + "," + math.random(250) + ")";
|
|
}
|
|
|
|
/** @method hexToRgb
|
|
Converts a hex color code to rgb format
|
|
@param {color code} [hex] Input color code in hex format
|
|
@param {float} [alpha] Color alpha level
|
|
*/
|
|
exports.hexToRgb = function(hex, a) {
|
|
// Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
|
|
var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
|
|
hex = hex.replace(shorthandRegex, function(m, r, g, b) {
|
|
return r + r + g + g + b + b;
|
|
});
|
|
|
|
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
|
|
if (!a) {
|
|
a = 0.5;
|
|
}
|
|
|
|
var r = parseInt(result[1], 16);
|
|
var g = parseInt(result[2], 16);
|
|
var b = parseInt(result[3], 16);
|
|
|
|
return "rgba(" + r + "," + g + "," + b + "," + a + ")";
|
|
}
|
|
|
|
exports.isInside = function(clickedNode,currObject) {
|
|
if (clickedNode.x > currObject.x && clickedNode.x < (currObject.x+currObject.w) && clickedNode.y > currObject.y && clickedNode.y < (currObject.y+currObject.h)) {
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
exports.makeRoundRect = function(ctx,xpos,ypos,wid,hgt,depth) {
|
|
var x1 = xpos;
|
|
var y1 = ypos;
|
|
var x2 = wid+x1;
|
|
var y2 = hgt+y1;
|
|
if (!depth) {
|
|
depth = 2;
|
|
}
|
|
|
|
ctx.beginPath();
|
|
ctx.moveTo(x1+depth, y1); //TOP LEFT
|
|
ctx.lineTo(x2-depth, y1); //TOP RIGHT
|
|
ctx.quadraticCurveTo(x2, y1, x2, y1+depth);
|
|
ctx.lineTo(x2, y2-depth); //BOTTOM RIGHT
|
|
ctx.quadraticCurveTo(x2, y2, x2-depth, y2);
|
|
ctx.lineTo(x1+depth, y2); //BOTTOM LEFT
|
|
ctx.quadraticCurveTo(x1, y2, x1, y2-depth);
|
|
ctx.lineTo(x1, y1+depth); //TOP LEFT
|
|
ctx.quadraticCurveTo(x1, y1, x1+depth, y1);
|
|
ctx.closePath();
|
|
}
|
|
|
|
exports.text = function(context, text, position) {
|
|
if (!position) {
|
|
position = [10 , 10];
|
|
}
|
|
with(context) {
|
|
beginPath();
|
|
font = "bold 12px sans-serif";
|
|
fillText(text,position[0],position[1]);
|
|
closePath();
|
|
}
|
|
}
|
|
},{"./math":6}],6:[function(require,module,exports){
|
|
|
|
|
|
/** @method toPolar
|
|
Receives cartesian coordinates and returns polar coordinates as an object with 'radius' and 'angle' properties.
|
|
@param {float} [x]
|
|
@param {float} [y]
|
|
```js
|
|
var ImOnACircle = nx.toPolar({ x: 20, y: 50 }})
|
|
```
|
|
*/
|
|
exports.toPolar = function(x,y) {
|
|
var r = Math.sqrt(x*x + y*y);
|
|
|
|
var theta = Math.atan2(y,x);
|
|
if (theta < 0.) {
|
|
theta = theta + (2 * Math.PI);
|
|
}
|
|
return {radius: r, angle: theta};
|
|
}
|
|
|
|
/** @method toCartesian
|
|
Receives polar coordinates and returns cartesian coordinates as an object with 'x' and 'y' properties.
|
|
@param {float} [radius]
|
|
@param {float} [angle]
|
|
*/
|
|
exports.toCartesian = function(radius, angle){
|
|
var cos = Math.cos(angle);
|
|
var sin = Math.sin(angle);
|
|
return {x: radius*cos, y: radius*sin*-1};
|
|
}
|
|
|
|
|
|
/** @method clip
|
|
Limits a number to within low and high values.
|
|
@param {float} [input value]
|
|
@param {float} [low limit]
|
|
@param {float} [high limit]
|
|
```js
|
|
nx.clip(5,0,10) // returns 5
|
|
nx.clip(15,0,10) // returns 10
|
|
nx.clip(-1,0,10) // returns 0
|
|
```
|
|
*/
|
|
exports.clip = function(value, low, high) {
|
|
return Math.min(high, Math.max(low, value));
|
|
}
|
|
|
|
/** @method prune
|
|
Limits a float to within a certain number of decimal places
|
|
@param {float} [input value]
|
|
@param {integer} [max decimal places]
|
|
```js
|
|
nx.prine(1.2345, 3) // returns 1.234
|
|
nx.prune(1.2345, 1) // returns 1.2
|
|
```
|
|
*/
|
|
|
|
exports.prune = function(data, scale) {
|
|
if (typeof data === "number") {
|
|
data = parseFloat(data.toFixed(scale));
|
|
} else if (data instanceof Array) {
|
|
for (var i=0;i<data.length;i++) {
|
|
if (typeof data[i]=="number") {
|
|
data[i] = parseFloat(data[i].toFixed(scale));
|
|
}
|
|
}
|
|
}
|
|
return data;
|
|
}
|
|
|
|
|
|
/** @method scale
|
|
Scales an input number to a new range of numbers
|
|
@param {float} [input value]
|
|
@param {float} [low1] input range (low)
|
|
@param {float} [high1] input range (high)
|
|
@param {float} [low2] output range (low)
|
|
@param {float} [high2] output range (high)
|
|
```js
|
|
nx.scale(5,0,10,0,100) // returns 50
|
|
nx.scale(5,0,10,1,2) // returns 1.5
|
|
```
|
|
*/
|
|
exports.scale = function(inNum, inMin, inMax, outMin, outMax) {
|
|
return (((inNum - inMin) * (outMax - outMin)) / (inMax - inMin)) + outMin;
|
|
}
|
|
|
|
/** @method invert
|
|
Equivalent to nx.scale(input,0,1,1,0). Inverts a normalized (0-1) number.
|
|
@param {float} [input value]
|
|
```js
|
|
nx.invert(0.25) // returns 0.75
|
|
nx.invert(0) // returns 1
|
|
```
|
|
*/
|
|
exports.invert = function (inNum) {
|
|
return exports.scale(inNum, 1, 0, 0, 1);
|
|
}
|
|
|
|
exports.bounce = function(posIn, borderMin, borderMax, delta) {
|
|
if (posIn > borderMin && posIn < borderMax) {
|
|
return delta;
|
|
} else if (posIn <= borderMin) {
|
|
return Math.abs(delta);
|
|
} else if (posIn >= borderMax) {
|
|
return Math.abs(delta) * (-1);
|
|
}
|
|
}
|
|
|
|
|
|
/** @method mtof
|
|
MIDI to frequency conversion. Returns frequency in Hz.
|
|
@param {float} [MIDI] MIDI value to convert
|
|
```js
|
|
nx.mtof(69) // returns 440
|
|
```
|
|
*/
|
|
exports.mtof = function(midi) {
|
|
return Math.pow(2, ((midi-69)/12)) * 440;
|
|
}
|
|
|
|
|
|
/** @method random
|
|
Returns a random integer between 0 a given scale parameter.
|
|
@param {float} [scale] Upper limit of random range.
|
|
```js
|
|
nx.random(10) // returns a random number from 0 to 9.
|
|
```
|
|
*/
|
|
exports.random = function(scale) {
|
|
return Math.floor(Math.random() * scale);
|
|
}
|
|
|
|
|
|
exports.interp = function(loc,min,max) {
|
|
return loc * (max - min) + min;
|
|
}
|
|
},{}],7:[function(require,module,exports){
|
|
|
|
|
|
exports.throttle = function(func, wait) {
|
|
var timeout;
|
|
return function() {
|
|
var context = this, args = arguments;
|
|
if (!timeout) {
|
|
// the first time the event fires, we setup a timer, which
|
|
// is used as a guard to block subsequent calls; once the
|
|
// timer's handler fires, we reset it and create a new one
|
|
timeout = setTimeout(function() {
|
|
timeout = null;
|
|
try {
|
|
func.apply(context, args);
|
|
} catch (err) {
|
|
console.log(err);
|
|
}
|
|
}, wait);
|
|
}
|
|
}
|
|
}
|
|
},{}],8:[function(require,module,exports){
|
|
exports.defineTransmit = function(protocol) {
|
|
|
|
var newTransmit;
|
|
|
|
if (typeof(protocol)=="function") {
|
|
return protocol;
|
|
} else {
|
|
switch (protocol) {
|
|
case 'js':
|
|
newTransmit = function(data) {
|
|
this.makeOSC(this.emit, data);
|
|
this.emit('*',data);
|
|
}
|
|
return newTransmit
|
|
|
|
case 'ajax':
|
|
newTransmit = function(data) {
|
|
this.makeOSC(exports.ajaxTransmit, data);
|
|
}
|
|
return newTransmit
|
|
|
|
case 'node':
|
|
newTransmit = function(data) {
|
|
this.makeOSC(exports.nodeTransmit, data);
|
|
}
|
|
return newTransmit
|
|
|
|
case 'ios':
|
|
newTransmit = function(data) {
|
|
|
|
}
|
|
return newTransmit
|
|
|
|
case 'max':
|
|
newTransmit = function(data) {
|
|
this.makeOSC(exports.maxTransmit, data);
|
|
}
|
|
return newTransmit
|
|
}
|
|
}
|
|
}
|
|
|
|
exports.setGlobalTransmit = function(protocol) {
|
|
var newTransmit = exports.defineTransmit(protocol)
|
|
this.transmit = newTransmit
|
|
this.destination = protocol
|
|
for (var key in nx.widgets) {
|
|
this.widgets[key].transmit = newTransmit;
|
|
this.widgets[key].destination = protocol;
|
|
}
|
|
}
|
|
|
|
exports.setWidgetTransmit = function(protocol) {
|
|
var newTransmit = exports.defineTransmit(protocol)
|
|
this.transmit = newTransmit
|
|
this.destination = protocol
|
|
}
|
|
|
|
|
|
exports.ajaxTransmit = function(subPath, data) {
|
|
|
|
var oscPath = subPath=='value' ? this.oscPath : this.oscPath+"/"+subPath;
|
|
|
|
xmlhttp=new XMLHttpRequest();
|
|
xmlhttp.open("POST",nx.ajaxPath,true);
|
|
xmlhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded");
|
|
xmlhttp.send('oscName='+oscPath+'&data='+data);
|
|
|
|
}
|
|
|
|
exports.setAjaxPath = function(path) {
|
|
this.ajaxPath = path;
|
|
}
|
|
|
|
exports.nodeTransmit = function(subPath, data) {
|
|
|
|
var msg = {
|
|
oscName: subPath=='value' ? this.oscPath : this.oscPath+"/"+subPath,
|
|
value: data
|
|
}
|
|
socket.emit('nx', msg)
|
|
|
|
}
|
|
|
|
exports.maxTransmit = function (subPath, data) {
|
|
var oscPath = subPath=='value' ? this.oscPath : this.oscPath+"/"+subPath;
|
|
window.max.outlet(oscPath + " " + data);
|
|
}
|
|
},{}],9:[function(require,module,exports){
|
|
var util = require('util');
|
|
var widget = require('../core/widget');
|
|
|
|
/**
|
|
@class banner
|
|
"Powered by NexusUI" tag with a link to our website. Use it if you want to share the positive vibes of NexusUI. Thanks for using!
|
|
```html
|
|
<canvas nx="banner"></canvas>
|
|
```
|
|
<canvas nx="banner" style="margin-left:25px"></canvas>
|
|
*/
|
|
|
|
var banner = module.exports = function (target) {
|
|
this.defaultSize = { width: 100, height: 40 };
|
|
widget.call(this, target);
|
|
|
|
//unique attributes
|
|
/** @property {string} message1 The first line of text on the banner. */
|
|
this.message1 = "Powered By";
|
|
/** @property {string} message2 The second line of text on the banner. */
|
|
this.message2 = "NexusUI";
|
|
/** @property {string} link The URL the banner will link to. */
|
|
this.link = "http://www.nexusosc.com";
|
|
/** @property {boolean} isLink Whether or not the banner is a hyperlink. Defaults to true. */
|
|
this.isLink = true;
|
|
}
|
|
util.inherits(banner, widget);
|
|
|
|
banner.prototype.init = function() {
|
|
this.draw();
|
|
}
|
|
|
|
banner.prototype.draw = function() {
|
|
with (this.context) {
|
|
|
|
globalAlpha = 0.1;
|
|
fillStyle = this.colors.accent;
|
|
beginPath();
|
|
moveTo(0,10);
|
|
lineTo(10,this.height/2+5);
|
|
lineTo(0,this.height);
|
|
lineTo(30,this.height);
|
|
lineTo(30,10);
|
|
fill();
|
|
moveTo(this.width-30,10);
|
|
lineTo(this.width-30,this.height);
|
|
lineTo(this.width,this.height);
|
|
lineTo(this.width-10,this.height/2+5);
|
|
lineTo(this.width,10);
|
|
fill();
|
|
closePath();
|
|
globalAlpha = 1;
|
|
|
|
fillStyle = this.colors.accent;
|
|
fillRect(15,0,this.width-30,this.height-10);
|
|
|
|
fillStyle = this.colors.white;
|
|
font = this.height/5+"px gill sans";
|
|
textAlign = "center";
|
|
fillText(this.message1, this.width/2, this.height/3.3);
|
|
fillText(this.message2, this.width/2, (this.height/3.3)*2);
|
|
|
|
fillStyle = this.colors.black;
|
|
beginPath();
|
|
moveTo(15,this.height-10);
|
|
lineTo(30,this.height);
|
|
lineTo(30,this.height-10);
|
|
lineTo(15,this.height-10);
|
|
fill();
|
|
moveTo(this.width-15,this.height-10);
|
|
lineTo(this.width-30,this.height);
|
|
lineTo(this.width-30,this.height-10);
|
|
lineTo(this.width-15,this.height-10);
|
|
fill();
|
|
closePath();
|
|
|
|
}
|
|
}
|
|
|
|
banner.prototype.click = function() {
|
|
if (this.isLink) {
|
|
window.location = this.link;
|
|
}
|
|
}
|
|
},{"../core/widget":3,"util":40}],10:[function(require,module,exports){
|
|
var util = require('util');
|
|
var widget = require('../core/widget');
|
|
|
|
var button = module.exports = function(target) {
|
|
|
|
/**
|
|
|
|
@public
|
|
@class button
|
|
|
|
Touch button with three modes of interaction ("toggle", "impulse", and "aftertouch").
|
|
```html
|
|
<canvas nx="button"></canvas>
|
|
```
|
|
<canvas nx="button" style="margin-left:25px"></canvas>
|
|
*/
|
|
|
|
this.defaultSize = { width: 50, height: 50 };
|
|
widget.call(this, target);
|
|
|
|
/**
|
|
@property {object} val Main value set and output, with sub-properties:
|
|
| | data
|
|
| --- | ---
|
|
| *press* | 0 (clicked) or 1 (unclicked)
|
|
| *x* | 0-1 float of x-position of click ("aftertouch" mode only)
|
|
| *y* | 0-1 float of y-position of click ("aftertouch" mode only)
|
|
|
|
When the widget is interacted with, val is sent as the output data for the widget.
|
|
```js
|
|
button1.on('*', function(data) {
|
|
// some code using data.press, data.x, and data.y
|
|
});
|
|
```
|
|
Or, if NexusUI is outputting OSC (e.g. if nx.sendsTo("ajax")), val will be broken into OSC messages:
|
|
```html
|
|
/button1/press 1
|
|
/button1/x 37
|
|
/button1/y 126
|
|
```
|
|
*/
|
|
this.val = {
|
|
press: 0
|
|
}
|
|
|
|
/** @property {string} mode Interaction mode. Options:
|
|
<b>impulse</b> 1 on click <br>
|
|
<b>toggle</b> 1 on click, 0 on release _(default)_<br>
|
|
<b>aftertouch</b> 1, x, y on click; x, y on move; 0, x, y on release <br>
|
|
```js
|
|
button1.mode = "aftertouch"
|
|
```
|
|
*/
|
|
this.mode = "toggle";
|
|
|
|
this.lockResize = true;
|
|
|
|
this.image = null;
|
|
this.imageHover = null;
|
|
this.imageTouch = null;
|
|
|
|
this.subval = new Object();
|
|
|
|
this.init();
|
|
|
|
}
|
|
util.inherits(button, widget);
|
|
|
|
button.prototype.init = function() {
|
|
this.width = this.canvas.width;
|
|
this.height = this.canvas.height;
|
|
this.draw();
|
|
}
|
|
|
|
button.prototype.draw = function() {
|
|
this.erase();
|
|
|
|
with (this.context) {
|
|
|
|
if (this.image !== null) {
|
|
// Image Button
|
|
if (!this.val.press) {
|
|
// Draw Image if not touched
|
|
drawImage(this.image, 0, 0);
|
|
} else {
|
|
if (!this.imageTouch) {
|
|
|
|
drawImage(this.image, 0, 0);
|
|
|
|
// No touch image, apply highlighting
|
|
globalAlpha = 0.5;
|
|
fillStyle = this.colors.accent;
|
|
fillRect (0, 0, this.width, this.height);
|
|
globalAlpha = 1;
|
|
|
|
} else {
|
|
// Draw Touch Image
|
|
drawImage(this.imageTouch, 0, 0);
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
// Regular Button
|
|
if (!this.val.press) {
|
|
fillStyle = this.colors.fill;
|
|
} else if (this.val.press) {
|
|
fillStyle = this.colors.accent;
|
|
}
|
|
|
|
beginPath();
|
|
arc(this.center.x, this.center.y, (Math.min(this.center.x, this.center.y)-this.lineWidth/2), 0, Math.PI*2, true);
|
|
fill();
|
|
closePath();
|
|
|
|
if (this.val.press && this.mode=="node") {
|
|
globalAlpha = 0.2;
|
|
fillStyle = this.colors.white;
|
|
beginPath();
|
|
arc(this.val.x, this.val.y, (Math.min(this.center.x, this.center.y)/2), 0, Math.PI*2, true);
|
|
fill();
|
|
closePath();
|
|
|
|
globalAlpha = 1;
|
|
}
|
|
}
|
|
|
|
this.drawLabel();
|
|
|
|
}
|
|
}
|
|
|
|
button.prototype.click = function(e) {
|
|
this.val["press"] = 1;
|
|
if (this.mode=="node") {
|
|
this.val["x"] = this.clickPos.x;
|
|
this.val["y"] = this.clickPos.y;
|
|
}
|
|
this.transmit(this.val);
|
|
this.draw();
|
|
}
|
|
|
|
button.prototype.move = function () {
|
|
// use to track movement on the button
|
|
if (this.mode=="node") {
|
|
this.val["x"] = this.clickPos.x;
|
|
this.val["y"] = this.clickPos.y;
|
|
this.subval["x"] = this.clickPos.x;
|
|
this.subval["y"] = this.clickPos.y;
|
|
this.transmit(this.subval);
|
|
this.draw();
|
|
}
|
|
}
|
|
|
|
button.prototype.release = function() {
|
|
this.val["press"] = 0;
|
|
if (this.mode=="toggle" || this.mode=="node") {
|
|
this.transmit(this.val);
|
|
}
|
|
this.draw();
|
|
}
|
|
|
|
|
|
/** @method setImage
|
|
Turns the button into an image button with custom image. Sets the default (unclicked) button image.
|
|
@param {string} [src] Image source */
|
|
button.prototype.setImage = function(image) {
|
|
this.image = new Image();
|
|
this.image.onload = function() { this.draw() }
|
|
this.image.src = image;
|
|
}
|
|
|
|
button.prototype.setHoverImage = function(image) {
|
|
this.imageHover = new Image();
|
|
this.imageHover.onload = function() { this.draw() }
|
|
this.imageHover.src = image;
|
|
}
|
|
|
|
/** @method setTouchImage
|
|
Sets the image that will show when the button is clicked.
|
|
@param {string} [src] Image source */
|
|
button.prototype.setTouchImage = function(image) {
|
|
this.imageTouch = new Image();
|
|
this.imageTouch.onload = this.draw();
|
|
this.imageTouch.src = image;
|
|
}
|
|
},{"../core/widget":3,"util":40}],11:[function(require,module,exports){
|
|
var util = require('util');
|
|
var widget = require('../core/widget');
|
|
|
|
/**
|
|
@class colors
|
|
Color picker that outputs RBG values
|
|
```html
|
|
<canvas nx="colors"></canvas>
|
|
```
|
|
<canvas nx="colors" style="margin-left:25px"></canvas>
|
|
*/
|
|
|
|
|
|
// this object is poor when it is resized
|
|
// because it calculates hsl based on
|
|
// hsl max values / width of object...
|
|
|
|
var colors = module.exports = function (target) {
|
|
|
|
this.defaultSize = { width: 100, height: 100 };
|
|
widget.call(this, target);
|
|
|
|
//define unique attributes
|
|
this.color_width = this.width - this.lineWidth*2;
|
|
this.color_height = this.height - this.lineWidth*2;
|
|
this.color_table = new Array();
|
|
/** @property {float} saturation Saturation percentage of the color picker (0-100)*/
|
|
this.saturation = 100;
|
|
this.color = [0,0,0];
|
|
|
|
this.init();
|
|
|
|
}
|
|
util.inherits(colors, widget);
|
|
|
|
colors.prototype.init = function() {
|
|
|
|
this.color_width = this.width - this.lineWidth*2;
|
|
this.color_height = this.height - this.lineWidth*2;
|
|
this.color_table = new Array();
|
|
this.color = [0,0,0];
|
|
|
|
//prep color picker
|
|
this.color_table = new Array(this.color_width);
|
|
for (var i=0;i<this.color_table.length;i++) {
|
|
this.color_table[i] = new Array(this.color_height);
|
|
}
|
|
|
|
|
|
for (var i=0;i<this.color_width;i++) {
|
|
h = Math.round((240/this.color_width)*i);
|
|
for (var j=0;j<this.color_height;j++) {
|
|
s = this.saturation;
|
|
l = Math.round((100/this.color_height)*j);
|
|
this.color_table[i][j] = [h, s, l];
|
|
}
|
|
}
|
|
this.draw();
|
|
}
|
|
|
|
colors.prototype.draw = function() {
|
|
this.erase();
|
|
for (var i=0;i<this.color_width;i++) {
|
|
for (var j=0;j<this.color_height;j++) {
|
|
hue = this.color_table[i][j][0];
|
|
sat = this.color_table[i][j][1];
|
|
lum = this.color_table[i][j][2];
|
|
with(this.context) {
|
|
beginPath();
|
|
fillStyle = 'hsl('+hue+', '+sat+'%, '+lum+'%)'
|
|
fillRect(i+this.lineWidth,j+this.lineWidth, 240/this.color_width, 240/this.color_height);
|
|
fill();
|
|
closePath();
|
|
}
|
|
}
|
|
}
|
|
|
|
this.drawLabel();
|
|
}
|
|
|
|
colors.prototype.drawColor = function() {
|
|
with(this.context) {
|
|
strokeStyle = "rgb("+this.val.r+","+this.val.g+","+this.val.b+")"
|
|
lineWidth = 4;
|
|
strokeRect(2,2,this.height-4,this.width-4)
|
|
}
|
|
}
|
|
|
|
colors.prototype.click = function(e) {
|
|
if (this.clickPos.x > 0 && this.clickPos.y > 0 && this.clickPos.x < this.width && this.clickPos.y < this.height) {
|
|
var imgData = this.context.getImageData(this.clickPos.x*2,this.clickPos.y*2,1,1);
|
|
} else {
|
|
return;
|
|
}
|
|
|
|
|
|
/** @property {object} val RGB color value at mouse position. <br> This is also the widget's data output (See <a href="#nexusui-api-widget-widgetval">widget.val</a>). <br> Properties:
|
|
| | data
|
|
| --- | ---
|
|
| *r* | red value 0-256
|
|
| *g* | green value 0-256
|
|
| *b* | blue value 0-256
|
|
```js
|
|
colors1.on('*', function(data) {
|
|
// some code using data.r, data.g, and data.b
|
|
}
|
|
```
|
|
*/
|
|
|
|
|
|
this.val = {
|
|
r: imgData.data[0],
|
|
g: imgData.data[1],
|
|
b: imgData.data[2]
|
|
}
|
|
this.transmit(this.val);
|
|
this.drawColor();
|
|
}
|
|
|
|
|
|
colors.prototype.move = function(e) {
|
|
this.click(e);
|
|
}
|
|
},{"../core/widget":3,"util":40}],12:[function(require,module,exports){
|
|
var util = require('util');
|
|
var widget = require('../core/widget');
|
|
|
|
/**
|
|
@class comment
|
|
Text comment
|
|
```html
|
|
<canvas nx="comment"></canvas>
|
|
```
|
|
<canvas nx="comment" style="margin-left:25px"></canvas>
|
|
*/
|
|
|
|
var comment = module.exports = function (target) {
|
|
|
|
this.defaultSize = { width: 75, height: 25 };
|
|
widget.call(this, target);
|
|
|
|
/** @property {object} val
|
|
| | data
|
|
| --- | ---
|
|
| *text* | text of comment area (as string)
|
|
```js
|
|
comment1.val.text = "This is my comment"
|
|
comment1.draw()
|
|
```
|
|
*/
|
|
|
|
this.val = {
|
|
text: "comment"
|
|
}
|
|
this.sizeSet = false;
|
|
|
|
this.init();
|
|
}
|
|
util.inherits(comment, widget);
|
|
|
|
/** @method setSize
|
|
Set the font size of the comment text
|
|
@param {integer} [size] Text size in pixels
|
|
*/
|
|
comment.prototype.setSize = function(size) {
|
|
this.size = size;
|
|
this.sizeSet = true;
|
|
this.draw();
|
|
}
|
|
|
|
comment.prototype.init = function() {
|
|
this.draw();
|
|
}
|
|
|
|
comment.prototype.draw = function() {
|
|
if (!this.sizeSet) {
|
|
this.size = Math.sqrt((this.width * this.height) / (this.val.text.length));
|
|
}
|
|
|
|
this.erase();
|
|
with (this.context) {
|
|
|
|
fillStyle = this.colors.fill;
|
|
fillRect(0,0,this.width,this.height);
|
|
|
|
beginPath();
|
|
moveTo(0,this.height);
|
|
lineTo(this.width,this.height);
|
|
closePath();
|
|
|
|
fillStyle = this.colors.black;
|
|
textAlign = "left";
|
|
font = this.size+"px Gill Sans";
|
|
}
|
|
this.wrapText(this.val.text, 6, 3+this.size, this.width-6, this.size);
|
|
}
|
|
},{"../core/widget":3,"util":40}],13:[function(require,module,exports){
|
|
var math = require('../utils/math');
|
|
var util = require('util');
|
|
var widget = require('../core/widget');
|
|
|
|
/**
|
|
@class dial
|
|
Circular dial
|
|
```html
|
|
<canvas nx="dial"></canvas>
|
|
```
|
|
<canvas nx="dial" style="margin-left:25px"></canvas>
|
|
*/
|
|
|
|
var dial = module.exports = function(target) {
|
|
|
|
this.defaultSize = { width: 50, height: 50 };
|
|
widget.call(this, target);
|
|
|
|
//define unique attributes
|
|
this.circleSize;
|
|
this.handleLength;
|
|
|
|
/** @property {object} val
|
|
| | data
|
|
| --- | ---
|
|
| *value* | Current value of dial as float 0-1
|
|
*/
|
|
this.val = {
|
|
value: 0
|
|
}
|
|
/** @property {float} responsivity How much the dial increments on drag. Default: 0.005<br>
|
|
*/
|
|
this.responsivity = 0.005;
|
|
|
|
this.aniStart = 0;
|
|
this.aniStop = 1;
|
|
this.aniMove = 0.01;
|
|
|
|
this.lockResize = true;
|
|
|
|
this.init();
|
|
|
|
}
|
|
|
|
util.inherits(dial, widget);
|
|
|
|
dial.prototype.init = function() {
|
|
|
|
this.circleSize = (Math.min(this.center.x, this.center.y)-this.lineWidth);
|
|
this.handleLength = this.circleSize+this.lineWidth;
|
|
|
|
if (this.width<101) {
|
|
this.handleLength--;
|
|
// this.handleLength--;
|
|
}
|
|
|
|
if (this.width<101 || this.width<101) {
|
|
this.accentWidth = this.lineWidth * 1;
|
|
} else {
|
|
this.accentWidth = this.lineWidth * 2;
|
|
}
|
|
|
|
this.draw();
|
|
|
|
return 1;
|
|
}
|
|
|
|
dial.prototype.draw = function() {
|
|
var dial_angle = (((1.0 - this.val.value) * 2 * Math.PI) + (1.5 * Math.PI));
|
|
var dial_position = (this.val.value + 0.25) * 2 * Math.PI
|
|
var point = math.toCartesian(this.handleLength, dial_angle);
|
|
|
|
this.erase();
|
|
|
|
with (this.context) {
|
|
|
|
fillStyle = this.colors.fill;
|
|
|
|
//draw main circle
|
|
beginPath();
|
|
arc(this.center.x, this.center.y, this.circleSize, 0, Math.PI*2, true);
|
|
fill();
|
|
closePath();
|
|
|
|
//draw color fill
|
|
beginPath();
|
|
lineWidth = this.accentWidth;
|
|
arc(this.center.x, this.center.y, this.circleSize , Math.PI* 0.5, dial_position, false);
|
|
lineTo(this.center.x,this.center.y);
|
|
globalAlpha = 0.1;
|
|
fillStyle = this.colors.accent;
|
|
fill();
|
|
globalAlpha = 1;
|
|
closePath();
|
|
|
|
//draw round accent
|
|
beginPath();
|
|
lineWidth = this.accentWidth;
|
|
arc(this.center.x, this.center.y, this.circleSize , Math.PI* 0.5, dial_position, false);
|
|
strokeStyle = this.colors.accent;
|
|
stroke();
|
|
closePath();
|
|
|
|
//draw bar accent
|
|
beginPath();
|
|
lineWidth = this.accentWidth;
|
|
strokeStyle = this.colors.accent;
|
|
moveTo(this.center.x, this.center.y);
|
|
lineTo(point.x + this.center.x, point.y + this.center.y);
|
|
stroke();
|
|
closePath();
|
|
|
|
//draw circle in center
|
|
beginPath();
|
|
fillStyle = this.colors.accent;
|
|
arc(this.center.x, this.center.y, this.circleSize/8, 0, Math.PI*2, false);
|
|
fill();
|
|
closePath();
|
|
|
|
}
|
|
|
|
this.drawLabel();
|
|
}
|
|
|
|
|
|
dial.prototype.click = function(e) {
|
|
this.val.value = math.prune(this.val.value, 3)
|
|
this.transmit(this.val);
|
|
this.draw();
|
|
this.aniStart = this.val.value;
|
|
}
|
|
|
|
|
|
dial.prototype.move = function() {
|
|
this.val.value = math.clip((this.val.value - (this.deltaMove.y * this.responsivity)), 0, 1);
|
|
this.val.value = math.prune(this.val.value, 3)
|
|
this.transmit(this.val);
|
|
|
|
this.draw();
|
|
}
|
|
|
|
|
|
dial.prototype.release = function() {
|
|
this.aniStop = this.val.value;
|
|
}
|
|
|
|
/** @method animate
|
|
Animates the dial
|
|
@param {string} [type] Type of animation. Currently accepts "bounce" (bounces between mousedown and mouserelease points) or "none" */
|
|
dial.prototype.animate = function(aniType) {
|
|
|
|
switch (aniType) {
|
|
case "bounce":
|
|
nx.aniItems.push(this.aniBounce.bind(this));
|
|
break;
|
|
case "none":
|
|
nx.aniItems.splice(nx.aniItems.indexOf(this.aniBounce));
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
dial.prototype.aniBounce = function() {
|
|
if (!this.clicked) {
|
|
this.val.value += this.aniMove;
|
|
if (this.aniStop < this.aniStart) {
|
|
this.stopPlaceholder = this.aniStop;
|
|
this.aniStop = this.aniStart;
|
|
this.aniStart = this.stopPlaceholder;
|
|
}
|
|
this.aniMove = math.bounce(this.val.value, this.aniStart, this.aniStop, this.aniMove);
|
|
this.draw();
|
|
this.val.value = math.prune(this.val.value, 3)
|
|
this.transmit(this.val);
|
|
}
|
|
}
|
|
|
|
|
|
},{"../core/widget":3,"../utils/math":6,"util":40}],14:[function(require,module,exports){
|
|
var math = require('../utils/math')
|
|
var util = require('util');
|
|
var widget = require('../core/widget');
|
|
|
|
/**
|
|
@class envelope
|
|
Three-point line ramp generator
|
|
```html
|
|
<canvas nx="envelope"></canvas>
|
|
```
|
|
<canvas nx="envelope" style="margin-left:25px"></canvas>
|
|
*/
|
|
|
|
var envelope = module.exports = function (target) {
|
|
|
|
this.defaultSize = { width: 75, height: 75 };
|
|
widget.call(this, target);
|
|
|
|
this.nodeSize = 0;
|
|
/** @property {boolean} active Whether or not the envelope is currently animating. */
|
|
this.active = false;
|
|
/** @property {integer} duration The envelope's duration in ms. */
|
|
this.duration = 1000; // 1000 ms
|
|
/** @property {boolean} looping Whether or not the envelope loops. */
|
|
this.looping = false
|
|
|
|
//define unique attributes
|
|
|
|
/** @property {object} val
|
|
| | data
|
|
| --- | ---
|
|
| *amp* | amplitude at current point of ramp (float 0-1)
|
|
| *index* | current progress through ramp (float 0-1)
|
|
| *x* | x of envelope peak point (float 0-1)
|
|
| *y* | y of envelope peak point (float 0-1)
|
|
*/
|
|
this.val = {
|
|
x: 0.15,
|
|
y: 0.5,
|
|
amp: 0,
|
|
index: 0
|
|
}
|
|
this.init();
|
|
|
|
}
|
|
|
|
util.inherits(envelope, widget);
|
|
|
|
envelope.prototype.init = function() {
|
|
this.actualWid = this.width- this.nodeSize*2;
|
|
this.actualHgt = this.height- this.nodeSize*2;
|
|
this.draw();
|
|
nx.aniItems.push(this.pulse.bind(this));
|
|
}
|
|
|
|
envelope.prototype.draw = function() {
|
|
this.erase();
|
|
with (this.context) {
|
|
fillStyle = this.colors.fill;
|
|
fillRect(0,0,this.width,this.height);
|
|
|
|
var drawingX = this.val.x * this.actualWid + this.nodeSize
|
|
var drawingY = this.val.y * this.actualHgt + this.nodeSize
|
|
|
|
//stay within right/left bounds
|
|
if (drawingX<(this.bgLeft+this.nodeSize)) {
|
|
drawingX = this.bgLeft + this.nodeSize;
|
|
} else if (drawingX>(this.bgRight-this.nodeSize)) {
|
|
drawingX = this.bgRight - this.nodeSize;
|
|
}
|
|
//stay within top/bottom bounds
|
|
if (drawingY<(this.bgTop+this.nodeSize)) {
|
|
drawingY = this.bgTop + this.nodeSize;
|
|
} else if (drawingY>(this.bgBottom-this.nodeSize)) {
|
|
drawingY = this.bgBottom - this.nodeSize;
|
|
}
|
|
|
|
with (this.context) {
|
|
beginPath();
|
|
strokeStyle = this.colors.accent;
|
|
moveTo(0,this.height);
|
|
lineTo(drawingX,drawingY);
|
|
lineTo(this.width,this.height);
|
|
stroke();
|
|
globalAlpha = 0.2;
|
|
fillStyle = this.colors.accent;
|
|
fill();
|
|
globalAlpha = 1;
|
|
closePath();
|
|
beginPath();
|
|
fillStyle = this.colors.accent;
|
|
strokeStyle = this.colors.border;
|
|
arc(drawingX, drawingY, this.nodeSize, 0, Math.PI*2, true);
|
|
fill();
|
|
closePath();
|
|
globalAlpha = 0.1
|
|
fillRect(0,0,this.val.index*this.width,this.height);
|
|
globalAlpha = 1;
|
|
}
|
|
}
|
|
|
|
this.drawLabel();
|
|
}
|
|
|
|
envelope.prototype.scaleNode = function() {
|
|
var actualX = this.val.x - this.nodeSize;
|
|
var actualY = this.val.y - this.nodeSize;
|
|
var clippedX = math.clip(actualX/this.actualWid, 0, 1);
|
|
var clippedY = math.clip(actualY/this.actualHgt, 0, 1);
|
|
this.val.x = math.prune(clippedX, 3)
|
|
this.val.y = math.prune(clippedY, 3)
|
|
}
|
|
|
|
envelope.prototype.click = function() {
|
|
this.val.x = this.clickPos.x;
|
|
this.val.y = this.clickPos.y;
|
|
this.scaleNode();
|
|
this.transmit(this.val);
|
|
this.draw();
|
|
}
|
|
|
|
envelope.prototype.move = function() {
|
|
if (this.clicked) {
|
|
this.val.x = this.clickPos.x;
|
|
this.val.y = this.clickPos.y;
|
|
this.scaleNode();
|
|
this.transmit(this.val);
|
|
this.draw();
|
|
}
|
|
}
|
|
|
|
envelope.prototype.release = function() {
|
|
this.val.x = this.clickPos.x;
|
|
this.val.y = this.clickPos.y;
|
|
this.scaleNode();
|
|
this.draw();
|
|
}
|
|
|
|
envelope.prototype.pulse = function() {
|
|
if (this.active) {
|
|
this.val.index += ((this.width/3.3)/this.duration);
|
|
this.val.index = math.clip(this.val.index, 0, 1)
|
|
|
|
if (this.val.index < this.val.x) {
|
|
var guiy = (this.val.index/this.val.x) * (1-this.val.y);
|
|
this.val.amp = math.clip(guiy, 0, 1)
|
|
} else {
|
|
var guiy = ((1-this.val.index)/(1-this.val.x)) * (1-this.val.y);
|
|
this.val.amp = math.clip(guiy, 0, 1)
|
|
}
|
|
|
|
this.transmit(this.val);
|
|
this.draw();
|
|
if (this.val.index >= 1) {
|
|
if (this.looping) {
|
|
this.val.index -= 1;
|
|
} else {
|
|
this.stop();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/** @method start
|
|
Start ramp from beginning. If set to loop, will loop the ramp until stopped. */
|
|
envelope.prototype.start = function() {
|
|
this.active = true;
|
|
this.val.index = 0;
|
|
}
|
|
|
|
/** @method stop
|
|
Stop the ramp and set progress to 0. */
|
|
envelope.prototype.stop = function() {
|
|
this.active = false;
|
|
this.val.index = 0;
|
|
this.draw();
|
|
}
|
|
},{"../core/widget":3,"../utils/math":6,"util":40}],15:[function(require,module,exports){
|
|
var math = require('../utils/math')
|
|
var util = require('util');
|
|
var widget = require('../core/widget');
|
|
|
|
/**
|
|
@class ghost (alpha)
|
|
|
|
```html
|
|
<canvas nx="ghost"></canvas>
|
|
```
|
|
<canvas nx="ghost" style="margin-left:25px"></canvas>
|
|
*/
|
|
|
|
var ghost = module.exports = function(target) {
|
|
|
|
this.defaultSize = { width: 100, height: 50 };
|
|
widget.call(this, target);
|
|
|
|
//define unique attributes
|
|
this.recording = false;
|
|
this.playing = false;
|
|
this.maxLength = 2000;
|
|
this.components = new Array();
|
|
this.buffer = new Array();
|
|
this.moment = 0;
|
|
this.val = new Object();
|
|
this.rate = 1;
|
|
this.start = 0;
|
|
this.end = 1;
|
|
this.size = 0;
|
|
this.looping = true;
|
|
this.boundLog = this.log.bind(this)
|
|
this.direction = 1;
|
|
this.init();
|
|
|
|
this.boundAdv = this.advance.bind(this);
|
|
nx.aniItems.push(this.boundAdv)
|
|
|
|
}
|
|
|
|
util.inherits(ghost, widget);
|
|
|
|
|
|
ghost.prototype.init = function() {
|
|
this.draw();
|
|
}
|
|
|
|
ghost.prototype.watch = function() {
|
|
for (var key in nx.widgets) {
|
|
this.connect(nx.widgets[key]);
|
|
}
|
|
}
|
|
|
|
//sets a new component to be recorded
|
|
ghost.prototype.connect = function(target) {
|
|
var compIndex = this.components.length;
|
|
this.components.push(target);
|
|
target.tapeNum = compIndex;
|
|
target.isRecording = true;
|
|
target.recorder = this;
|
|
this.buffer[compIndex] = new Object();
|
|
for (var key in target.val) {
|
|
this.buffer[compIndex][key] = new Array();
|
|
}
|
|
|
|
}
|
|
|
|
//the actual recording function
|
|
ghost.prototype.write = function(index, val) {
|
|
if (this.moment>=this.maxLength) {
|
|
this.stop();
|
|
}
|
|
for (var key in val) {
|
|
if (this.buffer[index][key]) {
|
|
this.buffer[index][key][this.moment] = val[key];
|
|
}
|
|
}
|
|
this.draw();
|
|
}
|
|
|
|
|
|
ghost.prototype.draw = function() {
|
|
|
|
with (this.context) {
|
|
fillStyle = this.colors.fill;
|
|
fillRect(0,0,this.width,this.height)
|
|
}
|
|
|
|
var quad = this.width/4;
|
|
var quad2 = this.width-quad;
|
|
|
|
if (!this.recording) {
|
|
with (this.context) {
|
|
fillStyle = "#e33";
|
|
beginPath()
|
|
arc(quad,this.height/2,quad*0.8,0,Math.PI*2)
|
|
fill()
|
|
closePath();
|
|
textAlign = "center"
|
|
textBaseline = "middle"
|
|
font = "normal "+this.height/6+"px courier"
|
|
fillStyle = this.colors.fill
|
|
fillText("rec",quad,this.height/2)
|
|
}
|
|
} else {
|
|
with (this.context) {
|
|
fillStyle = "#e33";
|
|
fillRect(quad*0.4,quad*0.4,quad*1.2,quad*1.2)
|
|
}
|
|
}
|
|
|
|
if (!this.playing) {
|
|
with (this.context) {
|
|
fillStyle = this.colors.border
|
|
beginPath()
|
|
arc(quad2,this.height/2,quad*0.8,0,Math.PI*2)
|
|
fill()
|
|
closePath()
|
|
textAlign = "center"
|
|
textBaseline = "middle"
|
|
font = "normal "+this.height/6+"px courier"
|
|
fillStyle = this.colors.fill
|
|
fillText("play",quad2,this.height/2)
|
|
}
|
|
} else {
|
|
with (this.context) {
|
|
strokeStyle = this.colors.border
|
|
lineWidth = this.width/30
|
|
beginPath()
|
|
arc(quad2,this.height/2,quad*0.8,0,Math.PI*2)
|
|
stroke()
|
|
closePath()
|
|
var sec = ~~(this.needle/30)
|
|
textAlign = "center"
|
|
textBaseline = "middle"
|
|
font = "normal "+this.height/3+"px courier"
|
|
fillStyle = this.colors.border
|
|
fillText(sec,quad2,this.height/2+2)
|
|
}
|
|
}
|
|
}
|
|
|
|
ghost.prototype.record = function() {
|
|
this.moment = 0;
|
|
nx.aniItems.push(this.boundLog)
|
|
this.recording = true;
|
|
}
|
|
|
|
ghost.prototype.log = function() {
|
|
for (var i=0;i<this.components.length;i++) {
|
|
var sender = this.components[i];
|
|
this.write(this.components[i].tapeNum,this.components[i].val);
|
|
}
|
|
this.moment++;
|
|
}
|
|
|
|
ghost.prototype.stop = function() {
|
|
nx.removeAni(this.boundLog);
|
|
this.size = this.moment;
|
|
this.recording = false;
|
|
this.draw();
|
|
}
|
|
|
|
ghost.prototype.scan = function(x) {
|
|
if (this.needle) {
|
|
for (var i=0;i<this.components.length;i++) {
|
|
var sender = this.components[i];
|
|
for (var key in this.buffer[sender.tapeNum]) {
|
|
if (this.buffer[sender.tapeNum][key]) {
|
|
var val = new Object();
|
|
var max = this.buffer[sender.tapeNum][key][~~this.needle+1] ? this.buffer[sender.tapeNum][key][~~this.needle+1] : this.buffer[sender.tapeNum][key][~~this.needle]
|
|
val[key] = nx.interp(this.needle - ~~this.needle, this.buffer[sender.tapeNum][key][~~this.needle], max)
|
|
if (this.buffer[sender.tapeNum][key][~~this.needle-this.direction] != undefined && this.buffer[sender.tapeNum][key][~~this.needle] != this.buffer[sender.tapeNum][key][~~this.needle-this.direction]) {
|
|
sender.set(val, true)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ghost.prototype.play = function(rate,start,end) {
|
|
rate ? this.rate = rate : null;
|
|
if (start) {
|
|
this.needle = this.moment-1;
|
|
this.start = start;
|
|
} else {
|
|
this.needle = this.moment-1;
|
|
this.start = 0;
|
|
}
|
|
end ? this.end = end : this.end = 1
|
|
this.playing = true;
|
|
}
|
|
|
|
ghost.prototype.pause = function() {
|
|
this.playing = false;
|
|
}
|
|
|
|
ghost.prototype.loop = function() {
|
|
|
|
}
|
|
|
|
ghost.prototype.advance = function() {
|
|
if (this.playing) {
|
|
this.needle += this.rate*this.direction;
|
|
if (this.needle/this.size < this.end && this.needle/this.size > this.start) {
|
|
this.scan();
|
|
} else if (this.looping) {
|
|
// this.needle = this.start;
|
|
this.direction = this.direction * -1
|
|
} else {
|
|
this.playing = false;
|
|
}
|
|
this.draw();
|
|
}
|
|
}
|
|
|
|
|
|
ghost.prototype.click = function(e) {
|
|
if (this.clickPos.x<this.width/2) {
|
|
if (this.recording) {
|
|
this.stop()
|
|
} else {
|
|
this.record()
|
|
}
|
|
} else {
|
|
if (this.playing) {
|
|
this.pause();
|
|
} else {
|
|
this.play();
|
|
}
|
|
this.draw();
|
|
}
|
|
}
|
|
|
|
|
|
ghost.prototype.move = function(e) {
|
|
}
|
|
},{"../core/widget":3,"../utils/math":6,"util":40}],16:[function(require,module,exports){
|
|
module.exports = {
|
|
banner: require('./banner'),
|
|
button: require('./button'),
|
|
colors: require('./colors'),
|
|
comment: require('./comment'),
|
|
dial: require('./dial'),
|
|
envelope: require('./envelope'),
|
|
ghost: require('./ghost'),
|
|
joints: require('./joints'),
|
|
keyboard: require('./keyboard'),
|
|
matrix: require('./matrix'),
|
|
message: require('./message'),
|
|
metro: require('./metro'),
|
|
mouse: require('./mouse'),
|
|
multislider: require('./multislider'),
|
|
multitouch: require('./multitouch'),
|
|
number: require('./number'),
|
|
position: require('./position'),
|
|
remix: require('./remix'),
|
|
range: require('./range'),
|
|
select: require('./select'),
|
|
slider: require('./slider'),
|
|
string: require('./string'),
|
|
tilt: require('./tilt'),
|
|
toggle: require('./toggle'),
|
|
typewriter: require('./typewriter'),
|
|
vinyl: require('./vinyl')
|
|
}
|
|
},{"./banner":9,"./button":10,"./colors":11,"./comment":12,"./dial":13,"./envelope":14,"./ghost":15,"./joints":17,"./keyboard":18,"./matrix":19,"./message":20,"./metro":21,"./mouse":22,"./multislider":23,"./multitouch":24,"./number":25,"./position":26,"./range":27,"./remix":28,"./select":29,"./slider":30,"./string":31,"./tilt":32,"./toggle":33,"./typewriter":34,"./vinyl":35}],17:[function(require,module,exports){
|
|
var math = require('../utils/math')
|
|
var util = require('util');
|
|
var widget = require('../core/widget');
|
|
|
|
/**
|
|
@class joints
|
|
2D slider with connections to several points; a proximity-based multislider.
|
|
```html
|
|
<canvas nx="joints"></canvas>
|
|
```
|
|
<canvas nx="joints" style="margin-left:25px"></canvas>
|
|
*/
|
|
|
|
var joints = module.exports = function (target) {
|
|
this.defaultSize = { width: 150, height: 150 };
|
|
widget.call(this, target);
|
|
|
|
/* @property {integer} nodeSize The size of the proximity points in pixels */
|
|
this.nodeSize = this.width/14;
|
|
this.values = [0,0];
|
|
|
|
/** @property {object} val
|
|
| | data
|
|
| --- | ---
|
|
| *x* | x position of touch/mouse
|
|
| *y* | y position of touch/mouse
|
|
| *node0* | nearness to first node if within range (float 0-1)
|
|
| *node1* | nearness to second node if within range (float 0-1)
|
|
| *node2* | nearness to third node if within range (float 0-1)
|
|
| etc... |
|
|
|
|
*/
|
|
this.val = {
|
|
x: 0.35,
|
|
y: 0.35,
|
|
node1: 0
|
|
}
|
|
/** @property {array} joints An array of objects with x and y properties detailing coordinates of each proximity node.
|
|
```js
|
|
// The widget will now have only 2 proximity points, instead of 8
|
|
joints1.joints = [
|
|
{ x: 20 , y: 100 },
|
|
{ x: 75 , y: 150 }
|
|
]
|
|
```
|
|
*/
|
|
this.joints = [
|
|
{ x: this.width/1.2 , y: this.height/1.2 },
|
|
{ x: this.width/2 , y: this.height/1.3 },
|
|
{ x: this.width/4.2 , y: this.height/1.1 },
|
|
|
|
{ x: this.width/1.4 , y: this.height/2.2 },
|
|
{ x: this.width/2.1 , y: this.height/1.8 },
|
|
{ x: this.width/5 , y: this.height/2.4 },
|
|
|
|
{ x: this.width/2.8 , y: this.height/6 },
|
|
{ x: this.width/6 , y: this.height/3.7 }
|
|
|
|
]
|
|
this.threshold = this.width / 3;
|
|
}
|
|
util.inherits(joints, widget);
|
|
|
|
joints.prototype.init = function() {
|
|
this.draw();
|
|
}
|
|
|
|
joints.prototype.draw = function() {
|
|
this.erase();
|
|
|
|
this.drawingX = this.val.x * this.width;
|
|
this.drawingY = this.val.y * this.height;
|
|
|
|
with (this.context) {
|
|
fillStyle = this.colors.fill;
|
|
fillRect(0,0,this.width,this.height);
|
|
if (this.val.x != null) {
|
|
this.drawNode();
|
|
}
|
|
else {
|
|
fillStyle = this.colors.border;
|
|
font = "14px courier";
|
|
fillText(this.default_text, 10, 20);
|
|
}
|
|
fillStyle = this.colors.accent;
|
|
strokeStyle = this.colors.border;
|
|
for (var i in this.joints) {
|
|
beginPath();
|
|
arc(this.joints[i].x, this.joints[i].y, this.nodeSize/2, 0, Math.PI*2, true);
|
|
fill();
|
|
closePath();
|
|
var cnctX = Math.abs(this.joints[i].x-this.drawingX);
|
|
var cnctY = Math.abs(this.joints[i].y-this.drawingY);
|
|
var strength = cnctX + cnctY;
|
|
if (strength < this.threshold) {
|
|
beginPath();
|
|
moveTo(this.joints[i].x, this.joints[i].y);
|
|
lineTo(this.drawingX,this.drawingY);
|
|
strokeStyle = this.colors.accent;
|
|
lineWidth = math.scale( strength, 0, this.threshold, this.nodeSize/2, 5 );
|
|
stroke();
|
|
closePath();
|
|
var scaledstrength = math.scale( strength, 0, this.threshold, 1, 0 );
|
|
this.val["node"+i] = scaledstrength;
|
|
}
|
|
}
|
|
}
|
|
|
|
this.drawLabel();
|
|
}
|
|
|
|
joints.prototype.drawNode = function() {
|
|
//stay within right/left bounds
|
|
if (this.drawingX<(this.nodeSize)) {
|
|
this.drawingX = this.nodeSize;
|
|
} else if (this.drawingX>(this.width-this.nodeSize)) {
|
|
this.drawingX = this.width - this.nodeSize;
|
|
}
|
|
//stay within top/bottom bounds
|
|
if (this.drawingY < this.nodeSize) {
|
|
this.drawingY = this.nodeSize;
|
|
} else if (this.drawingY>(this.height-this.nodeSize)) {
|
|
this.drawingY = this.height - this.nodeSize;
|
|
}
|
|
|
|
with (this.context) {
|
|
globalAlpha=1;
|
|
beginPath();
|
|
fillStyle = this.colors.accent;
|
|
strokeStyle = this.colors.border;
|
|
lineWidth = this.lineWidth;
|
|
arc(this.drawingX, this.drawingY, this.nodeSize, 0, Math.PI*2, true);
|
|
fill();
|
|
closePath();
|
|
}
|
|
}
|
|
|
|
joints.prototype.click = function() {
|
|
this.val = new Object();
|
|
this.val.x = this.clickPos.x/this.width;
|
|
this.val.y = this.clickPos.y/this.height;
|
|
this.draw();
|
|
this.transmit(this.val);
|
|
this.connections = new Array();
|
|
|
|
}
|
|
|
|
joints.prototype.move = function() {
|
|
this.val = new Object();
|
|
if (this.clicked) {
|
|
this.val.x = this.clickPos.x/this.width;
|
|
this.val.y = this.clickPos.y/this.height;
|
|
this.draw();
|
|
this.transmit(this.val);
|
|
this.connections = new Array();
|
|
}
|
|
}
|
|
|
|
|
|
joints.prototype.release = function() {
|
|
this.anix = this.deltaMove.x/this.width;
|
|
this.aniy = (this.deltaMove.y)/this.height;
|
|
|
|
}
|
|
|
|
/** @method animate
|
|
Add simple physics to the widget
|
|
@param {string} [type] Currently accepts "bounce" or "none".
|
|
*/
|
|
|
|
joints.prototype.animate = function(aniType) {
|
|
|
|
switch (aniType) {
|
|
case "bounce":
|
|
nx.aniItems.push(this.aniBounce.bind(this));
|
|
break;
|
|
case "none":
|
|
nx.aniItems.splice(nx.aniItems.indexOf(this.aniBounce));
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
joints.prototype.anix = 0;
|
|
joints.prototype.aniy = 0;
|
|
|
|
joints.prototype.aniBounce = function() {
|
|
if (!this.clicked && this.val.x) {
|
|
this.val.x += (this.anix);
|
|
this.val.y += (this.aniy);
|
|
this.anix = math.bounce(this.val.x, 0.1, 0.9, this.anix);
|
|
this.aniy = math.bounce(this.val.y, 0.1, 0.9, this.aniy);
|
|
this.draw();
|
|
this.transmit(this.val);
|
|
}
|
|
}
|
|
|
|
},{"../core/widget":3,"../utils/math":6,"util":40}],18:[function(require,module,exports){
|
|
var util = require('util');
|
|
var widget = require('../core/widget');
|
|
var drawing = require('../utils/drawing');
|
|
var math = require('../utils/math');
|
|
|
|
/**
|
|
@class keyboard
|
|
Piano keyboard which outputs MIDI
|
|
```html
|
|
<canvas nx="keyboard"></canvas>
|
|
```
|
|
<canvas nx="keyboard" style="margin-left:25px"></canvas>
|
|
*/
|
|
|
|
var keyboard = module.exports = function (target) {
|
|
|
|
this.defaultSize = { width: 300, height: 75 };
|
|
widget.call(this, target);
|
|
|
|
/** @property {integer} octaves Number of octaves on the keyboard */
|
|
this.octaves = 3;
|
|
this.white = {
|
|
width:0,
|
|
height:0
|
|
}
|
|
this.black = {
|
|
width:0,
|
|
height:0
|
|
}
|
|
this.wkeys = new Array();
|
|
this.bkeys = new Array();
|
|
|
|
/** @property {array} keypattern Array of 'w' and 'b' denoting the pattern of white and black keys. This can be customized! The pattern can be any number of keys, however each black key must be surrounded by two white keys.
|
|
```js
|
|
//This key pattern would put a black key between every white key
|
|
keyboard1.keypattern = ['w','b','w','b','w','b','w','b','w','b','w','b']
|
|
keyboard1.init()
|
|
|
|
//This key pattern uses only white keys
|
|
keyboard2.keypattern = ['w','w','w','w','w','w','w','w','w','w','w','w']
|
|
keyboard2.init()
|
|
```
|
|
|
|
|
|
*/
|
|
this.keypattern = ['w','b','w','b','w','w','b','w','b','w','b','w']
|
|
this.keys = new Array();
|
|
/** @property {integer} midibase The MIDI note value of the lowest note on the keyboard. Defaults to 48. */
|
|
this.midibase = 48;
|
|
this.lineWidth = 1;
|
|
|
|
//to enable multitouch
|
|
this.fingers = [
|
|
{
|
|
key: -1,
|
|
pkey: -1
|
|
|
|
}
|
|
]
|
|
this.multitouch = false; // will auto switch to true if experiences 2 simultaneous touches
|
|
this.oneleft = false;
|
|
|
|
/** @property {string} mode Play mode. Currently accepts "button" (default) or "sustain" in which each key acts as a toggle. */
|
|
this.mode = "button" // modes: "button", "sustain" and, possibly in future, "aftertouch"
|
|
|
|
// for each key: x, y, w, h, color, on, note
|
|
|
|
/** @property {object} val Core interactive values and data output
|
|
| | data
|
|
| --- | ---
|
|
| *on* | 0 if noteon, 1 if noteoff
|
|
| *note* | MIDI value of key pressed
|
|
| *midi* | paired MIDI message as a string - example "20 0" - This is to allow for simultaneous arrival of the MIDI pair if sent as an OSC message.
|
|
*/
|
|
this.val = {
|
|
on: 0,
|
|
note: 0,
|
|
midi: "0 0"
|
|
};
|
|
|
|
this.init();
|
|
|
|
}
|
|
util.inherits(keyboard, widget);
|
|
|
|
keyboard.prototype.init = function() {
|
|
|
|
//recap from header
|
|
this.white = {
|
|
width:0,
|
|
height:0
|
|
}
|
|
this.black = {
|
|
width:0,
|
|
height:0
|
|
}
|
|
this.wkeys = new Array();
|
|
this.bkeys = new Array();
|
|
|
|
/** @property {array} keys Array of key objects. This may be of use in combination with the keyboard.toggle method. */
|
|
this.keys = new Array();
|
|
|
|
//new stuff
|
|
this.white.num = 0;
|
|
for (var i=0;i<this.keypattern.length;i++) {
|
|
this.keypattern[i]=='w' ? this.white.num++ : null;
|
|
}
|
|
this.white.num *= this.octaves;
|
|
|
|
this.white.width = this.width/this.white.num
|
|
this.white.height = this.height
|
|
|
|
this.black.width = this.white.width*0.6
|
|
this.black.height = this.height*0.6
|
|
|
|
for (var i=0;i<this.keypattern.length*this.octaves;i++) {
|
|
this.keys[i] = {
|
|
note: i+this.midibase,
|
|
on: false
|
|
}
|
|
switch (this.keypattern[i%this.keypattern.length]) {
|
|
case 'w':
|
|
this.keys[i].x = this.wkeys.length*this.white.width,
|
|
this.keys[i].y = 0,
|
|
this.keys[i].w = this.white.width,
|
|
this.keys[i].h = this.white.height,
|
|
this.keys[i].type = 'w';
|
|
this.keys[i].index = i;
|
|
this.wkeys.push(this.keys[i]);
|
|
|
|
break;
|
|
case 'b':
|
|
this.keys[i].x = this.wkeys.length*this.white.width - this.black.width/2,
|
|
this.keys[i].y = 0,
|
|
this.keys[i].w = this.black.width,
|
|
this.keys[i].h = this.black.height,
|
|
this.keys[i].type = 'b';
|
|
this.keys[i].index = i;
|
|
this.bkeys.push(this.keys[i]);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
this.draw();
|
|
}
|
|
|
|
keyboard.prototype.draw = function() {
|
|
|
|
with (this.context) {
|
|
strokeStyle = this.colors.border;
|
|
lineWidth = 1;
|
|
|
|
for (var i in this.wkeys) {
|
|
fillStyle = this.wkeys[i].on ? this.colors.border : this.colors.fill
|
|
strokeRect(this.wkeys[i].x,0,this.white.width,this.white.height);
|
|
fillRect(this.wkeys[i].x,0,this.white.width,this.white.height);
|
|
}
|
|
for (var i in this.bkeys) {
|
|
fillStyle = this.bkeys[i].on ? this.colors.border : this.colors.black
|
|
fillRect(this.bkeys[i].x,0,this.black.width,this.black.height);
|
|
}
|
|
//strokeRect(0,0,this.width,this.height);
|
|
}
|
|
this.drawLabel();
|
|
}
|
|
|
|
/** @method toggle
|
|
Manually toggle a key on or off, and transmit the new state.
|
|
@param {object} [key] A key object (from the .keys array) to be turned on or off
|
|
@param {boolean} [on/off] (Optional) Whether the key should be turned on (true) or off (false). If this parameter is left out, the key will switch to its opposite state.
|
|
```js
|
|
// Turns the first key on
|
|
keyboard1.toggle( keyboard1.keys[0], true );
|
|
```
|
|
*/
|
|
keyboard.prototype.toggle = function(key, data) {
|
|
if (this.mode=="button") {
|
|
if (key) {
|
|
if (data) {
|
|
key.on = data;
|
|
} else {
|
|
key.on = !key.on;
|
|
}
|
|
|
|
var on = key.on ? 1 : 0;
|
|
var amp = math.invert(this.clickPos.y/this.height) * 128;
|
|
amp = math.prune(math.clip(amp,5,128),0);
|
|
|
|
this.val = {
|
|
on: on*amp,
|
|
note: key.note,
|
|
midi: key.note + " " + on
|
|
};
|
|
this.transmit(this.val);
|
|
this.draw();
|
|
}
|
|
} else if (this.mode=="sustain") {
|
|
if (key) {
|
|
if (data) {
|
|
key.on = data;
|
|
} else {
|
|
key.on = !key.on;
|
|
}
|
|
|
|
var on = key.on ? 1 : 0;
|
|
var amp = math.invert(this.clickPos.y/this.height) * 128;
|
|
amp = math.prune(math.clip(amp,5,128),0);
|
|
|
|
this.val = {
|
|
on: on*amp,
|
|
note: key.note,
|
|
midi: key.note + " " + on
|
|
};
|
|
this.transmit(this.val);
|
|
this.draw();
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
keyboard.prototype.whichKey = function (x, y){
|
|
|
|
for (var i in this.bkeys) {
|
|
if (drawing.isInside({"x":x,"y":y}, this.bkeys[i])) {
|
|
return this.bkeys[i]
|
|
}
|
|
}
|
|
|
|
var keyx = ~~(x/this.white.width);
|
|
if (keyx>=this.wkeys.length) { keyx = this.wkeys.length-1 }
|
|
if (keyx<0) { keyx = 0 }
|
|
return this.wkeys[keyx];
|
|
}
|
|
|
|
keyboard.prototype.click = function(e) {
|
|
if (this.clickPos.touches.length>1 || this.multitouch) {
|
|
if (this.clickPos.touches.length>=2 && this.oneleft) {
|
|
this.oneleft = false;
|
|
}
|
|
for (var j=0;j<this.clickPos.touches.length;j++) {
|
|
this.multitouch = true;
|
|
this.fingers[j] = {
|
|
key: this.whichKey(this.clickPos.touches[j].x, this.clickPos.touches[j].y)
|
|
}
|
|
if (!this.fingers[j].key.on) {
|
|
this.fingers[j].key.on = true;
|
|
}
|
|
}
|
|
} else {
|
|
this.fingers[0].pkey = this.fingers[0].key;
|
|
this.fingers[0].key = this.whichKey(this.clickPos.x, this.clickPos.y);
|
|
this.toggle(this.fingers[0].key)
|
|
}
|
|
}
|
|
|
|
keyboard.prototype.move = function(e) {
|
|
var debug = document.getElementById("debug");
|
|
if (this.clickPos.touches.length>1 || this.multitouch) {
|
|
this.keysinuse = new Array();
|
|
for (var j=0;j<this.clickPos.touches.length;j++) {
|
|
this.fingers[j] = {
|
|
key: this.whichKey(this.clickPos.touches[j].x, this.clickPos.touches[j].y)
|
|
}
|
|
if (!this.fingers[j].key.on) {
|
|
this.toggle(this.fingers[j].key, true)
|
|
}
|
|
this.keysinuse.push(this.fingers[j].key.index)
|
|
}
|
|
for (var j=0;j<this.keys.length;j++) {
|
|
if (this.keys[j].on && this.keysinuse.indexOf(this.keys[j].index)<0) {
|
|
this.toggle(this.keys[j], false);
|
|
}
|
|
}
|
|
} else {
|
|
this.fingers[0].pkey = this.fingers[0].key;
|
|
this.fingers[0].key = this.whichKey(this.clickPos.x, this.clickPos.y);
|
|
if (this.fingers[0].key && this.fingers[0].key.index != this.fingers[0].pkey.index) {
|
|
this.toggle(this.fingers[0].key, true);
|
|
this.toggle(this.fingers[0].pkey, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
keyboard.prototype.release = function(e) {
|
|
if (this.clickPos.touches.length>1 || this.multitouch) {
|
|
this.keysinuse = new Array();
|
|
for (var j=0;j<this.clickPos.touches.length;j++) {
|
|
if (this.oneleft && this.clickPos.touches.length==1) {
|
|
break;
|
|
}
|
|
this.fingers[j] = {
|
|
key: this.whichKey(this.clickPos.touches[j].x, this.clickPos.touches[j].y)
|
|
}
|
|
this.keysinuse.push(this.fingers[j].key.index)
|
|
}
|
|
for (var j=0;j<this.keys.length;j++) {
|
|
if (this.keys[j].on && this.keysinuse.indexOf(this.keys[j].index)<0) {
|
|
this.toggle(this.keys[j], false);
|
|
}
|
|
}
|
|
if (this.clickPos.touches.length==1) { this.oneleft = true }
|
|
} else {
|
|
if (this.mode=="button") {
|
|
this.toggle(this.fingers[0].key, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
},{"../core/widget":3,"../utils/drawing":5,"../utils/math":6,"util":40}],19:[function(require,module,exports){
|
|
var math = require('../utils/math');
|
|
var drawing = require('../utils/drawing');
|
|
var util = require('util');
|
|
var widget = require('../core/widget');
|
|
|
|
/**
|
|
@class matrix
|
|
Matrix of toggles, with sequencer functionality.
|
|
```html
|
|
<canvas nx="matrix"></canvas>
|
|
```
|
|
<canvas nx="matrix" style="margin-left:25px"></canvas>
|
|
*/
|
|
|
|
|
|
var matrix = module.exports = function (target) {
|
|
this.defaultSize = { width: 100, height: 100 };
|
|
widget.call(this, target);
|
|
|
|
|
|
/** @property {integer} row Number of rows in the matrix
|
|
```js
|
|
matrix1.row = 2;
|
|
matrix1.init()
|
|
```
|
|
*/
|
|
this.row = 4;
|
|
|
|
/** @property {integer} col Number of columns in the matrix
|
|
```js
|
|
matrix1.col = 10;
|
|
matrix1.init()
|
|
```
|
|
*/
|
|
this.col = 4;
|
|
|
|
this.cellHgt;
|
|
this.cellWid;
|
|
|
|
/** @property {array} matrix Nested array of matrix values. Cells can be manually altered using .matrix (see code), however this will *not* cause the new value to be transmit. See .setCell() to set/transmit cell values.
|
|
```js
|
|
//Turn on the cell at row 1 column 2
|
|
matrix1.matrix[1][2] = 1
|
|
matrix1.draw()
|
|
|
|
|
|
//Turn off the cell at row 3 column 0
|
|
matrix1.matrix[3][0] = 0
|
|
matrix1.draw()
|
|
```
|
|
*/
|
|
this.matrix;
|
|
|
|
/** @property {object} val Core values and data output
|
|
| | data
|
|
| --- | ---
|
|
| *row* | Current row being changed
|
|
| *col* | Current column being changed
|
|
| *value* | New value of matrix point (0-1 float)
|
|
*/
|
|
this.val = {
|
|
row: 0,
|
|
col: 0,
|
|
level: 0,
|
|
list: new Array()
|
|
}
|
|
|
|
//for mouse logic
|
|
this.cur;
|
|
this.prev;
|
|
|
|
/** @property {boolean} erasing Whether or not mouse clicks will erase cells. Set to true automatically if you click on an "on" cell. */
|
|
this.erasing = false;
|
|
|
|
/** @property {integer} place When sequencing, the current column. */
|
|
this.place = null;
|
|
|
|
this.starttime;
|
|
this.thisframe = 0;
|
|
this.lastframe = 0;
|
|
this.context.lineWidth = 1;
|
|
|
|
this.sequencing = false;
|
|
|
|
/** @property {string} sequenceMode Sequence pattern (currently accepts "linear" which is default, or "random") */
|
|
this.sequenceMode = "linear"; // "linear" or "random". future options would be "wander" (drunk) or "markov"
|
|
|
|
/** @property {integer} bpm Beats per minute (if sequencing)
|
|
```js
|
|
matrix1.bpm = 120;
|
|
```
|
|
*/
|
|
this.bpm = 120;
|
|
this.init();
|
|
|
|
}
|
|
util.inherits(matrix, widget);
|
|
|
|
|
|
|
|
matrix.prototype.init = function() {
|
|
|
|
this.lineWidth = 1;
|
|
|
|
this.matrix = null;
|
|
// generate 2D matrix array
|
|
this.matrix = new Array(this.col)
|
|
for (var i=0;i<this.col;i++) {
|
|
this.matrix[i] = new Array(this.row)
|
|
for (var j=0;j<this.row;j++) {
|
|
this.matrix[i][j] = 0; // set value of each matrix cell
|
|
}
|
|
}
|
|
|
|
|
|
this.draw();
|
|
|
|
}
|
|
|
|
matrix.prototype.draw = function() {
|
|
|
|
this.cellWid = this.canvas.width/this.col;
|
|
this.cellHgt = this.canvas.height/this.row;
|
|
|
|
for (var i=0;i<this.row;i++){
|
|
for (var j=0;j<this.col;j++) {
|
|
var st_x = j*this.cellWid // starting point(left)
|
|
j==0 ? st_x += 0 : null;
|
|
var st_y = i*this.cellHgt; // starting point(top)
|
|
i==0 ? st_y += 0 : null;
|
|
var boxwid = this.cellWid;
|
|
var boxhgt = this.cellHgt;
|
|
|
|
|
|
with (this.context) {
|
|
strokeStyle = this.colors.border;
|
|
if (this.matrix[j][i] > 0) {
|
|
fillStyle = this.colors.accent;
|
|
} else {
|
|
fillStyle = this.colors.fill;
|
|
}
|
|
fillRect(st_x, st_y, boxwid, boxhgt);
|
|
strokeRect(st_x, st_y, boxwid, boxhgt);
|
|
|
|
// sequencer highlight
|
|
if (this.place == j) {
|
|
globalAlpha = 0.4;
|
|
fillStyle = this.colors.border;
|
|
fillRect(st_x, st_y, boxwid, boxhgt);
|
|
globalAlpha = 1;
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
this.drawLabel();
|
|
}
|
|
|
|
|
|
|
|
matrix.prototype.click = function(e) {
|
|
|
|
this.cur = {
|
|
col: ~~(this.clickPos.x/this.cellWid),
|
|
row: ~~(this.clickPos.y/this.cellHgt)
|
|
}
|
|
|
|
if (this.matrix[this.cur.col][this.cur.row]) {
|
|
this.matrix[this.cur.col][this.cur.row] = 0;
|
|
this.erasing = true;
|
|
} else {
|
|
this.matrix[this.cur.col][this.cur.row] = 1;
|
|
this.erasing = false;
|
|
}
|
|
|
|
this.cur.value = this.matrix[this.cur.col][this.cur.row]
|
|
this.prev = this.cur;
|
|
|
|
// var data = this.matrix[this.cur.col];
|
|
// data = data.join();
|
|
// data = data.replace(/\,/g," ");
|
|
|
|
this.val = {
|
|
row: this.cur.row,
|
|
col: this.cur.col,
|
|
level: this.cur.value
|
|
}
|
|
|
|
this.transmit(this.val);
|
|
this.draw();
|
|
}
|
|
|
|
matrix.prototype.move = function(e) {
|
|
if (this.clicked) {
|
|
|
|
this.cur = {
|
|
col: ~~(this.clickPos.x/this.cellWid),
|
|
row: ~~(this.clickPos.y/this.cellHgt)
|
|
}
|
|
|
|
if (this.cur.row < this.row && this.cur.col < this.col && this.cur.row >= 0 && this.cur.col >=0) {
|
|
if (this.cur.col!=this.prev.col || this.cur.row != this.prev.row) {
|
|
if (this.erasing) {
|
|
this.matrix[this.cur.col][this.cur.row] = 0;
|
|
} else {
|
|
this.matrix[this.cur.col][this.cur.row] = 1;
|
|
}
|
|
|
|
this.cur.value = this.matrix[this.cur.col][this.cur.row]
|
|
this.prev = this.cur;
|
|
|
|
this.val = {
|
|
row: this.cur.row,
|
|
col: this.cur.col,
|
|
level: this.cur.value
|
|
}
|
|
|
|
this.transmit(this.val);
|
|
this.draw();
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
|
|
/** @method setCell
|
|
Manually set an individual cell on/off and transmit the new value.
|
|
@param {integer} [col] The column of the cell to be turned on/off
|
|
@param {integer} [row] The row of the cell to be turned on/off
|
|
@param {boolean} [on/off] Whether the cell should be turned on/off
|
|
|
|
```js
|
|
// Turns cell on at column 1 row 3
|
|
matrix1.setCell(1,3,true);
|
|
```
|
|
*/
|
|
matrix.prototype.setCell = function(col,row,on) {
|
|
|
|
var value = on ? 1 : 0;
|
|
this.matrix[col][row] = value
|
|
|
|
this.val = {
|
|
row: row,
|
|
col: col,
|
|
level: value
|
|
}
|
|
|
|
this.transmit(this.val);
|
|
this.draw();
|
|
|
|
}
|
|
|
|
/** @method sequence
|
|
@param {float} [bpm] Beats per minute of the pulse
|
|
Turns the matrix into a sequencer.
|
|
|
|
```js
|
|
matrix1.sequence(240);
|
|
```
|
|
*/
|
|
matrix.prototype.sequence = function(bpm) {
|
|
|
|
if (bpm) {
|
|
this.bpm = bpm;
|
|
}
|
|
this.sequencing = true;
|
|
requestAnimationFrame(this.seqStep.bind(this));
|
|
|
|
}
|
|
|
|
/** @method stop
|
|
Stops the matrix sequencer.
|
|
|
|
```js
|
|
matrix1.stop();
|
|
```
|
|
*/
|
|
matrix.prototype.stop = function() {
|
|
this.sequencing = false;
|
|
}
|
|
|
|
matrix.prototype.seqStep = function() {
|
|
|
|
var now = new Date().getTime();
|
|
var dt = now - nx.starttime;
|
|
|
|
this.thisframe = ~~(dt/(60000/this.bpm));
|
|
|
|
if (this.thisframe != this.lastframe) {
|
|
|
|
if (this.sequenceMode=="linear") {
|
|
this.place++;
|
|
} else if (this.sequenceMode=="random") {
|
|
this.place = math.random(this.col);
|
|
}
|
|
if (this.place>=this.col) {
|
|
this.place = 0;
|
|
}
|
|
|
|
if (this.place==null) {
|
|
this.place = 0;
|
|
}
|
|
this.draw();
|
|
|
|
// var data = this.matrix[this.place]
|
|
// data = data.join();
|
|
// data = data.replace(/\,/g," ");
|
|
this.val.list = this.matrix[this.place];
|
|
|
|
this.transmit(this.val);
|
|
|
|
}
|
|
|
|
this.lastframe = this.thisframe;
|
|
if (this.sequencing) {
|
|
requestAnimationFrame(this.seqStep.bind(this));
|
|
}
|
|
}
|
|
|
|
|
|
matrix.prototype.customDestroy = function() {
|
|
this.stop();
|
|
}
|
|
|
|
},{"../core/widget":3,"../utils/drawing":5,"../utils/math":6,"util":40}],20:[function(require,module,exports){
|
|
var util = require('util');
|
|
var widget = require('../core/widget');
|
|
|
|
/**
|
|
@class message
|
|
Send a string of text.
|
|
```html
|
|
<canvas nx="message"></canvas>
|
|
```
|
|
<canvas nx="message" style="margin-left:25px"></canvas>
|
|
*/
|
|
|
|
var message = module.exports = function (target) {
|
|
|
|
this.defaultSize = { width: 100, height: 30 };
|
|
widget.call(this, target);
|
|
|
|
|
|
/** @property {object} val
|
|
| | data
|
|
| --- | ---
|
|
| *value* | Text of message, as string
|
|
*/
|
|
|
|
this.val = {
|
|
value: "send a message"
|
|
}
|
|
|
|
/** @property {integer} size Text size in px */
|
|
this.size = 12;
|
|
|
|
}
|
|
util.inherits(message, widget);
|
|
|
|
message.prototype.init = function() {
|
|
if (this.canvas.getAttribute("label")) {
|
|
this.val.value = this.canvas.getAttribute("label");
|
|
}
|
|
//this.size = Math.sqrt((this.width * this.height) / (this.val.message.length));
|
|
this.draw();
|
|
}
|
|
|
|
message.prototype.draw = function() {
|
|
this.erase();
|
|
with (this.context) {
|
|
if (this.clicked) {
|
|
fillStyle = this.colors.accent;
|
|
} else {
|
|
fillStyle = this.colors.fill;
|
|
}
|
|
fillRect(0,0,this.width,this.height)
|
|
|
|
if (this.clicked) {
|
|
fillStyle = this.colors.white;
|
|
} else {
|
|
fillStyle = this.colors.black;
|
|
}
|
|
textAlign = "left";
|
|
font = this.size+"px courier";
|
|
}
|
|
this.wrapText(this.val.value, 5, 1+this.size, this.width-6, this.size);
|
|
}
|
|
|
|
message.prototype.click = function(e) {
|
|
this.draw();
|
|
this.transmit(this.val);
|
|
}
|
|
|
|
message.prototype.release = function(e) {
|
|
this.draw();
|
|
}
|
|
},{"../core/widget":3,"util":40}],21:[function(require,module,exports){
|
|
var math = require('../utils/math')
|
|
var util = require('util');
|
|
var widget = require('../core/widget');
|
|
|
|
/**
|
|
@class metro
|
|
Bouncing ball metronome
|
|
```html
|
|
<canvas nx="metro"></canvas>
|
|
```
|
|
<canvas nx="metro" style="margin-left:25px"></canvas>
|
|
*/
|
|
|
|
var metro = module.exports = function (target) {
|
|
this.defaultSize = { width: 100, height: 20 };
|
|
widget.call(this, target);
|
|
|
|
//define unique attributes
|
|
|
|
/** @property {object} val
|
|
| | data
|
|
| --- | ---
|
|
| *beat* | Which side the ball is bouncing on (0 if left, 1 if right)
|
|
*/
|
|
this.val = {
|
|
beat: 0
|
|
}
|
|
|
|
this.x = 10;
|
|
this.y = 10;
|
|
this.loc = 10;
|
|
this.nodeSize = 10;
|
|
/** @property {float} speed Speed of the ball (default 1) */
|
|
this.speed = 1;
|
|
this.direction = 1;
|
|
/** @property {string} orientation Orientation of metro. Default is "horizontal". */
|
|
this.orientation = "horizontal"
|
|
this.boundary = this.width
|
|
|
|
nx.aniItems.push(this.advance.bind(this));
|
|
this.active = true;
|
|
|
|
this.init();
|
|
}
|
|
util.inherits(metro, widget);
|
|
|
|
metro.prototype.init = function() {
|
|
this.nodeSize = Math.min(this.width,this.height)/2;
|
|
if (this.width<this.height) {
|
|
this.orientation = "vertical"
|
|
this.boundary = this.height
|
|
} else {
|
|
this.orientation = "horizontal"
|
|
this.boundary = this.width
|
|
}
|
|
this.x = this.nodeSize;
|
|
this.y = this.nodeSize;
|
|
this.loc = this.nodeSize;
|
|
|
|
this.draw();
|
|
|
|
}
|
|
|
|
metro.prototype.draw = function() {
|
|
this.erase()
|
|
with (this.context) {
|
|
fillStyle = this.colors.fill;
|
|
fillRect(0,0,this.width,this.height);
|
|
|
|
beginPath();
|
|
fillStyle = this.colors.accent;
|
|
arc(this.x, this.y, this.nodeSize, 0, Math.PI*2, true);
|
|
fill();
|
|
closePath();
|
|
}
|
|
|
|
this.drawLabel();
|
|
}
|
|
|
|
metro.prototype.click = function() {
|
|
}
|
|
|
|
metro.prototype.move = function() {
|
|
if (this.clicked) {
|
|
this.speed -= (this.deltaMove.y / 50);
|
|
}
|
|
}
|
|
|
|
metro.prototype.release = function() {
|
|
}
|
|
|
|
metro.prototype.advance = function() {
|
|
if (this.speed>=0) {
|
|
this.loc += this.speed * this.direction;
|
|
} else {
|
|
this.loc += this.speed * this.direction;
|
|
}
|
|
if (this.loc-this.nodeSize<0 || this.loc+this.nodeSize>this.boundary) {
|
|
this.val.beat = math.scale(this.direction,-1,1,0,1)
|
|
this.transmit(this.val);
|
|
this.direction *= -1
|
|
}
|
|
if (this.orientation == "vertical") {
|
|
this.y = this.loc
|
|
} else {
|
|
this.x = this.loc
|
|
}
|
|
this.draw();
|
|
}
|
|
|
|
metro.prototype.customDestroy = function() {
|
|
nx.removeAni(this.advance.bind(this))
|
|
}
|
|
},{"../core/widget":3,"../utils/math":6,"util":40}],22:[function(require,module,exports){
|
|
var util = require('util');
|
|
var widget = require('../core/widget');
|
|
var math = require('../utils/math');
|
|
|
|
/**
|
|
@class mouse
|
|
Mouse tracker, relative to web browser window.
|
|
```html
|
|
<canvas nx="mouse"></canvas>
|
|
```
|
|
<canvas nx="mouse" style="margin-left:25px"></canvas>
|
|
*/
|
|
|
|
var mouse = module.exports = function (target) {
|
|
|
|
this.defaultSize = { width: 98, height: 100 };
|
|
widget.call(this, target);
|
|
|
|
/** @property {object} val
|
|
| | data
|
|
| --- | ---
|
|
| *x* | x value of mouse relative to browser
|
|
| *y* | y value of mouse relative to browser
|
|
| *deltax* | x change in mouse from last position
|
|
| *deltay* | y change in mouse from last position
|
|
*/
|
|
this.val = {
|
|
x: 0,
|
|
y: 0,
|
|
deltax: 0,
|
|
deltay: 0
|
|
}
|
|
this.inside = new Object();
|
|
this.boundmove = this.preMove.bind(this)
|
|
this.mousing = window.addEventListener("mousemove", this.boundmove, false);
|
|
|
|
this.init();
|
|
}
|
|
util.inherits(mouse, widget);
|
|
|
|
mouse.prototype.init = function() {
|
|
|
|
this.inside.height = this.height;
|
|
this.inside.width = this.width;
|
|
this.inside.left = 0;
|
|
this.inside.top = 0;
|
|
this.inside.quarterwid = (this.inside.width)/4;
|
|
|
|
this.draw();
|
|
}
|
|
|
|
mouse.prototype.draw = function() {
|
|
this.erase();
|
|
|
|
with (this.context) {
|
|
fillStyle = this.colors.fill;
|
|
fillRect(0,0,this.width,this.height);
|
|
|
|
var scaledx = -(this.val.x) * this.height;
|
|
var scaledy = -(this.val.y) * this.height;
|
|
var scaleddx = -(this.val.deltax) * this.height - this.height/2;
|
|
var scaleddy = -(this.val.deltay) * this.height - this.height/2;
|
|
|
|
fillStyle = this.colors.accent;
|
|
fillRect(this.inside.left, this.inside.height, this.inside.quarterwid, scaledx);
|
|
fillRect(this.inside.quarterwid, this.inside.height, this.inside.quarterwid, scaledy);
|
|
fillRect(this.inside.quarterwid*2, this.inside.height, this.inside.quarterwid, scaleddx);
|
|
fillRect(this.inside.quarterwid*3, this.inside.height, this.inside.quarterwid, scaleddy);
|
|
|
|
globalAlpha = 0.5;
|
|
fillStyle = this.colors.white;
|
|
textAlign = "center";
|
|
font = this.width/7+"px gill sans";
|
|
fillText("x", this.inside.quarterwid*0 + this.inside.quarterwid/2, this.height-7);
|
|
fillText("y", this.inside.quarterwid*1 + this.inside.quarterwid/2, this.height-7);
|
|
fillText("dx", this.inside.quarterwid*2 + this.inside.quarterwid/2, this.height-7);
|
|
fillText("dy", this.inside.quarterwid*3 + this.inside.quarterwid/2, this.height-7);
|
|
|
|
globalAlpha = 1;
|
|
}
|
|
|
|
this.drawLabel();
|
|
}
|
|
|
|
mouse.prototype.move = function(e) {
|
|
this.val = {
|
|
deltax: e.clientX/window.innerWidth - this.val.x,
|
|
deltay: math.invert(e.clientY/window.innerHeight) - this.val.y,
|
|
x: e.clientX/window.innerWidth,
|
|
y: math.invert(e.clientY/window.innerHeight)
|
|
}
|
|
this.draw();
|
|
this.transmit(this.val);
|
|
|
|
}
|
|
|
|
mouse.prototype.customDestroy = function() {
|
|
window.removeEventListener("mousemove", this.boundmove, false);
|
|
}
|
|
},{"../core/widget":3,"../utils/math":6,"util":40}],23:[function(require,module,exports){
|
|
var math = require('../utils/math')
|
|
var util = require('util');
|
|
var widget = require('../core/widget');
|
|
|
|
/**
|
|
@class multislider
|
|
Multiple vertical sliders in one interface.
|
|
```html
|
|
<canvas nx="multislider"></canvas>
|
|
```
|
|
<canvas nx="multislider" style="margin-left:25px"></canvas>
|
|
*/
|
|
var multislider = module.exports = function (target) {
|
|
|
|
this.defaultSize = { width: 100, height: 75 };
|
|
widget.call(this, target);
|
|
|
|
/** @property {integer} sliders Number of sliders in the multislider. (Must call .init() after changing this setting, or set with .setNumberOfSliders) */
|
|
this.sliders = 15;
|
|
|
|
/** @property {array} val Array of slider values. <br> **Note:** This widget's output is not .val! Transmitted output is:
|
|
|
|
| | data
|
|
| --- | ---
|
|
| *(slider index)* | value of currently changed slider
|
|
| list | all multislider values as list. (if the interface sends to js or node, this list will be an array. if sending to ajax, max7, etc, the list will be a string of space-separated values)
|
|
|
|
*/
|
|
|
|
this.val = new Object();
|
|
for (var i=0;i<this.sliders;i++) {
|
|
this.val[i] = 0.7;
|
|
}
|
|
this.sliderClicked = 0;
|
|
this.oldSliderToMove;
|
|
this.init();
|
|
}
|
|
util.inherits(multislider, widget);
|
|
|
|
multislider.prototype.init = function() {
|
|
this.val = new Object();
|
|
for (var i=0;i<this.sliders;i++) {
|
|
this.val[i] = 0.7;
|
|
}
|
|
this.realSpace = { x: this.width, y: this.height }
|
|
this.sliderWidth = this.realSpace.x/this.sliders;
|
|
this.draw();
|
|
}
|
|
|
|
multislider.prototype.draw = function() {
|
|
this.erase();
|
|
with (this.context) {
|
|
fillStyle = this.colors.fill;
|
|
fillRect(0,0,this.width,this.height);
|
|
|
|
strokeStyle = this.colors.accent;
|
|
fillStyle = this.colors.accent;
|
|
lineWidth = 5;
|
|
|
|
for(var i=0; i<this.sliders; i++) {
|
|
beginPath();
|
|
moveTo(i*this.sliderWidth, this.height-this.val[i]*this.height);
|
|
lineTo(i*this.sliderWidth + this.sliderWidth, this.height-this.val[i]*this.height);
|
|
stroke();
|
|
lineTo(i*this.sliderWidth + this.sliderWidth, this.height);
|
|
lineTo(i*this.sliderWidth, this.height);
|
|
globalAlpha = 0.3 - (i%3)*0.1;
|
|
fill();
|
|
closePath();
|
|
globalAlpha = 1;
|
|
}
|
|
}
|
|
this.drawLabel();
|
|
}
|
|
|
|
multislider.prototype.click = function() {
|
|
this.oldSliderToMove = false;
|
|
this.move(true);
|
|
}
|
|
|
|
multislider.prototype.move = function(firstclick) {
|
|
if (this.clicked) {
|
|
|
|
|
|
if (this.clickPos.touches.length>1) {
|
|
|
|
for (var i=0;i<this.clickPos.touches.length;i++) {
|
|
var sliderToMove = Math.floor(this.clickPos.touches[i].x / this.sliderWidth);
|
|
sliderToMove = math.clip(sliderToMove,0,this.sliders-1);
|
|
this.val[sliderToMove] = math.clip(math.invert((this.clickPos.touches[i].y / this.height)),0,1);
|
|
}
|
|
|
|
} else {
|
|
|
|
var sliderToMove = Math.floor(this.clickPos.x / this.sliderWidth);
|
|
sliderToMove = math.clip(sliderToMove,0,this.sliders-1);
|
|
this.val[sliderToMove] = math.clip(math.invert(this.clickPos.y / this.height),0,1);
|
|
|
|
if (this.oldSliderToMove && this.oldSliderToMove > sliderToMove + 1) {
|
|
var missed = this.oldSliderToMove - sliderToMove - 1;
|
|
for (var i=1;i<=missed;i++) {
|
|
this.val[sliderToMove+i] = this.val[sliderToMove] + (this.val[this.oldSliderToMove] - this.val[sliderToMove]) * ((i/(missed+1)));
|
|
}
|
|
} else if (this.oldSliderToMove && sliderToMove > this.oldSliderToMove + 1) {
|
|
var missed = sliderToMove - this.oldSliderToMove - 1;
|
|
for (var i=1;i<=missed;i++) {
|
|
this.val[this.oldSliderToMove+i] = this.val[this.oldSliderToMove] + (this.val[sliderToMove] - this.val[this.oldSliderToMove]) * ((i/(missed+1)));
|
|
}
|
|
}
|
|
|
|
}
|
|
this.draw();
|
|
}
|
|
var msg = new Object()
|
|
msg[sliderToMove] = this.val[sliderToMove]
|
|
if (this.destination=="js" || this.destination=="node") {
|
|
msg["list"] = this.val;
|
|
} else {
|
|
msg["list"] = new String();
|
|
for (var key in this.val) { msg["list"] += this.val[key] + " " }
|
|
}
|
|
this.transmit(msg);
|
|
this.oldSliderToMove = sliderToMove;
|
|
|
|
}
|
|
|
|
/** @method setNumberOfSliders
|
|
@param {integer} [num] New number of sliders in the multislider */
|
|
multislider.prototype.setNumberOfSliders = function(numOfSliders) {
|
|
this.sliders = numOfSliders;
|
|
this.val = new Array();
|
|
for (var i=0;i<this.sliders;i++) {
|
|
this.val.push(0.7);
|
|
}
|
|
this.sliderWidth = this.realSpace.x/this.sliders;
|
|
this.init();
|
|
}
|
|
|
|
/** @method setSliderValue
|
|
Sets a slider to new value and transmits.
|
|
@param {integer} [slider] Slider to set (slider index starts at 0)
|
|
@param {integer} [value] New slider value */
|
|
multislider.prototype.setSliderValue = function(slider,value) {
|
|
this.val[slider] = value;
|
|
this.draw();
|
|
msg[slider] = this.val[slider]
|
|
if (this.destination=="js" || this.destination=="node") {
|
|
msg["list"] = this.val;
|
|
} else {
|
|
msg["list"] = new String();
|
|
for (var key in this.val) { msg["list"] += this.val[key] + " " }
|
|
}
|
|
this.transmit(msg);
|
|
}
|
|
|
|
},{"../core/widget":3,"../utils/math":6,"util":40}],24:[function(require,module,exports){
|
|
var math = require('../utils/math');
|
|
var drawing = require('../utils/drawing');
|
|
var util = require('util');
|
|
var widget = require('../core/widget');
|
|
|
|
/**
|
|
@class multitouch
|
|
Multitouch 2d-slider with up to 5 points of touch.
|
|
```html
|
|
<canvas nx="multitouch"></canvas>
|
|
```
|
|
<canvas nx="multitouch" style="margin-left:25px"></canvas>
|
|
*/
|
|
|
|
var multitouch = module.exports = function (target) {
|
|
|
|
this.defaultSize = { width: 200, height: 200 };
|
|
widget.call(this, target);
|
|
|
|
//unique attributes
|
|
this.nodeSize = this.width/10;
|
|
|
|
/** @property {object} val
|
|
| | data
|
|
| --- | ---
|
|
| *touch1.x* | x position of first touch
|
|
| *touch1.y* | y position of first touch
|
|
| *touch2.x* | x position of second touch (if 2 touches)
|
|
| *touch2.y* | y position of second touch (if 2 touches)
|
|
| *etc* |
|
|
*/
|
|
this.val = {
|
|
touch1: {
|
|
x: 0,
|
|
y: 0
|
|
}
|
|
}
|
|
|
|
this.nodes = new Array();
|
|
|
|
/** @property {string} text Text that will show when object is static */
|
|
this.text = "multitouch";
|
|
|
|
this.rainbow = ["#00f", "#04f", "#08F", "0AF", "0FF"];
|
|
|
|
/** @property {string} mode "normal" or "matrix" mode. "matrix" mode has a GUI of discrete touch areas.
|
|
*/
|
|
this.mode = "normal";
|
|
|
|
/** @property {integer} rows How many rows in the matrix (matrix mode only)
|
|
*/
|
|
this.rows = 10;
|
|
|
|
/** @property {integer} cols How many rows in the matrix (matrix mode only)
|
|
*/
|
|
this.cols = 10;
|
|
|
|
/** @property {array} matrixLabels An array of strings that can provide text labels on cells of the matrix. If shorter than the matrix cells, the array will repeat.
|
|
```
|
|
this.mode = "matrix"
|
|
this.matrixLabels = [ "A", "A#", "B", "C" ]
|
|
this.init();
|
|
```
|
|
*/
|
|
this.matrixLabels = false;
|
|
|
|
this.init();
|
|
}
|
|
util.inherits(multitouch, widget);
|
|
|
|
multitouch.prototype.init = function() {
|
|
this.nodeSize = this.width/10;
|
|
this.draw();
|
|
}
|
|
|
|
multitouch.prototype.draw = function() {
|
|
this.erase();
|
|
with (this.context) {
|
|
fillStyle = this.colors.fill;
|
|
fillRect(0,0,this.width,this.height);
|
|
|
|
var count = 0;
|
|
|
|
if (this.mode == "matrix") {
|
|
for (var j=0;j<this.rows;j++) {
|
|
for (var i=0;i<this.cols;i++) {
|
|
with (this.context) {
|
|
beginPath();
|
|
fillStyle = this.colors.accent;
|
|
strokeStyle = this.colors.border;
|
|
lineWidth = 1;
|
|
var circx = i*this.width/this.cols + (this.width/this.cols)/2;
|
|
var circy = j*this.height/this.rows + (this.height/this.rows)/2;
|
|
arc(circx, circy, (this.height/this.rows)/2, 0, Math.PI*2, true);
|
|
stroke();
|
|
fillStyle = this.colors.border;
|
|
textAlign = "center";
|
|
textBaseline = "middle";
|
|
if (this.matrixLabels) {
|
|
fillText(this.matrixLabels[count%this.matrixLabels.length], circx, circy);
|
|
count++
|
|
}
|
|
var thisarea = {
|
|
x: i*this.width/this.cols,
|
|
y: j*this.height/this.rows,
|
|
w: this.width/this.cols,
|
|
h: this.height/this.rows
|
|
}
|
|
if (this.clickPos.touches.length>=1) {
|
|
for (var k=0;k<this.clickPos.touches.length;k++) {
|
|
if (drawing.isInside(this.clickPos.touches[k],thisarea)) {
|
|
globalAlpha=0.5;
|
|
fillStyle = this.colors.accent;
|
|
fill();
|
|
globalAlpha=0.3;
|
|
fillStyle = this.rainbow[k];
|
|
fill();
|
|
globalAlpha=1;
|
|
}
|
|
}
|
|
}
|
|
closePath();
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
if (this.clickPos.touches.length>=1) {
|
|
for (var i=0;i<this.clickPos.touches.length;i++) {
|
|
|
|
with (this.context) {
|
|
globalAlpha=0.5;
|
|
beginPath();
|
|
fillStyle = this.colors.accent;
|
|
strokeStyle = this.colors.border;
|
|
lineWidth = this.lineWidth;
|
|
arc(this.clickPos.touches[i].x, this.clickPos.touches[i].y, this.nodeSize, 0, Math.PI*2, true);
|
|
fill();
|
|
// stroke();
|
|
closePath();
|
|
globalAlpha=0.3;
|
|
beginPath();
|
|
fillStyle = this.rainbow[i];
|
|
strokeStyle = this.colors.border;
|
|
lineWidth = this.lineWidth;
|
|
arc(this.clickPos.touches[i].x, this.clickPos.touches[i].y, this.nodeSize, 0, Math.PI*2, true);
|
|
fill();
|
|
// stroke();
|
|
closePath();
|
|
globalAlpha=1;
|
|
}
|
|
|
|
}
|
|
}
|
|
else {
|
|
fillStyle = this.colors.border;
|
|
font = "14px courier";
|
|
textAlign = "center";
|
|
|
|
fillText(this.text, this.width/2, this.height/2);
|
|
}
|
|
}
|
|
}
|
|
this.drawLabel();
|
|
}
|
|
|
|
multitouch.prototype.click = function() {
|
|
this.draw();
|
|
this.sendit();
|
|
}
|
|
|
|
multitouch.prototype.move = function() {
|
|
if (this.clicked) {
|
|
this.draw();
|
|
this.sendit();
|
|
}
|
|
}
|
|
|
|
multitouch.prototype.release = function() {
|
|
|
|
if(!this.clicked) {
|
|
this.clickPos.touches = new Array();
|
|
for (var i=0;i<5;i++) {
|
|
this.val["touch"+i] = {
|
|
x: 0,
|
|
y: 0
|
|
}
|
|
}
|
|
this.transmit(this.val);
|
|
}
|
|
|
|
this.draw();
|
|
this.sendit();
|
|
|
|
}
|
|
|
|
multitouch.prototype.sendit = function() {
|
|
this.val = new Object();
|
|
for (var i=0;i<this.clickPos.touches.length;i++) {
|
|
this.val["touch"+i] = {
|
|
x: this.clickPos.touches[i].x/this.canvas.width,
|
|
y: math.invert(this.clickPos.touches[i].y/this.canvas.height)
|
|
}
|
|
}
|
|
this.transmit(this.val);
|
|
}
|
|
},{"../core/widget":3,"../utils/drawing":5,"../utils/math":6,"util":40}],25:[function(require,module,exports){
|
|
var math = require('../utils/math')
|
|
var util = require('util');
|
|
var widget = require('../core/widget');
|
|
|
|
/**
|
|
@class number
|
|
Number box
|
|
```html
|
|
<canvas nx="number"></canvas>
|
|
```
|
|
<canvas nx="number" style="margin-left:25px"></canvas>
|
|
*/
|
|
|
|
var number = module.exports = function (target) {
|
|
this.defaultSize = { width: 50, height: 20 };
|
|
widget.call(this, target);
|
|
|
|
/** @property {object} val
|
|
| | data
|
|
| --- | ---
|
|
| *value* | Number value
|
|
|
|
```js
|
|
// Sets number1.val.value to 20
|
|
number1.set({
|
|
value: 20
|
|
})
|
|
```
|
|
*/
|
|
this.val = {
|
|
value: 0
|
|
}
|
|
this.init();
|
|
}
|
|
util.inherits(number, widget);
|
|
|
|
number.prototype.init = function() {
|
|
this.draw();
|
|
}
|
|
|
|
number.prototype.draw = function() {
|
|
this.erase();
|
|
with (this.context) {
|
|
fillStyle = this.colors.fill;
|
|
fillRect(0,0,this.width,this.height);
|
|
|
|
fillStyle = this.colors.black;
|
|
textAlign = "left";
|
|
font = this.height*.6+"px courier";
|
|
textBaseline = 'middle';
|
|
fillText(this.val.value, 10, this.height/2-1);
|
|
}
|
|
}
|
|
|
|
number.prototype.move = function(e) {
|
|
if (this.clicked) {
|
|
this.val.value += (this.deltaMove.y*-.1);
|
|
this.val.value = math.prune(this.val.value,1);
|
|
this.draw();
|
|
this.transmit(this.val);
|
|
}
|
|
}
|
|
},{"../core/widget":3,"../utils/math":6,"util":40}],26:[function(require,module,exports){
|
|
var math = require('../utils/math')
|
|
var util = require('util');
|
|
var widget = require('../core/widget');
|
|
|
|
/**
|
|
@class position
|
|
Two-dimensional touch slider.
|
|
```html
|
|
<canvas nx="position"></canvas>
|
|
```
|
|
<canvas nx="position" style="margin-left:25px"></canvas>
|
|
*/
|
|
|
|
var position = module.exports = function (target) {
|
|
this.defaultSize = { width: 150, height: 100 };
|
|
widget.call(this, target);
|
|
|
|
/** @property {integer} nodeSize Size of touch node graphic. */
|
|
this.nodeSize = 15;
|
|
|
|
/** @property {object} val
|
|
| | data
|
|
| --- | ---
|
|
| *x* | x position of slider (float 0-1)
|
|
| *y* | y position of slider (float 0-1)
|
|
*/
|
|
this.val = {
|
|
x: 0.5,
|
|
y: 0.5
|
|
}
|
|
|
|
this.init();
|
|
}
|
|
util.inherits(position, widget);
|
|
|
|
position.prototype.init = function() {
|
|
this.nodeSize = Math.min(this.height,this.width)/10;
|
|
this.actualWid = this.width - this.nodeSize*2;
|
|
this.actualHgt = this.height - this.nodeSize*2;
|
|
this.draw();
|
|
}
|
|
|
|
position.prototype.draw = function() {
|
|
this.erase();
|
|
with (this.context) {
|
|
|
|
fillStyle = this.colors.fill;
|
|
fillRect(0,0,this.width,this.height);
|
|
|
|
var drawingX = this.val.x * this.actualWid + this.nodeSize
|
|
var drawingY = math.invert(this.val.y) * this.actualHgt + this.nodeSize
|
|
|
|
//stay within right/left bounds
|
|
if (drawingX<(this.nodeSize)) {
|
|
drawingX = this.nodeSize;
|
|
} else if (drawingX>(this.width-this.nodeSize)) {
|
|
drawingX = this.width - this.nodeSize;
|
|
}
|
|
//stay within top/bottom bounds
|
|
if (drawingY<(this.nodeSize)) {
|
|
drawingY = this.nodeSize;
|
|
} else if (drawingY>(this.height-this.nodeSize)) {
|
|
drawingY = this.height - this.nodeSize;
|
|
}
|
|
|
|
with (this.context) {
|
|
beginPath();
|
|
strokeStyle = this.colors.accent;
|
|
lineWidth = 5;
|
|
moveTo(0,this.height);
|
|
lineTo(this.val.x*this.width,this.height);
|
|
moveTo(0,this.height);
|
|
lineTo(0,math.invert(this.val.y)*this.height);
|
|
stroke();
|
|
closePath();
|
|
beginPath();
|
|
fillStyle = this.colors.accent;
|
|
arc(drawingX, drawingY, this.nodeSize, 0, Math.PI*2, true);
|
|
fill();
|
|
closePath();
|
|
}
|
|
}
|
|
|
|
this.drawLabel();
|
|
}
|
|
|
|
position.prototype.scaleNode = function() {
|
|
var actualX = this.val.x - this.nodeSize;
|
|
var actualY = this.val.y - this.nodeSize;
|
|
var clippedX = math.clip(actualX/this.actualWid, 0, 1);
|
|
var clippedY = math.clip(actualY/this.actualHgt, 0, 1);
|
|
this.val.x = math.prune(clippedX, 3)
|
|
this.val.y = math.prune(clippedY, 3)
|
|
this.val.y = math.invert(this.val.y);
|
|
}
|
|
|
|
position.prototype.click = function() {
|
|
this.val.x = this.clickPos.x;
|
|
this.val.y = this.clickPos.y;
|
|
this.scaleNode();
|
|
this.val["state"] = "click"
|
|
this.transmit(this.val);
|
|
this.draw();
|
|
}
|
|
|
|
position.prototype.move = function() {
|
|
if (this.clicked) {
|
|
this.val.x = this.clickPos.x;
|
|
this.val.y = this.clickPos.y;
|
|
this.scaleNode();
|
|
this.val["state"] = "move"
|
|
this.transmit(this.val);
|
|
this.draw();
|
|
}
|
|
}
|
|
|
|
position.prototype.release = function() {
|
|
this.val.x = this.clickPos.x;
|
|
this.val.y = this.clickPos.y;
|
|
this.scaleNode();
|
|
this.val["state"] = "release"
|
|
this.transmit(this.val);
|
|
this.draw();
|
|
|
|
}
|
|
|
|
|
|
/** @method animate
|
|
Adds animation to the widget.
|
|
@param {string} [type] Type of animation. Currently accepts "none" or "bounce", in which case the touch node can be tossed and bounces.
|
|
*/
|
|
position.prototype.animate = function(aniType) {
|
|
|
|
switch (aniType) {
|
|
case "bounce":
|
|
nx.aniItems.push(this.aniBounce.bind(this));
|
|
break;
|
|
case "none":
|
|
nx.aniItems.splice(nx.aniItems.indexOf(this.aniBounce));
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
position.prototype.aniBounce = function() {
|
|
if (!this.clicked && this.val.x) {
|
|
this.val.x += (this.deltaMove.x/2)/this.width;
|
|
this.val.y += (this.deltaMove.y/2)/this.height;
|
|
this.val["state"] = "animated";
|
|
if (math.bounce(this.val.x, 0, 1, this.deltaMove.x) != this.deltaMove.x) {
|
|
this.deltaMove.x = math.bounce(this.val.x, 0, 1, this.deltaMove.x);
|
|
this.val["state"] = "bounce";
|
|
}
|
|
if (math.bounce(this.val.y, 0, 1, this.deltaMove.y) != this.deltaMove.y) {
|
|
this.deltaMove.y = math.bounce(this.val.y, 0, 1, this.deltaMove.y);
|
|
this.val["state"] = "bounce";
|
|
}
|
|
this.transmit(this.val);
|
|
this.draw();
|
|
}
|
|
}
|
|
|
|
position.prototype.customDestroy = function() {
|
|
nx.removeAni(this.aniBounce);
|
|
}
|
|
},{"../core/widget":3,"../utils/math":6,"util":40}],27:[function(require,module,exports){
|
|
var util = require('util');
|
|
var widget = require('../core/widget');
|
|
var math = require('../utils/math')
|
|
|
|
/**
|
|
@class range
|
|
Range slider
|
|
```html
|
|
<canvas nx="range"></canvas>
|
|
```
|
|
<canvas nx="range" style="margin-left:25px"></canvas>
|
|
*/
|
|
|
|
var range = module.exports = function (target) {
|
|
this.defaultSize = { width: 100, height: 30 };
|
|
widget.call(this, target);
|
|
|
|
/** @property {object} val Object containing core interactive aspects of widget, which are also its data output. Has the following properties:
|
|
| | data
|
|
| --- | ---
|
|
| *start* | Range start value (float 0-1)
|
|
| *stop* | Range end value (float 0-1)
|
|
| *size* | Distance between ends (float 0-1)
|
|
*/
|
|
this.val = {
|
|
start: 0.3,
|
|
stop: 0.7,
|
|
size: 0.4
|
|
}
|
|
|
|
|
|
// handling horiz possibility
|
|
/** @property {boolean} hslider Whether or not the slider is a horizontal slider. Default is false, but set automatically to true if the slider is wider than it is tall. */
|
|
this.hslider = false;
|
|
this.handle;
|
|
this.relhandle;
|
|
this.cap;
|
|
this.firsttouch = "start";
|
|
|
|
/** @property {string} mode Mode of interaction. "edge" mode lets you drag each edge of the range individually. "area" mode (default) lets you drag the range as a whole (with parallel mouse movement) or scale the range as a whole (with transverse mouse movement) */
|
|
this.mode = "area" // modes: "edge", "area"
|
|
this.touchdown = new Object();
|
|
this.init();
|
|
}
|
|
util.inherits(range, widget);
|
|
|
|
range.prototype.init = function() {
|
|
|
|
//decide if hslider or vslider
|
|
if (this.height>=this.width) {
|
|
this.hslider = false;
|
|
} else {
|
|
this.hslider = true;
|
|
}
|
|
|
|
if (this.canvas.getAttribute("label")!=null) {
|
|
this.label = this.canvas.getAttribute("label");
|
|
}
|
|
|
|
this.draw();
|
|
}
|
|
|
|
range.prototype.draw = function() {
|
|
this.erase();
|
|
|
|
with (this.context) {
|
|
fillStyle = this.colors.fill;
|
|
fillRect(0,0,this.width,this.height);
|
|
|
|
fillStyle = this.colors.accent;
|
|
|
|
if (!this.hslider) {
|
|
|
|
var x1 = 0;
|
|
var y1 = this.height-this.val.stop*this.height;
|
|
var x2 = this.width;
|
|
var y2 = this.height-this.val.start*this.height;
|
|
|
|
fillRect(x1,y1,x2-x1,y2-y1);
|
|
|
|
if (nx.showLabels) {
|
|
|
|
save();
|
|
translate(this.width/2, 0);
|
|
rotate(Math.PI/2);
|
|
textAlign = "left";
|
|
textBaseline = "middle";
|
|
font = "bold 15px courier";
|
|
fillStyle = this.colors.accent;
|
|
globalAlpha = 0.3;
|
|
fillText(this.label, this.width/2, 0);
|
|
globalAlpha = 1;
|
|
restore();
|
|
|
|
}
|
|
} else {
|
|
|
|
var x1 = this.val.start*this.width;
|
|
var y1 = 0;
|
|
var x2 = this.val.stop*this.width;
|
|
var y2 = this.height;
|
|
|
|
fillRect(x1,y1,x2-x1,y2-y1);
|
|
|
|
|
|
if (nx.showLabels) {
|
|
|
|
textAlign = "center";
|
|
textBaseline = "middle";
|
|
font = "bold 15px courier";
|
|
fillStyle = this.colors.accent;
|
|
globalAlpha = 0.3;
|
|
fillText(this.label, this.width/2, this.height/2);
|
|
globalAlpha = 1;
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
range.prototype.click = function() {
|
|
if (this.mode=="edge") {
|
|
if (this.hslider) {
|
|
if (Math.abs(this.clickPos.x-this.val.start*this.width) < Math.abs(this.clickPos.x-this.val.stop*this.width)) {
|
|
this.firsttouch = "start"
|
|
} else {
|
|
this.firsttouch = "stop"
|
|
}
|
|
} else {
|
|
if (Math.abs(Math.abs(this.clickPos.y-this.height)-this.val.start*this.height) < Math.abs(Math.abs(this.clickPos.y-this.height)-this.val.stop*this.height)) {
|
|
this.firsttouch = "start"
|
|
} else {
|
|
this.firsttouch = "stop"
|
|
}
|
|
}
|
|
} else if (this.mode=="area") {
|
|
this.touchdown = {
|
|
x: this.clickPos.x,
|
|
y: this.clickPos.y
|
|
}
|
|
this.startval = new Object();
|
|
this.startval.size = this.val.stop - this.val.start;
|
|
this.startval.loc = this.val.start + this.startval.size/2;
|
|
}
|
|
this.move();
|
|
}
|
|
|
|
range.prototype.move = function() {
|
|
|
|
if (this.mode=="edge") {
|
|
if (this.hslider) {
|
|
if (this.firsttouch=="start") {
|
|
this.val.start = this.clickPos.x/this.width;
|
|
if (this.clickPos.touches.length>1) {
|
|
this.val.stop = this.clickPos.touches[1].x/this.width;
|
|
}
|
|
} else {
|
|
this.val.stop = this.clickPos.x/this.width;
|
|
if (this.clickPos.touches.length>1) {
|
|
this.val.start = this.clickPos.touches[1].x/this.width;
|
|
}
|
|
}
|
|
} else {
|
|
if (this.firsttouch=="start") {
|
|
this.val.start = math.invert(this.clickPos.y/this.height);
|
|
if (this.clickPos.touches.length>1) {
|
|
this.val.stop = math.invert(this.clickPos.touches[1].y/this.height);
|
|
}
|
|
} else {
|
|
this.val.stop = math.invert(this.clickPos.y/this.height);
|
|
if (this.clickPos.touches.length>1) {
|
|
this.val.start = math.invert(this.clickPos.touches[1].y/this.height);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (this.val.stop < this.val.start) {
|
|
this.tempstart = this.val.start;
|
|
this.val.start = this.val.stop;
|
|
this.val.stop = this.tempstart;
|
|
if (this.firsttouch=="start") {
|
|
this.firsttouch = "stop";
|
|
} else {
|
|
this.firsttouch = "start";
|
|
}
|
|
}
|
|
this.val = {
|
|
start: math.clip(this.val.start, 0, 1),
|
|
stop: math.clip(this.val.stop, 0, 1),
|
|
}
|
|
this.val['size'] = math.prune(math.clip(Math.abs(this.val.stop - this.val.start), 0, 1), 3)
|
|
|
|
this.draw();
|
|
|
|
this.transmit(this.val);
|
|
|
|
} else if (this.mode=="area") {
|
|
|
|
if (this.hslider) {
|
|
var moveloc = this.clickPos.x/this.width;
|
|
var movesize = (this.touchdown.y - this.clickPos.y)/this.height;
|
|
} else {
|
|
var moveloc = this.clickPos.y/this.height;
|
|
var movesize = (this.touchdown.x - this.clickPos.x)/this.width;
|
|
moveloc *= -1;
|
|
movesize *= -1;
|
|
}
|
|
movesize /= 3;
|
|
var size = this.startval.size + movesize;
|
|
size = math.clip(size,0.001,1);
|
|
|
|
this.val = {
|
|
start: moveloc - size/2,
|
|
stop: moveloc + size/2
|
|
}
|
|
|
|
this.val.start = math.clip(this.val.start,0,1);
|
|
this.val.stop = math.clip(this.val.stop,0,1);
|
|
|
|
this.draw();
|
|
|
|
this.transmit(this.val);
|
|
|
|
}
|
|
}
|
|
},{"../core/widget":3,"../utils/math":6,"util":40}],28:[function(require,module,exports){
|
|
var math = require('../utils/math')
|
|
var util = require('util');
|
|
var widget = require('../core/widget');
|
|
|
|
/**
|
|
@class remix (alpha)
|
|
|
|
```html
|
|
<canvas nx="remix"></canvas>
|
|
```
|
|
<canvas nx="remix" style="margin-left:25px"></canvas>
|
|
*/
|
|
|
|
var remix = module.exports = function(target) {
|
|
|
|
this.defaultSize = { width: 400, height: 150 };
|
|
widget.call(this, target);
|
|
|
|
//define unique attributes
|
|
this.maxLength = 2000;
|
|
this.components = new Array();
|
|
this.buffer = new Array();
|
|
this.moment = 0;
|
|
this.val = {
|
|
x: 0.15,
|
|
y: 0.5
|
|
}
|
|
this.rate = 1;
|
|
this.start = 0;
|
|
this.end = 1;
|
|
this.size = 0;
|
|
this.looping = false;
|
|
this.boundLog = this.log.bind(this)
|
|
this.init();
|
|
|
|
this.boundAdv = this.advance.bind(this);
|
|
nx.aniItems.push(this.boundAdv)
|
|
|
|
}
|
|
|
|
util.inherits(remix, widget);
|
|
|
|
|
|
remix.prototype.init = function() {
|
|
this.draw();
|
|
}
|
|
|
|
//sets a new component to be recorded
|
|
remix.prototype.connect = function(target) {
|
|
var compIndex = this.components.length;
|
|
this.components.push(target);
|
|
target.tapeNum = compIndex;
|
|
target.isRecording = true;
|
|
target.recorder = this;
|
|
this.buffer[compIndex] = new Object();
|
|
for (var key in target.val) {
|
|
this.buffer[compIndex][key] = new Array();
|
|
}
|
|
|
|
}
|
|
|
|
//the actual recording function
|
|
remix.prototype.write = function(index, val) {
|
|
if (this.moment>=this.maxLength) {
|
|
this.stop();
|
|
}
|
|
for (var key in val) {
|
|
if (this.buffer[index][key]) {
|
|
this.buffer[index][key][this.moment] = val[key];
|
|
}
|
|
}
|
|
this.draw();
|
|
}
|
|
|
|
|
|
remix.prototype.draw = function() {
|
|
|
|
with (this.context) {
|
|
fillStyle = this.colors.fill;
|
|
fillRect(0,0,this.width,this.height)
|
|
}
|
|
|
|
if (this.moment>= 0) {
|
|
var nodeWid = this.width / this.moment
|
|
} else {
|
|
var nodeWid = this.width;
|
|
}
|
|
var nodeDrawWid = 5;
|
|
|
|
var nodeX = this.moment*nodeWid+this.lineWidth/2;
|
|
var nodeY;
|
|
|
|
if (!this.recording) {
|
|
with (this.context) {
|
|
strokeStyle = this.colors.accent;
|
|
lineWidth = 1;
|
|
|
|
for (var i=0;i<this.buffer.length;i++) {
|
|
for (var key in this.buffer[i]) {
|
|
for (var j=0;j<this.buffer[i][key].length;j++) {
|
|
pnodeX = (j-1)*nodeWid;
|
|
pnodeY = Math.abs(this.buffer[i][key][j-1]-1)*(this.height);
|
|
|
|
nodeX = j*nodeWid;
|
|
nodeY = Math.abs(this.buffer[i][key][j]-1)*(this.height);
|
|
|
|
beginPath()
|
|
moveTo(pnodeX,pnodeY)
|
|
lineTo(nodeX,nodeY)
|
|
stroke()
|
|
closePath();
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
} else {
|
|
|
|
with (this.context) {
|
|
font = "bold "+this.height/3+"px gill sans";
|
|
textAlign = "center";
|
|
textBaseline = "middle"
|
|
fillStyle = "#F00"
|
|
fillText("rec",this.width/2,this.height/2);
|
|
}
|
|
}
|
|
}
|
|
|
|
remix.prototype.record = function() {
|
|
this.moment = 0;
|
|
nx.aniItems.push(this.boundLog)
|
|
this.recording = true;
|
|
}
|
|
|
|
remix.prototype.log = function() {
|
|
for (var i=0;i<this.components.length;i++) {
|
|
var sender = this.components[i];
|
|
this.write(this.components[i].tapeNum,this.components[i].val);
|
|
}
|
|
this.moment++;
|
|
}
|
|
|
|
remix.prototype.stop = function() {
|
|
nx.removeAni(this.boundLog);
|
|
this.size = this.moment;
|
|
this.recording = false;
|
|
this.draw();
|
|
}
|
|
|
|
remix.prototype.scan = function(x) {
|
|
this.needle = x * this.size;
|
|
this.needle = nx.clip(this.needle,0,this.size-1)
|
|
if (this.needle) {
|
|
for (var i=0;i<this.components.length;i++) {
|
|
var sender = this.components[i];
|
|
for (var key in this.buffer[sender.tapeNum]) {
|
|
if (this.buffer[sender.tapeNum][key]) {
|
|
var val = new Object();
|
|
var max = this.buffer[sender.tapeNum][key][~~this.needle+1] ? this.buffer[sender.tapeNum][key][~~this.needle+1] : this.buffer[sender.tapeNum][key][~~this.needle]
|
|
val[key] = nx.interp(this.needle - ~~this.needle, this.buffer[sender.tapeNum][key][~~this.needle], max)
|
|
sender.set(val, true)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
remix.prototype.play = function(rate,start,end) {
|
|
rate ? this.rate = rate : null;
|
|
if (start) {
|
|
this.needle = start * this.size;
|
|
this.start = start;
|
|
} else {
|
|
this.needle = 0;
|
|
this.start = 0;
|
|
}
|
|
end ? this.end = end : this.end = 1
|
|
this.playing = true;
|
|
}
|
|
|
|
remix.prototype.loop = function() {
|
|
|
|
}
|
|
|
|
remix.prototype.advance = function() {
|
|
if (this.playing) {
|
|
this.needle += this.rate;
|
|
if (this.needle/this.size < this.end) {
|
|
this.scan(this.needle/this.size);
|
|
} else if (this.looping) {
|
|
this.needle = this.start;
|
|
} else {
|
|
this.playing = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
remix.prototype.click = function(e) {
|
|
if (this.size) {
|
|
this.scan(this.clickPos.x/this.width)
|
|
}
|
|
}
|
|
|
|
|
|
remix.prototype.move = function(e) {
|
|
if (this.size) {
|
|
this.scan(this.clickPos.x/this.width)
|
|
}
|
|
}
|
|
},{"../core/widget":3,"../utils/math":6,"util":40}],29:[function(require,module,exports){
|
|
var util = require('util');
|
|
var widget = require('../core/widget');
|
|
|
|
/**
|
|
@class select
|
|
HTML-style option selector. Outputs the chosen text string. <br> **Note:** Currently the canvas is actaully replaced by an HTML select object. Any inline style on your canvas may be lost in this transformation. To style the resultant select element, we recommend creating CSS styles for the select object using its ID or the select tag.
|
|
```html
|
|
<canvas nx="select" choices="sine,saw,square"></canvas>
|
|
```
|
|
<canvas nx="select" choices="sine,saw,square"></canvas>
|
|
*/
|
|
|
|
var select = module.exports = function (target) {
|
|
this.defaultSize = { width: 200, height: 30 };
|
|
widget.call(this, target);
|
|
|
|
/** @property {array} choices Desired choices, as an array of strings. Can be initialized with a "choices" HTML attribute of comma-separated text (see example above).
|
|
```js
|
|
select1.choices = ["PartA", "PartB", "GoNuts"]
|
|
select1.init()
|
|
```
|
|
*/
|
|
this.choices = [ ];
|
|
|
|
/** @property {object} val
|
|
| | data
|
|
| --- | ---
|
|
| *value* | Text string of option chosen
|
|
*/
|
|
this.val = new Object();
|
|
}
|
|
util.inherits(select, widget);
|
|
|
|
select.prototype.init = function() {
|
|
|
|
this.canvas.ontouchstart = null;
|
|
this.canvas.ontouchmove = null;
|
|
this.canvas.ontouchend = null;
|
|
|
|
if (this.canvas.getAttribute("choices")) {
|
|
this.choices = this.canvas.getAttribute("choices");
|
|
this.choices = this.choices.split(",");
|
|
}
|
|
|
|
var htmlstr = '<select id="'+this.canvasID+'" style="height:'+this.height+'px;width:'+this.width+'px;font-size:'+this.height/2+'px" onchange="'+this.canvasID+'.change(this)"></select><canvas height="1px" width="1px" style="display:none"></canvas>'
|
|
var canv = this.canvas
|
|
var parent = canv.parentNode;
|
|
var newdiv = document.createElement("div");
|
|
newdiv.innerHTML = htmlstr;
|
|
parent.replaceChild(newdiv,canv)
|
|
|
|
this.canvas = document.getElementById(this.canvasID);
|
|
|
|
for (var i=0;i<this.choices.length;i++) {
|
|
var option=document.createElement("option");
|
|
option.text = this.choices[i];
|
|
option.value = this.choices[i];
|
|
this.canvas.add(option,null);
|
|
}
|
|
|
|
}
|
|
|
|
// should have a modified "set" function
|
|
select.prototype.change = function(thisselect) {
|
|
this.val.text = thisselect.value;
|
|
this.transmit(this.val);
|
|
}
|
|
},{"../core/widget":3,"util":40}],30:[function(require,module,exports){
|
|
var math = require('../utils/math')
|
|
var util = require('util');
|
|
var widget = require('../core/widget');
|
|
|
|
/**
|
|
@class slider
|
|
Slider (vertical or horizontal)
|
|
```html
|
|
<canvas nx="slider"></canvas>
|
|
```
|
|
<canvas nx="slider" style="margin-left:25px"></canvas>
|
|
*/
|
|
|
|
var slider = module.exports = function (target) {
|
|
this.defaultSize = { width: 30, height: 100 };
|
|
widget.call(this, target);
|
|
|
|
/** @property {object} val
|
|
| | data
|
|
| --- | ---
|
|
| *value* | Slider value (float 0-1)
|
|
*/
|
|
this.val.value = 0.7
|
|
|
|
/** @property {string} mode Set "absolute" or "relative" mode. In absolute mode, slider will jump to click/touch position. In relative mode, it will not.
|
|
```js
|
|
nx.onload = function() {
|
|
// Slider will not jump to touch position.
|
|
slider1.mode = "relative"
|
|
}
|
|
```
|
|
*/
|
|
this.mode = "absolute";
|
|
|
|
/** @property {boolean} hslider Whether or not the slider should be horizontal. This is set to true automatically if the canvas is wider than it is tall. To override the default decision, set this property to true to create a horizontal slider, or false to create a vertical slider.
|
|
|
|
```js
|
|
nx.onload = function() {
|
|
//forces horizontal slider
|
|
slider1.hslider = true
|
|
slider1.draw();
|
|
//forces vertical slider
|
|
slider2.hslider = false
|
|
slider2.draw();
|
|
}
|
|
```
|
|
*/
|
|
this.hslider = false;
|
|
this.handle;
|
|
this.relhandle;
|
|
this.cap;
|
|
this.init();
|
|
}
|
|
util.inherits(slider, widget);
|
|
|
|
slider.prototype.init = function() {
|
|
|
|
//decide if hslider or vslider
|
|
if (this.height>=this.width) {
|
|
this.hslider = false;
|
|
} else {
|
|
this.hslider = true;
|
|
}
|
|
|
|
if (this.canvas.getAttribute("label")!=null) {
|
|
this.label = this.canvas.getAttribute("label");
|
|
}
|
|
|
|
this.draw();
|
|
}
|
|
|
|
slider.prototype.draw = function() {
|
|
|
|
this.erase();
|
|
|
|
with (this.context) {
|
|
fillStyle = this.colors.fill;
|
|
fillRect(0,0,this.width,this.height);
|
|
|
|
fillStyle = this.colors.accent;
|
|
|
|
if (!this.hslider) {
|
|
|
|
var x1 = 0;
|
|
var y1 = this.height-this.val.value*this.height;
|
|
var x2 = this.width;
|
|
var y2 = this.height;
|
|
|
|
if (this.val.value>0.01) {
|
|
fillRect(x1,y1,x2-x1,y2-y1);
|
|
}
|
|
|
|
if (nx.showLabels) {
|
|
|
|
save();
|
|
translate(this.width/2, 0);
|
|
rotate(Math.PI/2);
|
|
textAlign = "left";
|
|
textBaseline = "middle";
|
|
font = "bold 15px courier";
|
|
fillStyle = this.colors.accent;
|
|
globalAlpha = 0.3;
|
|
fillText(this.label, this.width/2, 0);
|
|
globalAlpha = 1;
|
|
restore();
|
|
|
|
}
|
|
} else {
|
|
|
|
var x1 = 0;
|
|
var y1 = 0;
|
|
var x2 = this.val.value*this.width;
|
|
var y2 = this.height;
|
|
|
|
if (this.val.value>0.01) {
|
|
fillRect(x1,y1,x2-x1,y2-y1);
|
|
}
|
|
|
|
if (nx.showLabels) {
|
|
textAlign = "center";
|
|
textBaseline = "middle";
|
|
font = "bold 15px courier";
|
|
fillStyle = this.colors.accent;
|
|
globalAlpha = 0.3;
|
|
fillText(this.label, this.width/2, this.height/2);
|
|
globalAlpha = 1;
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
slider.prototype.click = function() {
|
|
this.move();
|
|
}
|
|
|
|
slider.prototype.move = function() {
|
|
if (this.hslider) {
|
|
this.handle = this.clickPos.x;
|
|
this.relhandle = this.deltaMove.x;
|
|
this.cap = this.width;
|
|
} else {
|
|
this.handle = this.clickPos.y;
|
|
this.relhandle = this.deltaMove.y*-1;
|
|
this.cap = this.height
|
|
}
|
|
|
|
if (this.mode=="absolute") {
|
|
if (this.clicked) {
|
|
if (!this.hslider) {
|
|
this.val.value = math.prune((Math.abs((math.clip(this.clickPos.y/this.height, 0, 1)) - 1)),3);
|
|
} else {
|
|
this.val.value = math.prune(math.clip(this.clickPos.x/this.width, 0, 1),3);
|
|
}
|
|
this.draw();
|
|
}
|
|
} else if (this.mode=="relative") {
|
|
if (this.clicked) {
|
|
if (!this.hslider) {
|
|
this.val.value = math.clip((this.val.value + ((this.deltaMove.y*-1)/this.height)),0,1);
|
|
} else {
|
|
this.val.value = math.clip((this.val.value + ((this.deltaMove.x)/this.width)),0,1);
|
|
}
|
|
this.draw();
|
|
}
|
|
}
|
|
this.transmit(this.val);
|
|
}
|
|
},{"../core/widget":3,"../utils/math":6,"util":40}],31:[function(require,module,exports){
|
|
var util = require('util');
|
|
var widget = require('../core/widget');
|
|
|
|
/**
|
|
@class string
|
|
Animated model of a plucked string interface.
|
|
```html
|
|
<canvas nx="string"></canvas>
|
|
```
|
|
<canvas nx="string" style="margin-left:25px"></canvas>
|
|
*/
|
|
|
|
var string = module.exports = function (target) {
|
|
this.defaultSize = { width: 150, height: 75 };
|
|
widget.call(this, target);
|
|
|
|
/** @property {object} val Object containing the core interactive aspects of the widget, which are also its data output. Has the following properties:
|
|
| | data
|
|
| --- | ---
|
|
| *string* | Index of the string that is plucked (starts at 0)
|
|
| *x* | Where on the string the pluck occured (float 0-1);
|
|
*/
|
|
this.val = {
|
|
string: 0,
|
|
x: 0
|
|
}
|
|
/** @property {integer} numberOfStrings How many strings in the widget. We recommend setting this property with .setStrings() */
|
|
this.numberOfStrings = 10;
|
|
this.strings = new Array();
|
|
this.abovestring = new Array();
|
|
/** @property {integer} friction How quickly the string slows down */
|
|
this.friction = 1;
|
|
|
|
var stringdiv;
|
|
|
|
this.init();
|
|
|
|
nx.aniItems.push(this.draw.bind(this));
|
|
}
|
|
util.inherits(string, widget);
|
|
|
|
string.prototype.init = function() {
|
|
stringdiv = this.height/(this.numberOfStrings + 1);
|
|
for (var i=0;i<this.numberOfStrings;i++) {
|
|
this.strings[i] = {
|
|
x1: this.lineWidth,
|
|
y1: stringdiv*(1+i),
|
|
x2: this.width - this.lineWidth,
|
|
y2: stringdiv*(i+1),
|
|
held: false, // whether or not it's gripped
|
|
vibrating: false, // whether or not its vibrating
|
|
force: 0, // amount of force of pull on string
|
|
maxstretch: 0, // vibration cap (in Y domain)
|
|
stretch: 0, // current point vibrating in y domain
|
|
direction: 0, // which direction it's vibrating
|
|
above: false // is mouse above or below string
|
|
};
|
|
}
|
|
this.draw();
|
|
}
|
|
|
|
string.prototype.pulse = function() {
|
|
this.draw();
|
|
}
|
|
|
|
/* @method setStrings Sets how many strings are in the widget.
|
|
```js
|
|
string1.setStrings(20);
|
|
```
|
|
*/
|
|
string.prototype.setStrings = function(val) {
|
|
this.numberOfStrings = val;
|
|
this.strings = new Array();
|
|
this.init();
|
|
}
|
|
|
|
string.prototype.draw = function() {
|
|
this.erase();
|
|
this.makeRoundedBG();
|
|
with (this.context) {
|
|
strokeStyle = this.colors.border;
|
|
fillStyle = this.colors.fill;
|
|
lineWidth = this.lineWidth;
|
|
// stroke();
|
|
fill();
|
|
|
|
strokeStyle = this.colors.accent;
|
|
|
|
for (var i = 0;i<this.strings.length;i++) {
|
|
|
|
var st = this.strings[i];
|
|
|
|
if (st.vibrating) {
|
|
if (st.maxstretch < 0) {
|
|
st.vibrating = false;
|
|
st.held = false;
|
|
}
|
|
st.stretch = st.stretch + st.direction;
|
|
|
|
if (Math.abs(st.stretch) > st.maxstretch) {
|
|
//st.direction *= (-0.99);
|
|
st.direction *= -1;
|
|
st.stretch = st.stretch + st.direction;
|
|
st.maxstretch = st.maxstretch - this.friction;
|
|
|
|
st.direction = (st.direction / Math.abs(st.direction)) * (st.maxstretch/1)
|
|
}
|
|
|
|
beginPath();
|
|
moveTo(st.x1, st.y1);
|
|
quadraticCurveTo(this.width/2, st.y1+st.stretch, st.x2, st.y2);
|
|
stroke();
|
|
closePath();
|
|
st.on = true;
|
|
|
|
|
|
} else if (st.held) {
|
|
//will draw rounded
|
|
//if mouse is higher than string and gripup
|
|
//or if mouse is
|
|
// if (this.clickPos.y-st.y1<0 && st.gripup || this.clickPos.y-st.y1>0 && !st.gripup) {
|
|
beginPath();
|
|
moveTo(st.x1, st.y1);
|
|
quadraticCurveTo(this.clickPos.x, this.clickPos.y, st.x2, st.y2);
|
|
stroke();
|
|
closePath();
|
|
st.on = true;
|
|
/* } else {
|
|
beginPath();
|
|
moveTo(st.x1, st.y1);
|
|
lineTo(st.x2, st.y2);
|
|
stroke();
|
|
closePath();
|
|
} */
|
|
} else {
|
|
beginPath();
|
|
moveTo(st.x1, st.y1);
|
|
lineTo(st.x2, st.y2);
|
|
stroke();
|
|
closePath();
|
|
if (st.on) {
|
|
st.on = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
this.drawLabel();
|
|
}
|
|
|
|
string.prototype.click = function() {
|
|
for (var i = 0;i<this.numberOfStrings;i++) {
|
|
this.strings[i].above = (this.clickPos.y<this.strings[i].y1);
|
|
}
|
|
this.draw();
|
|
}
|
|
|
|
string.prototype.move = function() {
|
|
if (this.clicked) {
|
|
for (var i = 0;i<this.strings.length;i++) {
|
|
|
|
//if crosses string
|
|
if (this.strings[i].above != (this.clickPos.y<this.strings[i].y1) ) {
|
|
this.strings[i].held = true;
|
|
this.strings[i].above ^= true;
|
|
}
|
|
|
|
if (this.strings[i].held && Math.abs(this.clickPos.y - this.strings[i].y1) > this.height/(this.strings.length*3)) {
|
|
|
|
this.pluck(i)
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
string.prototype.release = function() {
|
|
for (var i = 0;i<this.strings.length;i++) {
|
|
if (this.strings[i].held) {
|
|
this.pluck(i);
|
|
}
|
|
}
|
|
}
|
|
|
|
string.prototype.pluck = function(which) {
|
|
var i = which;
|
|
this.val = {
|
|
string: i,
|
|
x: this.clickPos.x/this.width
|
|
}
|
|
this.transmit(this.val);
|
|
this.strings[i].held = false;
|
|
this.strings[i].force = this.clickPos.y - this.strings[i].y1;
|
|
this.strings[i].maxstretch = Math.abs(this.clickPos.y - this.strings[i].y1);
|
|
this.strings[i].stretch = this.clickPos.y - this.strings[i].y1;
|
|
this.strings[i].vibrating = true;
|
|
this.strings[i].direction = (this.clickPos.y - this.strings[i].y1)/Math.abs(this.clickPos.y - this.strings[i].y1) * ((this.clickPos.y - this.strings[i].y1)/-1.2);
|
|
}
|
|
|
|
string.prototype.customDestroy = function() {
|
|
nx.removeAni(this.draw.bind(this));
|
|
}
|
|
},{"../core/widget":3,"util":40}],32:[function(require,module,exports){
|
|
var math = require('../utils/math')
|
|
var util = require('util');
|
|
var widget = require('../core/widget');
|
|
|
|
/**
|
|
@class tilt
|
|
Mobile and Mac/Chrome-compatible tilt sensor. May not work on all devices! <br> **Notes:** Clicking on this widget toggles it inactive or active. <br>
|
|
We recommend not calling .init() on this object after the original initialization, because it will add additional redundant tilt listeners to your document.
|
|
```html
|
|
<canvas nx="tilt"></canvas>
|
|
```
|
|
<canvas nx="tilt" style="margin-left:25px"></canvas>
|
|
*/
|
|
|
|
var tilt = module.exports = function (target) {
|
|
this.defaultSize = { width: 50, height: 50 };
|
|
widget.call(this, target);
|
|
|
|
this.tiltLR;
|
|
this.tiltFB;
|
|
this.z;
|
|
/** @property {boolean} active Whether or not the tilt widget is on (animating and transmitting data). */
|
|
this.active = true;
|
|
|
|
/** @property {object} val Object containing the core interactive aspects of the widget, which are also its data output. Has the following properties:
|
|
| | data
|
|
| --- | ---
|
|
| *x* | X-axis rotation if supported (-1 to 1)
|
|
| *y* | Y-axis rotation if supported (-1 to 1)
|
|
| *z* | Z-axis rotation if supported (-1 to 1 or possibly 0 to 360 depending on device)
|
|
*/
|
|
this.val = {
|
|
x: 0,
|
|
y: 0,
|
|
z: 0
|
|
}
|
|
|
|
/** @property {string} text Text shown on tilt object
|
|
*/
|
|
|
|
this.text = "TILT";
|
|
this.init();
|
|
|
|
this.boundChromeTilt = this.chromeTilt.bind(this)
|
|
this.boundMozTilt = this.mozTilt.bind(this)
|
|
|
|
if (window.DeviceOrientationEvent) {
|
|
window.addEventListener('deviceorientation', this.boundChromeTilt, false);
|
|
} else if (window.OrientationEvent) {
|
|
window.addEventListener('MozOrientation', this.boundMozTilt, false);
|
|
} else {
|
|
console.log("Not supported on your device or browser.")
|
|
}
|
|
|
|
}
|
|
util.inherits(tilt, widget);
|
|
|
|
tilt.prototype.deviceOrientationHandler = function() {
|
|
|
|
this.val = {
|
|
x: math.prune(this.tiltLR/90,3),
|
|
y: math.prune(this.tiltFB/90,3),
|
|
z: math.prune(this.z,3)
|
|
}
|
|
|
|
if (this.active) {
|
|
this.transmit(this.val);
|
|
}
|
|
|
|
}
|
|
|
|
tilt.prototype.chromeTilt = function(eventData) {
|
|
this.tiltLR = eventData.gamma;
|
|
this.tiltFB = eventData.beta;
|
|
this.z = eventData.alpha
|
|
this.deviceOrientationHandler();
|
|
this.draw();
|
|
}
|
|
|
|
tilt.prototype.mozTilt = function(eventData) {
|
|
this.tiltLR = eventData.x * 90;
|
|
// y is the front-to-back tilt from -1 to +1, so we need to convert to degrees
|
|
// We also need to invert the value so tilting the device towards us (forward)
|
|
// results in a positive value.
|
|
this.tiltFB = eventData.y * -90;
|
|
this.z = eventData.z;
|
|
this.deviceOrientationHandler();
|
|
this.draw();
|
|
}
|
|
|
|
tilt.prototype.init = function() {
|
|
this.draw();
|
|
}
|
|
|
|
tilt.prototype.draw = function() {
|
|
|
|
this.erase();
|
|
|
|
with (this.context) {
|
|
fillStyle = this.colors.fill;
|
|
fillRect(0,0,this.width,this.height);
|
|
|
|
save();
|
|
translate(this.width/2,this.height/2)
|
|
rotate(-this.val.x*Math.PI/2);
|
|
translate(-this.width/2,-this.height/2)
|
|
globalAlpha = 0.4;
|
|
|
|
if (this.active) {
|
|
fillStyle = this.colors.accent;
|
|
} else {
|
|
fillStyle = this.colors.border;
|
|
}
|
|
|
|
fillRect(-this.width,this.height*(this.val.y/2)+this.height/2,this.width*3,this.height*2)
|
|
font = "bold "+this.height/5+"px gill sans";
|
|
textAlign = "center";
|
|
fillText(this.text, this.width/2, this.height*(this.val.y/2)+this.height/2+this.height/15);
|
|
globalAlpha = 1;
|
|
restore();
|
|
}
|
|
this.drawLabel();
|
|
}
|
|
|
|
tilt.prototype.click = function() {
|
|
this.active = !this.active;
|
|
}
|
|
|
|
tilt.prototype.customDestroy = function() {
|
|
this.active = false;
|
|
window.removeEventListener("deviceorientation",this.boundChromeTilt,false);
|
|
window.removeEventListener("mozOrientation",this.boundMozTilt,false);
|
|
}
|
|
},{"../core/widget":3,"../utils/math":6,"util":40}],33:[function(require,module,exports){
|
|
var drawing = require('../utils/drawing');
|
|
var util = require('util');
|
|
var widget = require('../core/widget');
|
|
|
|
/**
|
|
@class toggle
|
|
On/off toggle
|
|
```html
|
|
<canvas nx="toggle"></canvas>
|
|
```
|
|
<canvas nx="toggle" style="margin-left:25px"></canvas>
|
|
*/
|
|
|
|
var toggle = module.exports = function (target) {
|
|
this.defaultSize = { width: 50, height: 50 };
|
|
widget.call(this, target);
|
|
|
|
this.mindim = this.height>this.width ? this.width : this.height;
|
|
|
|
/** @property {object} val Object containing the core interactive aspects of the widget, which are also its data output. Has the following properties:
|
|
| | data
|
|
| --- | ---
|
|
| *value*| 1 if on, 0 if off
|
|
*/
|
|
this.val = {
|
|
value: 0
|
|
}
|
|
this.init();
|
|
}
|
|
util.inherits(toggle, widget);
|
|
|
|
toggle.prototype.init = function() {
|
|
this.fontsize = this.mindim/4;
|
|
this.draw();
|
|
}
|
|
|
|
toggle.prototype.draw = function() {
|
|
|
|
this.erase()
|
|
|
|
with (this.context) {
|
|
if (this.val.value) {
|
|
fillStyle = this.colors.accent;
|
|
} else {
|
|
fillStyle = this.colors.fill;
|
|
}
|
|
fillRect(0,0,this.width,this.height);
|
|
font = "bold "+this.fontsize+"px gill sans"
|
|
textAlign = "center"
|
|
if (this.val.value) {
|
|
fillStyle = this.colors.white
|
|
fillText("on", this.width/2, this.height/2 + this.fontsize/3.5 );
|
|
} else {
|
|
globalAlpha = 0.6;
|
|
fillStyle = this.colors.black
|
|
fillText("off", this.width/2, this.height/2 + this.fontsize/3.5 );
|
|
globalAlpha = 1;
|
|
}
|
|
}
|
|
|
|
this.drawLabel();
|
|
|
|
}
|
|
|
|
toggle.prototype.click = function() {
|
|
if (!this.val.value) {
|
|
this.val.value = 1;
|
|
} else {
|
|
this.val.value = 0;
|
|
}
|
|
this.draw();
|
|
this.transmit(this.val);
|
|
}
|
|
},{"../core/widget":3,"../utils/drawing":5,"util":40}],34:[function(require,module,exports){
|
|
var drawing = require('../utils/drawing');
|
|
var util = require('util');
|
|
var widget = require('../core/widget');
|
|
|
|
/**
|
|
@class typewriter
|
|
Computer keyboard listener and visualization. (Desktop only) <br> **Note:** Clicking on the widget toggles it inactive or active, which can be useful if you need to temporarily type without triggering the widget's events.
|
|
```html
|
|
<canvas nx="typewriter"></canvas>
|
|
```
|
|
<canvas nx="typewriter" style="margin-left:25px"></canvas>
|
|
*/
|
|
|
|
var typewriter = module.exports = function (target) {
|
|
this.defaultSize = { width: 175, height: 75 };
|
|
widget.call(this, target);
|
|
|
|
|
|
this.letter = ""
|
|
this.keywid = this.width/14.5;
|
|
this.keyhgt = this.height/5
|
|
|
|
/** @property {boolean} active Whether or not the widget is on (listening for events and transmitting values).*/
|
|
this.active = true;
|
|
|
|
/** @property {object} val Object containing the core interactive aspects of the widget, which are also its data output. Has the following properties:
|
|
| | data
|
|
| --- | ---
|
|
| *key* | symbol of key pressed (example: "a")
|
|
| *ascii* | ascii value of key pressed (example: 48)
|
|
| *on* | 0 if key is being pressed, 1 if key is being released
|
|
*/
|
|
this.val = {
|
|
key: "",
|
|
ascii: 0,
|
|
on: 0
|
|
}
|
|
|
|
this.rows = [
|
|
[
|
|
{ symbol: "`", value: 192, width: 1, on: false },
|
|
{ symbol: "1", value: 49, width: 1, on: false },
|
|
{ symbol: "2", value: 50, width: 1, on: false },
|
|
{ symbol: "3", value: 51, width: 1, on: false },
|
|
{ symbol: "4", value: 52, width: 1, on: false },
|
|
{ symbol: "5", value: 53, width: 1, on: false },
|
|
{ symbol: "6", value: 54, width: 1, on: false },
|
|
{ symbol: "7", value: 55, width: 1, on: false },
|
|
{ symbol: "8", value: 56, width: 1, on: false },
|
|
{ symbol: "9", value: 57, width: 1, on: false },
|
|
{ symbol: "0", value: 48, width: 1, on: false },
|
|
{ symbol: "-", value: 189, width: 1, on: false },
|
|
{ symbol: "=", value: 187, width: 1, on: false },
|
|
{ symbol: "delete", value: 46, width: 1.5, on: false }
|
|
],
|
|
[
|
|
{ symbol: "tab", value: 10, width: 1.5, on: false },
|
|
{ symbol: "q", value: 81, width: 1, on: false },
|
|
{ symbol: "w", value: 87, width: 1, on: false },
|
|
{ symbol: "e", value: 69, width: 1, on: false },
|
|
{ symbol: "r", value: 82, width: 1, on: false },
|
|
{ symbol: "t", value: 84, width: 1, on: false },
|
|
{ symbol: "y", value: 89, width: 1, on: false },
|
|
{ symbol: "u", value: 85, width: 1, on: false },
|
|
{ symbol: "i", value: 73, width: 1, on: false },
|
|
{ symbol: "o", value: 79, width: 1, on: false },
|
|
{ symbol: "p", value: 80, width: 1, on: false },
|
|
{ symbol: "[", value: 219, width: 1, on: false },
|
|
{ symbol: "]", value: 221, width: 1, on: false },
|
|
{ symbol: "\\", value: 220, width: 1, on: false }
|
|
],
|
|
[
|
|
{ symbol: "caps", value: 20, width: 1.75, on: false },
|
|
{ symbol: "a", value: 65, width: 1, on: false },
|
|
{ symbol: "s", value: 83, width: 1, on: false },
|
|
{ symbol: "d", value: 68, width: 1, on: false },
|
|
{ symbol: "f", value: 70, width: 1, on: false },
|
|
{ symbol: "g", value: 71, width: 1, on: false },
|
|
{ symbol: "h", value: 72, width: 1, on: false },
|
|
{ symbol: "j", value: 74, width: 1, on: false },
|
|
{ symbol: "k", value: 75, width: 1, on: false },
|
|
{ symbol: "l", value: 76, width: 1, on: false },
|
|
{ symbol: ";", value: 186, width: 1, on: false },
|
|
{ symbol: "'", value: 222, width: 1, on: false },
|
|
{ symbol: "enter", value: 13, width: 1.75, on: false }
|
|
],
|
|
[
|
|
{ symbol: "shift", value: 16, width: 2.25, on: false },
|
|
{ symbol: "z", value: 90, width: 1, on: false },
|
|
{ symbol: "x", value: 88, width: 1, on: false },
|
|
{ symbol: "c", value: 67, width: 1, on: false },
|
|
{ symbol: "v", value: 86, width: 1, on: false },
|
|
{ symbol: "b", value: 66, width: 1, on: false },
|
|
{ symbol: "n", value: 78, width: 1, on: false },
|
|
{ symbol: "m", value: 77, width: 1, on: false },
|
|
{ symbol: ",", value: 10, width: 1, on: false },
|
|
{ symbol: ".", value: 10, width: 1, on: false },
|
|
{ symbol: "/", value: 10, width: 1, on: false },
|
|
{ symbol: "shift", value: 16, width: 2.25, on: false }
|
|
],
|
|
[
|
|
{ symbol: "fn", value: 10, width: 1, on: false },
|
|
{ symbol: "ctrl", value: 17, width: 1, on: false },
|
|
{ symbol: "opt", value: 10, width: 1, on: false },
|
|
{ symbol: "cmd", value: 10, width: 1.25, on: false },
|
|
{ symbol: "space", value: 32, width: 5, on: false },
|
|
{ symbol: "cmd", value: 10, width: 1, on: false },
|
|
{ symbol: "opt", value: 10, width: 1, on: false },
|
|
{ symbol: "<", value: 37, width: .81, on: false },
|
|
{ symbol: "^", value: 38, width: .81, on: false },
|
|
{ symbol: "v", value: 39, width: .81, on: false },
|
|
{ symbol: ">", value: 40, width: .81, on: false }
|
|
]
|
|
]
|
|
|
|
this.boundType = this.type.bind(this);
|
|
this.boundUntype = this.untype.bind(this);
|
|
window.addEventListener("keydown", this.boundType);
|
|
window.addEventListener("keyup", this.boundUntype);
|
|
|
|
this.init();
|
|
}
|
|
util.inherits(typewriter, widget);
|
|
|
|
typewriter.prototype.init = function() {
|
|
|
|
this.keywid = this.width/14.5;
|
|
this.keyhgt = this.height/5
|
|
|
|
this.draw();
|
|
}
|
|
|
|
typewriter.prototype.draw = function() { // erase
|
|
this.erase();
|
|
|
|
if (!this.active) {
|
|
this.context.globalAlpha = 0.4
|
|
} else {
|
|
this.context.globalAlpha = 1
|
|
}
|
|
|
|
with (this.context) {
|
|
|
|
strokeStyle = this.colors.border
|
|
fillStyle = this.colors.accent
|
|
lineWidth = 1
|
|
|
|
for (var i=0;i<this.rows.length;i++) {
|
|
var currkeyL = 0;
|
|
for (var j=0;j<this.rows[i].length;j++) {
|
|
|
|
if (this.val.key==this.rows[i][j].symbol) {
|
|
if (this.val.on) {
|
|
this.rows[i][j].on = true;
|
|
} else {
|
|
this.rows[i][j].on = false;
|
|
}
|
|
}
|
|
|
|
drawing.makeRoundRect(this.context, currkeyL , i*this.keyhgt,this.keywid*this.rows[i][j].width,this.keyhgt,4);
|
|
|
|
if (this.rows[i][j].on) {
|
|
fillStyle = this.colors.accent
|
|
strokeStyle = this.colors.accent
|
|
fill()
|
|
stroke()
|
|
} else {
|
|
fillStyle = this.colors.fill
|
|
strokeStyle = this.colors.border
|
|
|
|
fill()
|
|
stroke()
|
|
}
|
|
|
|
currkeyL += this.keywid*this.rows[i][j].width;
|
|
|
|
}
|
|
}
|
|
|
|
if (this.val.on) {
|
|
globalAlpha = 0.7
|
|
fillStyle = this.colors.border;
|
|
font = this.height+"px courier";
|
|
textAlign = "center";
|
|
textBaseline = "middle";
|
|
fillText(this.val.key, this.width/2, this.height/2);
|
|
|
|
globalAlpha = 1
|
|
}
|
|
|
|
if (!this.active) {
|
|
globalAlpha = 0.7
|
|
fillStyle = this.colors.border;
|
|
font = (this.height/2)+"px courier";
|
|
textAlign = "center";
|
|
textBaseline = "middle"
|
|
fillText("inactive", this.width/2, this.height/2);
|
|
}
|
|
}
|
|
|
|
this.drawLabel();
|
|
}
|
|
|
|
typewriter.prototype.click = function(e) {
|
|
this.active = !this.active;
|
|
this.draw();
|
|
}
|
|
|
|
typewriter.prototype.type = function(e) {
|
|
if (this.active) {
|
|
var currKey = e.which;
|
|
for (var i=0;i<this.rows.length;i++) {
|
|
for (var j=0;j<this.rows[i].length;j++) {
|
|
if (currKey == this.rows[i][j].value) {
|
|
this.val.key = this.rows[i][j].symbol;
|
|
this.val.on = 1;
|
|
this.val.ascii = e.which;
|
|
this.transmit(this.val);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
this.draw();
|
|
}
|
|
}
|
|
|
|
typewriter.prototype.untype = function(e) {
|
|
if (this.active) {
|
|
var currKey = e.which;
|
|
for (var i=0;i<this.rows.length;i++) {
|
|
for (var j=0;j<this.rows[i].length;j++) {
|
|
if (currKey == this.rows[i][j].value) {
|
|
// this.rows[i][j].on = false;
|
|
this.val.key = this.rows[i][j].symbol;
|
|
this.val.on = 0;
|
|
this.val.ascii = e.which;
|
|
this.transmit(this.val);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
this.draw();
|
|
}
|
|
}
|
|
|
|
typewriter.prototype.customDestroy = function() {
|
|
window.removeEventListener("keydown", this.boundType);
|
|
window.removeEventListener("keyup", this.boundUntype);
|
|
}
|
|
},{"../core/widget":3,"../utils/drawing":5,"util":40}],35:[function(require,module,exports){
|
|
var math = require('../utils/math')
|
|
var util = require('util');
|
|
var widget = require('../core/widget');
|
|
|
|
/**
|
|
@class vinyl
|
|
For the boom bap
|
|
```html
|
|
<canvas nx="vinyl"></canvas>
|
|
```
|
|
<canvas nx="vinyl" style="margin-left:25px"></canvas>
|
|
*/
|
|
|
|
var vinyl = module.exports = function (target) {
|
|
this.defaultSize = { width: 100, height: 100 };
|
|
widget.call(this, target);
|
|
|
|
this.circleSize;
|
|
|
|
/** @property speed The rotation increment. Default is 0.05. Not to be confused with .val.speed (see below) which is the data output. During rotation, .speed will always move towards .defaultSpeed */
|
|
this.speed = 0.05;
|
|
/** @property defaultSpeed The "steady-state" rotation increment. Default is 0.05. During rotation, if .speed is changed, it will gradually move towards this. */
|
|
this.defaultspeed = 0.05
|
|
this.rotation = 0;
|
|
this.hasMovedOnce = false;
|
|
/** @property {float} val Object containing the core interactive aspects of the widget, which are also its data output. Has the following properties:
|
|
| | data
|
|
| --- | ---
|
|
| *speed*| Current speed of the record player's rotation (normal is 1)
|
|
*/
|
|
this.val = {
|
|
speed: 0
|
|
}
|
|
this.init();
|
|
nx.aniItems.push(this.spin.bind(this));
|
|
}
|
|
util.inherits(vinyl, widget);
|
|
|
|
vinyl.prototype.init = function() {
|
|
|
|
this.circleSize = (Math.min(this.center.x, this.center.y)-this.lineWidth);
|
|
this.draw();
|
|
}
|
|
|
|
vinyl.prototype.draw = function() {
|
|
this.erase()
|
|
|
|
with (this.context) {
|
|
strokeStyle = this.colors.border;
|
|
fillStyle = this.colors.fill;
|
|
fillRect(0,0,this.width,this.height)
|
|
|
|
//draw main circle
|
|
beginPath();
|
|
fillStyle = this.colors.black;
|
|
arc(this.center.x, this.center.y, this.circleSize-5, 0, Math.PI*2, true);
|
|
fill();
|
|
closePath();
|
|
|
|
|
|
//draw circle in center
|
|
beginPath();
|
|
fillStyle = this.colors.accent;
|
|
arc(this.center.x, this.center.y*1, this.circleSize/4, 0, Math.PI*2, false);
|
|
fill()
|
|
closePath();
|
|
|
|
|
|
//draw tint
|
|
beginPath();
|
|
globalAlpha = 0.5;
|
|
fillStyle = this.colors.fill;
|
|
arc(this.center.x, this.center.y, this.circleSize, this.rotation, this.rotation + 0.4, false);
|
|
lineTo(this.center.x, this.center.y);
|
|
arc(this.center.x, this.center.y, this.circleSize, this.rotation+Math.PI, this.rotation +Math.PI+ 0.4, false);
|
|
lineTo(this.center.x, this.center.y);
|
|
fill();
|
|
globalAlpha = 1;
|
|
closePath();
|
|
|
|
|
|
//draw white circle in center
|
|
beginPath();
|
|
fillStyle = this.colors.white;
|
|
arc(this.center.x, this.center.y*1, this.circleSize/16, 0, Math.PI*2, false);
|
|
fill()
|
|
closePath();
|
|
|
|
}
|
|
|
|
this.drawLabel();
|
|
}
|
|
|
|
vinyl.prototype.click = function(e) {
|
|
this.hasMovedOnce = false;
|
|
this.lastRotation = this.rotation
|
|
this.grabAngle = this.rotation % (Math.PI*2)
|
|
this.grabPos = math.toPolar(this.clickPos.x-this.center.x,this.clickPos.y-this.center.y).angle
|
|
|
|
}
|
|
|
|
vinyl.prototype.move = function() {
|
|
|
|
if (!this.hasMovedOnce) {
|
|
this.hasMovedOnce = true;
|
|
this.grabAngle = this.rotation % (Math.PI*2)
|
|
this.grabPos = math.toPolar(this.clickPos.x-this.center.x,this.clickPos.y-this.center.y).angle
|
|
}
|
|
|
|
this.rotation = math.toPolar(this.clickPos.x-this.center.x,this.clickPos.y-this.center.y).angle + this.grabAngle - this.grabPos
|
|
|
|
|
|
}
|
|
|
|
vinyl.prototype.release = function() {
|
|
this.speed = ((this.rotation - this.lastRotation) + (this.lastRotation-this.lastRotation2))/2 ;
|
|
}
|
|
|
|
vinyl.prototype.spin = function() {
|
|
|
|
if (this.clicked) {
|
|
this.speed /= 1.1;
|
|
} else {
|
|
this.speed = this.speed*0.9 + this.defaultspeed*0.1
|
|
}
|
|
|
|
// may need to math.clip(this.val.speed,-10,10);
|
|
this.val.speed = (this.rotation - this.lastRotation) * 20; // normalizes it to 1
|
|
|
|
this.lastRotation2 = this.lastRotation
|
|
this.lastRotation = this.rotation
|
|
|
|
this.rotation += this.speed
|
|
|
|
this.draw();
|
|
|
|
this.transmit(this.val)
|
|
|
|
}
|
|
|
|
vinyl.prototype.customDestroy = function() {
|
|
nx.removeAni(this.spin.bind(this));
|
|
}
|
|
},{"../core/widget":3,"../utils/math":6,"util":40}],36:[function(require,module,exports){
|
|
// Copyright Joyent, Inc. and other Node contributors.
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a
|
|
// copy of this software and associated documentation files (the
|
|
// "Software"), to deal in the Software without restriction, including
|
|
// without limitation the rights to use, copy, modify, merge, publish,
|
|
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
|
// persons to whom the Software is furnished to do so, subject to the
|
|
// following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included
|
|
// in all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
|
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
|
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
|
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
function EventEmitter() {
|
|
this._events = this._events || {};
|
|
this._maxListeners = this._maxListeners || undefined;
|
|
}
|
|
module.exports = EventEmitter;
|
|
|
|
// Backwards-compat with node 0.10.x
|
|
EventEmitter.EventEmitter = EventEmitter;
|
|
|
|
EventEmitter.prototype._events = undefined;
|
|
EventEmitter.prototype._maxListeners = undefined;
|
|
|
|
// By default EventEmitters will print a warning if more than 10 listeners are
|
|
// added to it. This is a useful default which helps finding memory leaks.
|
|
EventEmitter.defaultMaxListeners = 10;
|
|
|
|
// Obviously not all Emitters should be limited to 10. This function allows
|
|
// that to be increased. Set to zero for unlimited.
|
|
EventEmitter.prototype.setMaxListeners = function(n) {
|
|
if (!isNumber(n) || n < 0 || isNaN(n))
|
|
throw TypeError('n must be a positive number');
|
|
this._maxListeners = n;
|
|
return this;
|
|
};
|
|
|
|
EventEmitter.prototype.emit = function(type) {
|
|
var er, handler, len, args, i, listeners;
|
|
|
|
if (!this._events)
|
|
this._events = {};
|
|
|
|
// If there is no 'error' event listener then throw.
|
|
if (type === 'error') {
|
|
if (!this._events.error ||
|
|
(isObject(this._events.error) && !this._events.error.length)) {
|
|
er = arguments[1];
|
|
if (er instanceof Error) {
|
|
throw er; // Unhandled 'error' event
|
|
}
|
|
throw TypeError('Uncaught, unspecified "error" event.');
|
|
}
|
|
}
|
|
|
|
handler = this._events[type];
|
|
|
|
if (isUndefined(handler))
|
|
return false;
|
|
|
|
if (isFunction(handler)) {
|
|
switch (arguments.length) {
|
|
// fast cases
|
|
case 1:
|
|
handler.call(this);
|
|
break;
|
|
case 2:
|
|
handler.call(this, arguments[1]);
|
|
break;
|
|
case 3:
|
|
handler.call(this, arguments[1], arguments[2]);
|
|
break;
|
|
// slower
|
|
default:
|
|
len = arguments.length;
|
|
args = new Array(len - 1);
|
|
for (i = 1; i < len; i++)
|
|
args[i - 1] = arguments[i];
|
|
handler.apply(this, args);
|
|
}
|
|
} else if (isObject(handler)) {
|
|
len = arguments.length;
|
|
args = new Array(len - 1);
|
|
for (i = 1; i < len; i++)
|
|
args[i - 1] = arguments[i];
|
|
|
|
listeners = handler.slice();
|
|
len = listeners.length;
|
|
for (i = 0; i < len; i++)
|
|
listeners[i].apply(this, args);
|
|
}
|
|
|
|
return true;
|
|
};
|
|
|
|
EventEmitter.prototype.addListener = function(type, listener) {
|
|
var m;
|
|
|
|
if (!isFunction(listener))
|
|
throw TypeError('listener must be a function');
|
|
|
|
if (!this._events)
|
|
this._events = {};
|
|
|
|
// To avoid recursion in the case that type === "newListener"! Before
|
|
// adding it to the listeners, first emit "newListener".
|
|
if (this._events.newListener)
|
|
this.emit('newListener', type,
|
|
isFunction(listener.listener) ?
|
|
listener.listener : listener);
|
|
|
|
if (!this._events[type])
|
|
// Optimize the case of one listener. Don't need the extra array object.
|
|
this._events[type] = listener;
|
|
else if (isObject(this._events[type]))
|
|
// If we've already got an array, just append.
|
|
this._events[type].push(listener);
|
|
else
|
|
// Adding the second element, need to change to array.
|
|
this._events[type] = [this._events[type], listener];
|
|
|
|
// Check for listener leak
|
|
if (isObject(this._events[type]) && !this._events[type].warned) {
|
|
var m;
|
|
if (!isUndefined(this._maxListeners)) {
|
|
m = this._maxListeners;
|
|
} else {
|
|
m = EventEmitter.defaultMaxListeners;
|
|
}
|
|
|
|
if (m && m > 0 && this._events[type].length > m) {
|
|
this._events[type].warned = true;
|
|
console.error('(node) warning: possible EventEmitter memory ' +
|
|
'leak detected. %d listeners added. ' +
|
|
'Use emitter.setMaxListeners() to increase limit.',
|
|
this._events[type].length);
|
|
if (typeof console.trace === 'function') {
|
|
// not supported in IE 10
|
|
console.trace();
|
|
}
|
|
}
|
|
}
|
|
|
|
return this;
|
|
};
|
|
|
|
EventEmitter.prototype.on = EventEmitter.prototype.addListener;
|
|
|
|
EventEmitter.prototype.once = function(type, listener) {
|
|
if (!isFunction(listener))
|
|
throw TypeError('listener must be a function');
|
|
|
|
var fired = false;
|
|
|
|
function g() {
|
|
this.removeListener(type, g);
|
|
|
|
if (!fired) {
|
|
fired = true;
|
|
listener.apply(this, arguments);
|
|
}
|
|
}
|
|
|
|
g.listener = listener;
|
|
this.on(type, g);
|
|
|
|
return this;
|
|
};
|
|
|
|
// emits a 'removeListener' event iff the listener was removed
|
|
EventEmitter.prototype.removeListener = function(type, listener) {
|
|
var list, position, length, i;
|
|
|
|
if (!isFunction(listener))
|
|
throw TypeError('listener must be a function');
|
|
|
|
if (!this._events || !this._events[type])
|
|
return this;
|
|
|
|
list = this._events[type];
|
|
length = list.length;
|
|
position = -1;
|
|
|
|
if (list === listener ||
|
|
(isFunction(list.listener) && list.listener === listener)) {
|
|
delete this._events[type];
|
|
if (this._events.removeListener)
|
|
this.emit('removeListener', type, listener);
|
|
|
|
} else if (isObject(list)) {
|
|
for (i = length; i-- > 0;) {
|
|
if (list[i] === listener ||
|
|
(list[i].listener && list[i].listener === listener)) {
|
|
position = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (position < 0)
|
|
return this;
|
|
|
|
if (list.length === 1) {
|
|
list.length = 0;
|
|
delete this._events[type];
|
|
} else {
|
|
list.splice(position, 1);
|
|
}
|
|
|
|
if (this._events.removeListener)
|
|
this.emit('removeListener', type, listener);
|
|
}
|
|
|
|
return this;
|
|
};
|
|
|
|
EventEmitter.prototype.removeAllListeners = function(type) {
|
|
var key, listeners;
|
|
|
|
if (!this._events)
|
|
return this;
|
|
|
|
// not listening for removeListener, no need to emit
|
|
if (!this._events.removeListener) {
|
|
if (arguments.length === 0)
|
|
this._events = {};
|
|
else if (this._events[type])
|
|
delete this._events[type];
|
|
return this;
|
|
}
|
|
|
|
// emit removeListener for all listeners on all events
|
|
if (arguments.length === 0) {
|
|
for (key in this._events) {
|
|
if (key === 'removeListener') continue;
|
|
this.removeAllListeners(key);
|
|
}
|
|
this.removeAllListeners('removeListener');
|
|
this._events = {};
|
|
return this;
|
|
}
|
|
|
|
listeners = this._events[type];
|
|
|
|
if (isFunction(listeners)) {
|
|
this.removeListener(type, listeners);
|
|
} else {
|
|
// LIFO order
|
|
while (listeners.length)
|
|
this.removeListener(type, listeners[listeners.length - 1]);
|
|
}
|
|
delete this._events[type];
|
|
|
|
return this;
|
|
};
|
|
|
|
EventEmitter.prototype.listeners = function(type) {
|
|
var ret;
|
|
if (!this._events || !this._events[type])
|
|
ret = [];
|
|
else if (isFunction(this._events[type]))
|
|
ret = [this._events[type]];
|
|
else
|
|
ret = this._events[type].slice();
|
|
return ret;
|
|
};
|
|
|
|
EventEmitter.listenerCount = function(emitter, type) {
|
|
var ret;
|
|
if (!emitter._events || !emitter._events[type])
|
|
ret = 0;
|
|
else if (isFunction(emitter._events[type]))
|
|
ret = 1;
|
|
else
|
|
ret = emitter._events[type].length;
|
|
return ret;
|
|
};
|
|
|
|
function isFunction(arg) {
|
|
return typeof arg === 'function';
|
|
}
|
|
|
|
function isNumber(arg) {
|
|
return typeof arg === 'number';
|
|
}
|
|
|
|
function isObject(arg) {
|
|
return typeof arg === 'object' && arg !== null;
|
|
}
|
|
|
|
function isUndefined(arg) {
|
|
return arg === void 0;
|
|
}
|
|
|
|
},{}],37:[function(require,module,exports){
|
|
if (typeof Object.create === 'function') {
|
|
// implementation from standard node.js 'util' module
|
|
module.exports = function inherits(ctor, superCtor) {
|
|
ctor.super_ = superCtor
|
|
ctor.prototype = Object.create(superCtor.prototype, {
|
|
constructor: {
|
|
value: ctor,
|
|
enumerable: false,
|
|
writable: true,
|
|
configurable: true
|
|
}
|
|
});
|
|
};
|
|
} else {
|
|
// old school shim for old browsers
|
|
module.exports = function inherits(ctor, superCtor) {
|
|
ctor.super_ = superCtor
|
|
var TempCtor = function () {}
|
|
TempCtor.prototype = superCtor.prototype
|
|
ctor.prototype = new TempCtor()
|
|
ctor.prototype.constructor = ctor
|
|
}
|
|
}
|
|
|
|
},{}],38:[function(require,module,exports){
|
|
// shim for using process in browser
|
|
|
|
var process = module.exports = {};
|
|
|
|
process.nextTick = (function () {
|
|
var canSetImmediate = typeof window !== 'undefined'
|
|
&& window.setImmediate;
|
|
var canPost = typeof window !== 'undefined'
|
|
&& window.postMessage && window.addEventListener
|
|
;
|
|
|
|
if (canSetImmediate) {
|
|
return function (f) { return window.setImmediate(f) };
|
|
}
|
|
|
|
if (canPost) {
|
|
var queue = [];
|
|
window.addEventListener('message', function (ev) {
|
|
var source = ev.source;
|
|
if ((source === window || source === null) && ev.data === 'process-tick') {
|
|
ev.stopPropagation();
|
|
if (queue.length > 0) {
|
|
var fn = queue.shift();
|
|
fn();
|
|
}
|
|
}
|
|
}, true);
|
|
|
|
return function nextTick(fn) {
|
|
queue.push(fn);
|
|
window.postMessage('process-tick', '*');
|
|
};
|
|
}
|
|
|
|
return function nextTick(fn) {
|
|
setTimeout(fn, 0);
|
|
};
|
|
})();
|
|
|
|
process.title = 'browser';
|
|
process.browser = true;
|
|
process.env = {};
|
|
process.argv = [];
|
|
|
|
function noop() {}
|
|
|
|
process.on = noop;
|
|
process.addListener = noop;
|
|
process.once = noop;
|
|
process.off = noop;
|
|
process.removeListener = noop;
|
|
process.removeAllListeners = noop;
|
|
process.emit = noop;
|
|
|
|
process.binding = function (name) {
|
|
throw new Error('process.binding is not supported');
|
|
}
|
|
|
|
// TODO(shtylman)
|
|
process.cwd = function () { return '/' };
|
|
process.chdir = function (dir) {
|
|
throw new Error('process.chdir is not supported');
|
|
};
|
|
|
|
},{}],39:[function(require,module,exports){
|
|
module.exports = function isBuffer(arg) {
|
|
return arg && typeof arg === 'object'
|
|
&& typeof arg.copy === 'function'
|
|
&& typeof arg.fill === 'function'
|
|
&& typeof arg.readUInt8 === 'function';
|
|
}
|
|
},{}],40:[function(require,module,exports){
|
|
(function (process,global){
|
|
// Copyright Joyent, Inc. and other Node contributors.
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a
|
|
// copy of this software and associated documentation files (the
|
|
// "Software"), to deal in the Software without restriction, including
|
|
// without limitation the rights to use, copy, modify, merge, publish,
|
|
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
|
// persons to whom the Software is furnished to do so, subject to the
|
|
// following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included
|
|
// in all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
|
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
|
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
|
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
var formatRegExp = /%[sdj%]/g;
|
|
exports.format = function(f) {
|
|
if (!isString(f)) {
|
|
var objects = [];
|
|
for (var i = 0; i < arguments.length; i++) {
|
|
objects.push(inspect(arguments[i]));
|
|
}
|
|
return objects.join(' ');
|
|
}
|
|
|
|
var i = 1;
|
|
var args = arguments;
|
|
var len = args.length;
|
|
var str = String(f).replace(formatRegExp, function(x) {
|
|
if (x === '%%') return '%';
|
|
if (i >= len) return x;
|
|
switch (x) {
|
|
case '%s': return String(args[i++]);
|
|
case '%d': return Number(args[i++]);
|
|
case '%j':
|
|
try {
|
|
return JSON.stringify(args[i++]);
|
|
} catch (_) {
|
|
return '[Circular]';
|
|
}
|
|
default:
|
|
return x;
|
|
}
|
|
});
|
|
for (var x = args[i]; i < len; x = args[++i]) {
|
|
if (isNull(x) || !isObject(x)) {
|
|
str += ' ' + x;
|
|
} else {
|
|
str += ' ' + inspect(x);
|
|
}
|
|
}
|
|
return str;
|
|
};
|
|
|
|
|
|
// Mark that a method should not be used.
|
|
// Returns a modified function which warns once by default.
|
|
// If --no-deprecation is set, then it is a no-op.
|
|
exports.deprecate = function(fn, msg) {
|
|
// Allow for deprecating things in the process of starting up.
|
|
if (isUndefined(global.process)) {
|
|
return function() {
|
|
return exports.deprecate(fn, msg).apply(this, arguments);
|
|
};
|
|
}
|
|
|
|
if (process.noDeprecation === true) {
|
|
return fn;
|
|
}
|
|
|
|
var warned = false;
|
|
function deprecated() {
|
|
if (!warned) {
|
|
if (process.throwDeprecation) {
|
|
throw new Error(msg);
|
|
} else if (process.traceDeprecation) {
|
|
console.trace(msg);
|
|
} else {
|
|
console.error(msg);
|
|
}
|
|
warned = true;
|
|
}
|
|
return fn.apply(this, arguments);
|
|
}
|
|
|
|
return deprecated;
|
|
};
|
|
|
|
|
|
var debugs = {};
|
|
var debugEnviron;
|
|
exports.debuglog = function(set) {
|
|
if (isUndefined(debugEnviron))
|
|
debugEnviron = process.env.NODE_DEBUG || '';
|
|
set = set.toUpperCase();
|
|
if (!debugs[set]) {
|
|
if (new RegExp('\\b' + set + '\\b', 'i').test(debugEnviron)) {
|
|
var pid = process.pid;
|
|
debugs[set] = function() {
|
|
var msg = exports.format.apply(exports, arguments);
|
|
console.error('%s %d: %s', set, pid, msg);
|
|
};
|
|
} else {
|
|
debugs[set] = function() {};
|
|
}
|
|
}
|
|
return debugs[set];
|
|
};
|
|
|
|
|
|
/**
|
|
* Echos the value of a value. Trys to print the value out
|
|
* in the best way possible given the different types.
|
|
*
|
|
*/
|
|
/* legacy: obj, showHidden, depth, colors*/
|
|
function inspect(obj, opts) {
|
|
// default options
|
|
var ctx = {
|
|
seen: [],
|
|
stylize: stylizeNoColor
|
|
};
|
|
// legacy...
|
|
if (arguments.length >= 3) ctx.depth = arguments[2];
|
|
if (arguments.length >= 4) ctx.colors = arguments[3];
|
|
if (isBoolean(opts)) {
|
|
// legacy...
|
|
ctx.showHidden = opts;
|
|
} else if (opts) {
|
|
// got an "options" object
|
|
exports._extend(ctx, opts);
|
|
}
|
|
// set default options
|
|
if (isUndefined(ctx.showHidden)) ctx.showHidden = false;
|
|
if (isUndefined(ctx.depth)) ctx.depth = 2;
|
|
if (isUndefined(ctx.colors)) ctx.colors = false;
|
|
if (isUndefined(ctx.customInspect)) ctx.customInspect = true;
|
|
if (ctx.colors) ctx.stylize = stylizeWithColor;
|
|
return formatValue(ctx, obj, ctx.depth);
|
|
}
|
|
exports.inspect = inspect;
|
|
|
|
|
|
// http://en.wikipedia.org/wiki/ANSI_escape_code#graphics
|
|
inspect.colors = {
|
|
'bold' : [1, 22],
|
|
'italic' : [3, 23],
|
|
'underline' : [4, 24],
|
|
'inverse' : [7, 27],
|
|
'white' : [37, 39],
|
|
'grey' : [90, 39],
|
|
'black' : [30, 39],
|
|
'blue' : [34, 39],
|
|
'cyan' : [36, 39],
|
|
'green' : [32, 39],
|
|
'magenta' : [35, 39],
|
|
'red' : [31, 39],
|
|
'yellow' : [33, 39]
|
|
};
|
|
|
|
// Don't use 'blue' not visible on cmd.exe
|
|
inspect.styles = {
|
|
'special': 'cyan',
|
|
'number': 'yellow',
|
|
'boolean': 'yellow',
|
|
'undefined': 'grey',
|
|
'null': 'bold',
|
|
'string': 'green',
|
|
'date': 'magenta',
|
|
// "name": intentionally not styling
|
|
'regexp': 'red'
|
|
};
|
|
|
|
|
|
function stylizeWithColor(str, styleType) {
|
|
var style = inspect.styles[styleType];
|
|
|
|
if (style) {
|
|
return '\u001b[' + inspect.colors[style][0] + 'm' + str +
|
|
'\u001b[' + inspect.colors[style][1] + 'm';
|
|
} else {
|
|
return str;
|
|
}
|
|
}
|
|
|
|
|
|
function stylizeNoColor(str, styleType) {
|
|
return str;
|
|
}
|
|
|
|
|
|
function arrayToHash(array) {
|
|
var hash = {};
|
|
|
|
array.forEach(function(val, idx) {
|
|
hash[val] = true;
|
|
});
|
|
|
|
return hash;
|
|
}
|
|
|
|
|
|
function formatValue(ctx, value, recurseTimes) {
|
|
// Provide a hook for user-specified inspect functions.
|
|
// Check that value is an object with an inspect function on it
|
|
if (ctx.customInspect &&
|
|
value &&
|
|
isFunction(value.inspect) &&
|
|
// Filter out the util module, it's inspect function is special
|
|
value.inspect !== exports.inspect &&
|
|
// Also filter out any prototype objects using the circular check.
|
|
!(value.constructor && value.constructor.prototype === value)) {
|
|
var ret = value.inspect(recurseTimes, ctx);
|
|
if (!isString(ret)) {
|
|
ret = formatValue(ctx, ret, recurseTimes);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
// Primitive types cannot have properties
|
|
var primitive = formatPrimitive(ctx, value);
|
|
if (primitive) {
|
|
return primitive;
|
|
}
|
|
|
|
// Look up the keys of the object.
|
|
var keys = Object.keys(value);
|
|
var visibleKeys = arrayToHash(keys);
|
|
|
|
if (ctx.showHidden) {
|
|
keys = Object.getOwnPropertyNames(value);
|
|
}
|
|
|
|
// IE doesn't make error fields non-enumerable
|
|
// http://msdn.microsoft.com/en-us/library/ie/dww52sbt(v=vs.94).aspx
|
|
if (isError(value)
|
|
&& (keys.indexOf('message') >= 0 || keys.indexOf('description') >= 0)) {
|
|
return formatError(value);
|
|
}
|
|
|
|
// Some type of object without properties can be shortcutted.
|
|
if (keys.length === 0) {
|
|
if (isFunction(value)) {
|
|
var name = value.name ? ': ' + value.name : '';
|
|
return ctx.stylize('[Function' + name + ']', 'special');
|
|
}
|
|
if (isRegExp(value)) {
|
|
return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp');
|
|
}
|
|
if (isDate(value)) {
|
|
return ctx.stylize(Date.prototype.toString.call(value), 'date');
|
|
}
|
|
if (isError(value)) {
|
|
return formatError(value);
|
|
}
|
|
}
|
|
|
|
var base = '', array = false, braces = ['{', '}'];
|
|
|
|
// Make Array say that they are Array
|
|
if (isArray(value)) {
|
|
array = true;
|
|
braces = ['[', ']'];
|
|
}
|
|
|
|
// Make functions say that they are functions
|
|
if (isFunction(value)) {
|
|
var n = value.name ? ': ' + value.name : '';
|
|
base = ' [Function' + n + ']';
|
|
}
|
|
|
|
// Make RegExps say that they are RegExps
|
|
if (isRegExp(value)) {
|
|
base = ' ' + RegExp.prototype.toString.call(value);
|
|
}
|
|
|
|
// Make dates with properties first say the date
|
|
if (isDate(value)) {
|
|
base = ' ' + Date.prototype.toUTCString.call(value);
|
|
}
|
|
|
|
// Make error with message first say the error
|
|
if (isError(value)) {
|
|
base = ' ' + formatError(value);
|
|
}
|
|
|
|
if (keys.length === 0 && (!array || value.length == 0)) {
|
|
return braces[0] + base + braces[1];
|
|
}
|
|
|
|
if (recurseTimes < 0) {
|
|
if (isRegExp(value)) {
|
|
return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp');
|
|
} else {
|
|
return ctx.stylize('[Object]', 'special');
|
|
}
|
|
}
|
|
|
|
ctx.seen.push(value);
|
|
|
|
var output;
|
|
if (array) {
|
|
output = formatArray(ctx, value, recurseTimes, visibleKeys, keys);
|
|
} else {
|
|
output = keys.map(function(key) {
|
|
return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array);
|
|
});
|
|
}
|
|
|
|
ctx.seen.pop();
|
|
|
|
return reduceToSingleString(output, base, braces);
|
|
}
|
|
|
|
|
|
function formatPrimitive(ctx, value) {
|
|
if (isUndefined(value))
|
|
return ctx.stylize('undefined', 'undefined');
|
|
if (isString(value)) {
|
|
var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '')
|
|
.replace(/'/g, "\\'")
|
|
.replace(/\\"/g, '"') + '\'';
|
|
return ctx.stylize(simple, 'string');
|
|
}
|
|
if (isNumber(value))
|
|
return ctx.stylize('' + value, 'number');
|
|
if (isBoolean(value))
|
|
return ctx.stylize('' + value, 'boolean');
|
|
// For some reason typeof null is "object", so special case here.
|
|
if (isNull(value))
|
|
return ctx.stylize('null', 'null');
|
|
}
|
|
|
|
|
|
function formatError(value) {
|
|
return '[' + Error.prototype.toString.call(value) + ']';
|
|
}
|
|
|
|
|
|
function formatArray(ctx, value, recurseTimes, visibleKeys, keys) {
|
|
var output = [];
|
|
for (var i = 0, l = value.length; i < l; ++i) {
|
|
if (hasOwnProperty(value, String(i))) {
|
|
output.push(formatProperty(ctx, value, recurseTimes, visibleKeys,
|
|
String(i), true));
|
|
} else {
|
|
output.push('');
|
|
}
|
|
}
|
|
keys.forEach(function(key) {
|
|
if (!key.match(/^\d+$/)) {
|
|
output.push(formatProperty(ctx, value, recurseTimes, visibleKeys,
|
|
key, true));
|
|
}
|
|
});
|
|
return output;
|
|
}
|
|
|
|
|
|
function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) {
|
|
var name, str, desc;
|
|
desc = Object.getOwnPropertyDescriptor(value, key) || { value: value[key] };
|
|
if (desc.get) {
|
|
if (desc.set) {
|
|
str = ctx.stylize('[Getter/Setter]', 'special');
|
|
} else {
|
|
str = ctx.stylize('[Getter]', 'special');
|
|
}
|
|
} else {
|
|
if (desc.set) {
|
|
str = ctx.stylize('[Setter]', 'special');
|
|
}
|
|
}
|
|
if (!hasOwnProperty(visibleKeys, key)) {
|
|
name = '[' + key + ']';
|
|
}
|
|
if (!str) {
|
|
if (ctx.seen.indexOf(desc.value) < 0) {
|
|
if (isNull(recurseTimes)) {
|
|
str = formatValue(ctx, desc.value, null);
|
|
} else {
|
|
str = formatValue(ctx, desc.value, recurseTimes - 1);
|
|
}
|
|
if (str.indexOf('\n') > -1) {
|
|
if (array) {
|
|
str = str.split('\n').map(function(line) {
|
|
return ' ' + line;
|
|
}).join('\n').substr(2);
|
|
} else {
|
|
str = '\n' + str.split('\n').map(function(line) {
|
|
return ' ' + line;
|
|
}).join('\n');
|
|
}
|
|
}
|
|
} else {
|
|
str = ctx.stylize('[Circular]', 'special');
|
|
}
|
|
}
|
|
if (isUndefined(name)) {
|
|
if (array && key.match(/^\d+$/)) {
|
|
return str;
|
|
}
|
|
name = JSON.stringify('' + key);
|
|
if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) {
|
|
name = name.substr(1, name.length - 2);
|
|
name = ctx.stylize(name, 'name');
|
|
} else {
|
|
name = name.replace(/'/g, "\\'")
|
|
.replace(/\\"/g, '"')
|
|
.replace(/(^"|"$)/g, "'");
|
|
name = ctx.stylize(name, 'string');
|
|
}
|
|
}
|
|
|
|
return name + ': ' + str;
|
|
}
|
|
|
|
|
|
function reduceToSingleString(output, base, braces) {
|
|
var numLinesEst = 0;
|
|
var length = output.reduce(function(prev, cur) {
|
|
numLinesEst++;
|
|
if (cur.indexOf('\n') >= 0) numLinesEst++;
|
|
return prev + cur.replace(/\u001b\[\d\d?m/g, '').length + 1;
|
|
}, 0);
|
|
|
|
if (length > 60) {
|
|
return braces[0] +
|
|
(base === '' ? '' : base + '\n ') +
|
|
' ' +
|
|
output.join(',\n ') +
|
|
' ' +
|
|
braces[1];
|
|
}
|
|
|
|
return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1];
|
|
}
|
|
|
|
|
|
// NOTE: These type checking functions intentionally don't use `instanceof`
|
|
// because it is fragile and can be easily faked with `Object.create()`.
|
|
function isArray(ar) {
|
|
return Array.isArray(ar);
|
|
}
|
|
exports.isArray = isArray;
|
|
|
|
function isBoolean(arg) {
|
|
return typeof arg === 'boolean';
|
|
}
|
|
exports.isBoolean = isBoolean;
|
|
|
|
function isNull(arg) {
|
|
return arg === null;
|
|
}
|
|
exports.isNull = isNull;
|
|
|
|
function isNullOrUndefined(arg) {
|
|
return arg == null;
|
|
}
|
|
exports.isNullOrUndefined = isNullOrUndefined;
|
|
|
|
function isNumber(arg) {
|
|
return typeof arg === 'number';
|
|
}
|
|
exports.isNumber = isNumber;
|
|
|
|
function isString(arg) {
|
|
return typeof arg === 'string';
|
|
}
|
|
exports.isString = isString;
|
|
|
|
function isSymbol(arg) {
|
|
return typeof arg === 'symbol';
|
|
}
|
|
exports.isSymbol = isSymbol;
|
|
|
|
function isUndefined(arg) {
|
|
return arg === void 0;
|
|
}
|
|
exports.isUndefined = isUndefined;
|
|
|
|
function isRegExp(re) {
|
|
return isObject(re) && objectToString(re) === '[object RegExp]';
|
|
}
|
|
exports.isRegExp = isRegExp;
|
|
|
|
function isObject(arg) {
|
|
return typeof arg === 'object' && arg !== null;
|
|
}
|
|
exports.isObject = isObject;
|
|
|
|
function isDate(d) {
|
|
return isObject(d) && objectToString(d) === '[object Date]';
|
|
}
|
|
exports.isDate = isDate;
|
|
|
|
function isError(e) {
|
|
return isObject(e) &&
|
|
(objectToString(e) === '[object Error]' || e instanceof Error);
|
|
}
|
|
exports.isError = isError;
|
|
|
|
function isFunction(arg) {
|
|
return typeof arg === 'function';
|
|
}
|
|
exports.isFunction = isFunction;
|
|
|
|
function isPrimitive(arg) {
|
|
return arg === null ||
|
|
typeof arg === 'boolean' ||
|
|
typeof arg === 'number' ||
|
|
typeof arg === 'string' ||
|
|
typeof arg === 'symbol' || // ES6 symbol
|
|
typeof arg === 'undefined';
|
|
}
|
|
exports.isPrimitive = isPrimitive;
|
|
|
|
exports.isBuffer = require('./support/isBuffer');
|
|
|
|
function objectToString(o) {
|
|
return Object.prototype.toString.call(o);
|
|
}
|
|
|
|
|
|
function pad(n) {
|
|
return n < 10 ? '0' + n.toString(10) : n.toString(10);
|
|
}
|
|
|
|
|
|
var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
|
|
'Oct', 'Nov', 'Dec'];
|
|
|
|
// 26 Feb 16:19:34
|
|
function timestamp() {
|
|
var d = new Date();
|
|
var time = [pad(d.getHours()),
|
|
pad(d.getMinutes()),
|
|
pad(d.getSeconds())].join(':');
|
|
return [d.getDate(), months[d.getMonth()], time].join(' ');
|
|
}
|
|
|
|
|
|
// log is just a thin wrapper to console.log that prepends a timestamp
|
|
exports.log = function() {
|
|
console.log('%s - %s', timestamp(), exports.format.apply(exports, arguments));
|
|
};
|
|
|
|
|
|
exports.inherits = require('inherits');
|
|
|
|
exports._extend = function(origin, add) {
|
|
// Don't do anything if add isn't an object
|
|
if (!add || !isObject(add)) return origin;
|
|
|
|
var keys = Object.keys(add);
|
|
var i = keys.length;
|
|
while (i--) {
|
|
origin[keys[i]] = add[keys[i]];
|
|
}
|
|
return origin;
|
|
};
|
|
|
|
function hasOwnProperty(obj, prop) {
|
|
return Object.prototype.hasOwnProperty.call(obj, prop);
|
|
}
|
|
|
|
}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
|
|
},{"./support/isBuffer":39,"_process":38,"inherits":37}],41:[function(require,module,exports){
|
|
var hasOwn = Object.prototype.hasOwnProperty;
|
|
var toString = Object.prototype.toString;
|
|
var undefined;
|
|
|
|
var isPlainObject = function isPlainObject(obj) {
|
|
'use strict';
|
|
if (!obj || toString.call(obj) !== '[object Object]') {
|
|
return false;
|
|
}
|
|
|
|
var has_own_constructor = hasOwn.call(obj, 'constructor');
|
|
var has_is_property_of_method = obj.constructor && obj.constructor.prototype && hasOwn.call(obj.constructor.prototype, 'isPrototypeOf');
|
|
// Not own constructor property must be Object
|
|
if (obj.constructor && !has_own_constructor && !has_is_property_of_method) {
|
|
return false;
|
|
}
|
|
|
|
// Own properties are enumerated firstly, so to speed up,
|
|
// if last one is own, then all properties are own.
|
|
var key;
|
|
for (key in obj) {}
|
|
|
|
return key === undefined || hasOwn.call(obj, key);
|
|
};
|
|
|
|
module.exports = function extend() {
|
|
'use strict';
|
|
var options, name, src, copy, copyIsArray, clone,
|
|
target = arguments[0],
|
|
i = 1,
|
|
length = arguments.length,
|
|
deep = false;
|
|
|
|
// Handle a deep copy situation
|
|
if (typeof target === 'boolean') {
|
|
deep = target;
|
|
target = arguments[1] || {};
|
|
// skip the boolean and the target
|
|
i = 2;
|
|
} else if ((typeof target !== 'object' && typeof target !== 'function') || target == null) {
|
|
target = {};
|
|
}
|
|
|
|
for (; i < length; ++i) {
|
|
options = arguments[i];
|
|
// Only deal with non-null/undefined values
|
|
if (options != null) {
|
|
// Extend the base object
|
|
for (name in options) {
|
|
src = target[name];
|
|
copy = options[name];
|
|
|
|
// Prevent never-ending loop
|
|
if (target === copy) {
|
|
continue;
|
|
}
|
|
|
|
// Recurse if we're merging plain objects or arrays
|
|
if (deep && copy && (isPlainObject(copy) || (copyIsArray = Array.isArray(copy)))) {
|
|
if (copyIsArray) {
|
|
copyIsArray = false;
|
|
clone = src && Array.isArray(src) ? src : [];
|
|
} else {
|
|
clone = src && isPlainObject(src) ? src : {};
|
|
}
|
|
|
|
// Never move original objects, clone them
|
|
target[name] = extend(deep, clone, copy);
|
|
|
|
// Don't bring in undefined values
|
|
} else if (copy !== undefined) {
|
|
target[name] = copy;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Return the modified object
|
|
return target;
|
|
};
|
|
|
|
|
|
},{}]},{},[1]);
|