diff --git a/examples/autoPanner.html b/examples/autoPanner.html index 31974f3c..4cecf402 100644 --- a/examples/autoPanner.html +++ b/examples/autoPanner.html @@ -7,106 +7,71 @@ - - - - - - - + + + + + + + + -
-
- Auto Panner -
-
- An Auto Panner is a left/right panner with an LFO controlling - the amount of pan. The effect is much more pronounced with headphones. -
-
-
-
- +
+ Auto Panner +
+
+ An Auto Panner is a left/right panner with an LFO controlling + the amount of pan. The effect is much more pronounced with headphones. +
+
+
+
+
+ + \ No newline at end of file diff --git a/examples/buses.html b/examples/buses.html index 62d25580..81c576c0 100644 --- a/examples/buses.html +++ b/examples/buses.html @@ -6,99 +6,89 @@ - - - - - - - + + + + + + + + + -
-
- Buses -
-
- Buses allow you to send signal using named sends and receives. Sometimes - this is advantageous over directly connecting nodes for modularity and loose coupling. -
-
- -
+ +
+ Buses +
+
+ Buses allow you to send signal using named sends and receives. Sometimes + this is advantageous over directly connecting nodes for modularity and loose coupling.
- + - \ No newline at end of file diff --git a/examples/deps/interface.js b/examples/deps/interface.js deleted file mode 100644 index b8da344e..00000000 --- a/examples/deps/interface.js +++ /dev/null @@ -1,4193 +0,0 @@ -/**#Interface -A singleton object holding all widget constructors and a couple of other methods / properties. It is automatically created as soon as interface.js is loaded. -**/ - -/**###Interface.extend : method -This method deep copies all the properties and methods of one object to another. - -param **destination** Object. The object that properties and methods will be inserted into. -param **source** Object. The object providing the properties and methods for copying. -**/ - -/**###Interface.mouseDown : property -Boolean. This property tells whether the left mouse button (in non-touch browsers) is currently pressed. -**/ - -/**###Interface.useTouch : property -Boolean. Whether or not a touch UI browser is being used. -**/ - -/**###Interface.isAndroid : property -Boolean. Whether or not the browser is running under Android. This is used to determine the range of accelerometer values generated. -**/ -var Interface = { - extend : function(destination, source) { - for (var property in source) { - var keys = property.split("."); - - if(source[property] instanceof Array && source[property].length < 100) { // don't copy large array buffers - destination[property] = source[property].slice(0); - } else { - destination[property] = source[property]; - } - } - return destination; - }, - - isAndroid : (function() { - var ua = navigator.userAgent.toLowerCase(); - return ua.indexOf("android") > -1; - })(), - - keyCodeToChar : {8:"Backspace",9:"Tab",13:"Enter",16:"Shift",17:"Ctrl",18:"Alt",19:"Pause/Break",20:"Caps Lock",27:"Esc",32:"Space",33:"Page Up",34:"Page Down",35:"End",36:"Home",37:"Left",38:"Up",39:"Right",40:"Down",45:"Insert",46:"Delete",48:"0",49:"1",50:"2",51:"3",52:"4",53:"5",54:"6",55:"7",56:"8",57:"9",65:"A",66:"B",67:"C",68:"D",69:"E",70:"F",71:"G",72:"H",73:"I",74:"J",75:"K",76:"L",77:"M",78:"N",79:"O",80:"P",81:"Q",82:"R",83:"S",84:"T",85:"U",86:"V",87:"W",88:"X",89:"Y",90:"Z",91:"Windows",93:"Right Click",96:"Numpad 0",97:"Numpad 1",98:"Numpad 2",99:"Numpad 3",100:"Numpad 4",101:"Numpad 5",102:"Numpad 6",103:"Numpad 7",104:"Numpad 8",105:"Numpad 9",106:"Numpad *",107:"Numpad +",109:"Numpad -",110:"Numpad .",111:"Numpad /",112:"F1",113:"F2",114:"F3",115:"F4",116:"F5",117:"F6",118:"F7",119:"F8",120:"F9",121:"F10",122:"F11",123:"F12",144:"Num Lock",145:"Scroll Lock",182:"My Computer",183:"My Calculator",186:";",187:"=",188:",",189:"-",190:".",191:"/",192:"`",219:"[",220:"\\",221:"]",222:"'"}, - - panels : [], - mouseDown : false, - useTouch : 'ontouchstart' in document.documentElement, - widgets : [], -}; - -Interface.Presets = { - dictionary : typeof localStorage.interfacejs === 'undefined' ? {} : JSON.parse( localStorage.interfacejs ), - - save : function(name) { - var preset = []; - for(var h = 0; h < Interface.panels.length; h++) { - var panel = Interface.panels[h]; - preset[h] = []; - for(var i = 0; i < panel.children.length; i++) { - var widget = panel.children[i]; - if(typeof widget.children === 'object') { - var children = []; - for(var j = 0; j < widget.children.length; j++) { - children[j] = widget.children[j].value; - } - preset[h][i] = children; - }else{ - preset[h][i] = widget.value; - } - } - this.dictionary[ name ] = preset; - localStorage.interfacejs = JSON.stringify( this.dictionary ); - } - }, - - load : function(name) { - var preset = this.dictionary[ name ]; - for(var h = 0; h < Interface.panels.length; h++) { - var panel = Interface.panels[h]; - for(var i = 0; i < panel.children.length; i++) { - var widget = panel.children[i]; - if(typeof widget.children === 'object') { - for(var j = 0; j < widget.children.length; j++) { - widget.children[j].setValue( preset[h][i][j] ); - } - }else{ - widget.setValue( preset[h][i] ); - } - } - } - }, - - list : function() { - return Object.keys( this.dictionary ); - }, -}; -/**#Interface.Panel - Widget -A panel is a container for on-screen widgets. There can be multiple panels in a HTML page. Panels are the starting point for event processing in Interface.js. -**/ - -/**###Interface.Panel.children : property -Array. An array of all widgets displayed in the panel -**/ - -/**###Interface.Panel.shouldDraw : property -Boolean. Whenever the panel refreshes itself it will redraw widgets found in this array. -**/ - -/**###Interface.Panel.fps : property -Number. The number of times the panel should refresh itself per second. -**/ - -/**###Interface.Panel.useRelativeSizesAndPositions : property -Boolean. This determines whether widgets in the panel uses sizes/positions relative to the size of the panel or use absolute pixel coordinates. -**/ - -/**###Interface.Panel.container : property -HTMLElement. The HTMLElement (such as a div tag) containing the Panel. -**/ - -/**###Interface.Panel.canvas : property -HTMLElement. The canvas element that the Panel draws onto. This element is created when the panel is initialized. -**/ - -/**###Interface.Panel.touchEvent : method -The starting point for on-screen all touch event handling in a Panel. This method distributes events to all child widgets. - -param **event** HTML Touch Event Object. -**/ - -/**###Interface.Panel.mouseEvent : method -The starting point for on-screen all mouse event handling in a Panel. This method distributes events to all child widgets. - -param **event** HTML Mouse Event Object. -**/ - -/**###Interface.Panel.init : method -Initialization method called automatically when panel is instantiated. -**/ - -/**###Interface.Panel.x : property -Number. The x position of the panel in absolute coordinates relative to the window. -**/ -/**###Interface.Panel.y : property -Number. The y position of the panel in absolute coordinates relative to the window. -**/ -/**###Interface.Panel.width : property -Number. The width of the panel in pixels -**/ -/**###Interface.Panel.width : property -Number. The height of the panel in pixels -**/ - -/**###Interface.Panel.draw : method -This method tells all 'dirty' widgets stored in the shouldDraw property to draw themselves. -**/ -/**###Interface.Panel.refresh : method -Clear the entire panel and then tell all widgets to draw themselves. -**/ - -/**###Interface.Panel.add : method -Add a new widget to the panel - -param **widget** Object. The widget to be added. Motion widgets do not need to be added to the Panel -**/ - -/**###Interface.Panel.setBackgroundColor : method -Set the background color the panel using a css color value. - -param **cssColor** String. Any valid css color, such as 'red', '#f00', or 'rgb(255,0,0)'. -**/ - -/**###Interface.Panel.background : property -String. The default background color for all widgets in the panel. THIS IS NOT THE BACKGROUND COLOR FOR THE PANEL. Any valid css color, such as 'red', '#f00', or 'rgb(255,0,0)' can be assigned to this property. -**/ -/**###Interface.Panel.fill : property -String. The default fill color for all widgets in the panel. Any valid css color, such as 'red', '#f00', or 'rgb(255,0,0)' can be assigned to this property. -**/ -/**###Interface.Panel.stroke : property -String. The default stroke color for all widgets in the panel. Any valid css color, such as 'red', '#f00', or 'rgb(255,0,0)' can be assigned to this property. -**/ -Interface.Panel = function() { - var self = this, - _container = arguments.length >= 1 ? arguments[0].container : undefined; - - Interface.extend(this, { - type: 'Panel', - active: true, - children: [], - shouldDraw : [], - fps : 30, - useRelativeSizesAndPositions : true, - labelSize: '12px', - font:'normal 12px Helvetica', - serializeMe: ['fps', 'useRelativeSizesAndPositions', 'labelSize', 'font', 'background', 'fill', 'stroke', 'backgroundColor'], - - container: (function() { - if(typeof _container === 'undefined') { - $('body').css({ - margin : 0, - padding: 0, - }); - - var d = $('
'); - d.css({ - width:$(window).width(), - height:$(window).height(), - display:'block', - margin:0, - padding:0, - position:'absolute', - left:0, - top:0 - }); - - $('body').append(d); - - return d; - }else{ - return _container; - } - })(), - - canvas: document.createElement('canvas'), - - touchEvent : function(event) { - if(self.active) { - if( typeof event.changedTouches === 'undefined' && event.originalEvent ) { - event.changedTouches = event.originalEvent.changedTouches - } - for (var j = 0; j < event.changedTouches.length; j++){ - var touch = event.changedTouches.item(j); - - for(var i = 0; i < self.children.length; i++) { - touch.x = touch.pageX - self.x; - touch.y = touch.pageY - self.y; - touch.type = event.type; - self.children[i].touchEvent(touch); - } - //var breakCheck = this.events[event.type].call(this, touch); - - //if(breakCheck) break; - } - event.preventDefault(); // HTML Elements must simulate touch events in their touchEvent method - } - }, - - mouseEvent : function(e) { - if(self.active) { - if(e.type === 'mousedown') { - Interface.mouseDown = true; - }else if(e.type === 'mouseup') { - Interface.mouseDown = false; - } - - var event = { - x : e.offsetX || (e.pageX - self.x), // pageX and pageY is for firefox compatibility - y : e.offsetY || (e.pageY - self.y), - type: e.type, - } - //console.log("MOUSE", event, self.y, e.pageY, e.layerY, e.clientY, e ); - - for(var i = 0; i < self.children.length; i++) { - self.children[i].mouseEvent(event); - } - } - }, - - init : function() { - var offset = $(this.container).offset(); - this.width = $(this.container).width(); - this.height = $(this.container).height(); - this.x = offset.left; - this.y = offset.top; - - if( isNaN(this.x) ) this.x = 0; - if( isNaN(this.y) ) this.y = 0; - - $(this.canvas).attr({ - 'width': this.width, - 'height': this.height, - }); - - $(this.container).css({ 'user-select': 'none', '-webkit-user-select': 'none'}); - - $(this.container).append(this.canvas); - - this.ctx = this.canvas.getContext( '2d' ); - this.ctx.translate(0.5, 0.5); - this.ctx.lineWidth = 1; - - if(Interface.useTouch) { - $(this.container).on( 'touchstart', this.touchEvent ); - $(this.container).on( 'touchmove', this.touchEvent ); - $(this.container).on( 'touchend', this.touchEvent ); - }else{ - $(this.container).on( 'mousedown', this.mouseEvent ); - $(this.container).on( 'mousemove', this.mouseEvent ); - $(this.container).on( 'mouseup', this.mouseEvent ); - } - - $( this.container ).css({ outline: 'none' }) - $( this.container ).attr( 'tabindex', 5 ) - $( this.container ).on( 'keydown', this.keydown.bind( this ) ) - $( this.container ).on( 'keyup', this.keyup.bind( this ) ) - }, - - keydown: function(e) { - for( var i = 0; i < this.children.length; i++ ) { - if( this.children[i].onkeydown ) this.children[i].onkeydown(e) - } - }, - - keyup: function(e) { - for( var i = 0; i < this.children.length; i++ ) { - if( this.children[i].onkeyup ) this.children[i].onkeyup(e) - } - }, - - draw : function() { - if(this.active) { - for(var i = 0; i < this.shouldDraw.length; i++) { - this.shouldDraw[i].draw(); - } - this.shouldDraw.length = 0; - } - $.publish('/draw') - }, - - getWidgetWithName: function( name ) { - for(var i = 0; i < this.children.length; i++) { - if( this.children[i].name === name) { - return this.children[i]; - } - } - }, - - redoBoundaries : function() { - var offset = $(this.container).offset(); - this.width = $(this.container).width(); - this.height = $(this.container).height(); - this.x = offset.left; - this.y = offset.top; - - if( isNaN(this.x) ) this.x = 0; - if( isNaN(this.y) ) this.y = 0; - - $(this.canvas).attr({ - 'width': this.width, - 'height': this.height, - }); - - this.ctx.translate(0.5, 0.5); - this.ctx.lineWidth = 1; - - this.refresh(); - }, - - refresh: function() { - if(this.active) { - this.ctx.clearRect(0,0,this.canvas.width, this.canvas.height); - for(var i = 0; i < this.children.length; i++) { - this.children[i].draw(); - } - } - }, - - add: function() { - for(var i = 0; i < arguments.length; i++) { - var widget = arguments[i]; - - widget.panel = this; - widget.canvas = this.canvas; - widget.container = this.container; - widget.ctx = this.ctx; - - this.children.push( widget ); - if(widget._init && !widget.added) widget._init(); - - if(widget.oninit && !widget.added) widget.oninit(); - - widget.draw(); - widget.added = true; - - if(typeof widget.add === 'function') widget.add(); - } - }, - - clear : function() { - this.ctx.clearRect( 0,0,this.width,this.height ); - this.children.length = 0; - }, - remove: function(widget) { - this.ctx.clearRect( widget._x(), widget._y(), widget._width(), widget._height() ); - - if(typeof widget.children !== 'undefined' && widget.type !== "XY") { - for(var i = 0; i < widget.children.length; i++) { - this.children.splice( this.children.indexOf( widget.children[i] ), 1 ); - Interface.widgets.splice( Interface.widgets.indexOf( widget.children[i] ), 1 ); - } - }else{ - if(this.children.indexOf( widget ) > -1) { - this.children.splice( this.children.indexOf( widget ), 1 ); - Interface.widgets.splice( Interface.widgets.indexOf( widget ), 1 ); - if(typeof widget.remove === 'function') widget.remove(); - } - } - }, - }); - - if(typeof arguments[0] !== 'undefined') Interface.extend(this, arguments[0]); - if(this.backgroundColor) this.setBackgroundColor( this.backgroundColor ); - - this.init(); - - this.timer = setInterval( function() { self.draw(); }, Math.round(1000 / this.fps) ); - - var childBackground ='#000', - childFill = '#666', - childStroke = '#999', - background = 'transparent', - self = this, - useRelativeSizesAndPositions = this.useRelativeSizesAndPositions; - - Object.defineProperties(this, { - 'useRelativeSizesAndPositions' : { - get: function() { return useRelativeSizesAndPositions; }, - set: function(val) { - if(val !== useRelativeSizesAndPositions) { - useRelativeSizesAndPositions = val; - if(val === false) { - for(var i = 0; i < this.children.length; i++) { - var child = this.children[i]; - child.bounds = [ - Math.round( child.x * this.width ), - Math.round( child.y * this.height ), - Math.round( child.width * this.width ), - Math.round( child.height * this.height ), - ]; - } - }else{ - for(var i = 0; i < this.children.length; i++) { - var child = this.children[i]; - child.bounds = [ - child.x / this.width, - child.y / this.height, - child.width / this.width, - child.height / this.height, - ]; - } - } - } - this.refresh(); - }, - }, - 'background': { - get: function() { return background; }, - set: function(val) { - background = val; - $(this.container).css({ backgroundColor:background }); - }, - }, - 'childBackground': { - get: function() { return childBackground; }, - set: function(val) { - childBackground = val; - self.refresh(); - }, - }, - 'childStroke': { - get: function() { return childStroke; }, - set: function(val) { - childStroke = val; - self.refresh(); - }, - }, - 'childFill': { - get: function() { return childFill; }, - set: function(val) { - childFill = val; - self.refresh(); - }, - } - }); - if(arguments[0]) { - if(arguments[0].childBackground) this.childBackground = arguments[0].childBackground; - if(arguments[0].childFill) this.childFill = arguments[0].childFill; - if(arguments[0].childStroke) this.childStroke = arguments[0].childStroke; - } - Interface.panels.push( this ); -}; - -var convertMouseEvent = function(eventName) { - switch(eventName) { - case 'mousedown': - return 'touchmousedown'; - case 'mousemove': - return 'touchmousemove'; - case 'mouseup': - return 'touchmouseup'; - default: - return eventName; - } -}; - -var convertTouchEvent = function(eventName) { - switch(eventName) { - case 'touchstart': - return 'touchmousedown'; - case 'touchmove': - return 'touchmousemove'; - case 'touchend': - return 'touchmouseup'; - default: - return eventName; - } -}; - -/**#Interface.Widget - Widget -The prototype object for all Interface.js widgets. These methods and properties are inherited by all widgets. -**/ - -/**###Interface.Widget.x : property -Number. The horizontal position of the widget inside of its parent panel. By default, this position is determined relative to the size of the widget's containing panel, but absolute values can also be used if the panel's useRelativeSizesAndPositions property is set to false. -**/ -/**###Interface.Widget.y : property -Number. The vertical position of the widget inside of its parent panel. By default, this position is determined relative to the size of the widget's containing panel, but absolute values can also be used if the panel's useRelativeSizesAndPositions property is set to false. -**/ -/**###Interface.Widget.width : property -Number. The width of the widget. By default, this is determined relative to the size of the widget's containing panel, but absolute values can also be used if the panel's useRelativeSizesAndPositions property is set to false. -**/ -/**###Interface.Widget.height : property -Number. The width of the widget. By default, this is determined relative to the size of the widget's containing panel, but absolute values can also be used if the panel's useRelativeSizesAndPositions property is set to false. -**/ -/**###Interface.Widget.bounds : property -Array. A shorthand to set x,y,width and height simultaneously upon initialization. By default, these values are determined relative to the size of the widget's containing panel, but absolute values can also be used if the panel's useRelativeSizesAndPositions property is set to false. -**/ -/**###Interface.Widget.min : property -Number. Default 0. The minimum value the widget should output. -**/ -/**###Interface.Widget.max : property -Number. Default 1. The maximum value the widget should output. -**/ -/**###Interface.Widget.ontouchstart : method -Function. A user defined event handler for whenever a touch begins over a widget. -**/ -/**###Interface.Widget.ontouchmove : method -Function. A user defined event handler for whenever a touch moves over a widget. -**/ -/**###Interface.Widget.ontouchend : method -Function. A user defined event handler for whenever a touch ends. -**/ -/**###Interface.Widget.onmousedown : method -Function. A user defined event handler for whenever a mouse press occurs over a widget. -**/ -/**###Interface.Widget.onmousemove : method -Function. A user defined event handler for whenever a mouse moves over a widget while its button is pressed. -**/ -/**###Interface.Widget.onmouseup : method -Function. A user defined event handler for whenever a mouse press ends. -**/ -/**###Interface.Widget.ontouchmousedown : method -Function. A user defined event handler for whenever a mouse press or touch occurs over a widget. -**/ -/**###Interface.Widget.ontouchmousemove : method -Function. A user defined event handler for whenever a mouse or touch moves over a widget. -**/ -/**###Interface.Widget.ontouchmouseup : method -Function. A user defined event handler for whenever a mouse press ends or a touch leaves the screen. -**/ - -/**###Interface.Widget.init : method -This method is called as soon as widgets are created. It copies properties passed in the constructor to the widget and also copies some default property values. - -param **options** Object. A dictionary of options for the widget to be initilized with. -**/ -/**###Interface.Widget.refresh : method -Tell the widget to redraw itself. This method adds the widget to the shouldDraw array of the parent panel. -**/ -/**###Interface.Widget.setValue : method -Programmatically change the value of the widget. You can optionally not have the widget redraw itself when calling this method. - -param **value** Number or String. The new value for the widget. -param **doNotDraw** Optional, default false. Whether or not the widget should redraw itself. -**/ -/**###Interface.Widget.hitTest : method -Given an HTML touch or mouse event, determine if the event overlaps a graphical widget. - -param **event** HTMLEvent. The touch or mouse event to check -**/ -/**###Interface.Widget.hitTest : method -Given an HTML touch or mouse event, determine if the event overlaps a graphical widget. - -param **event** HTMLEvent. The touch or mouse event to check -**/ -/**###Interface.Widget.draw : method -Tell the widget to draw itself. This method must be overridden by every graphical widget. -**/ -/**###Interface.Widget.mouseEvent : method -The default mouse event handler for the widget. This method also calls any user defined mouse event handlers. This method should probably never be called manually, but you might want to override it. - -param **event** HTMLEvent. The mouse event to process -**/ -/**###Interface.Widget.mouseEvent : method -The default touch event handler for the widget. This method also calls any user defined touch event handlers. This method should probably never be called manually, but you might want to override it. - -param **event** HTMLEvent. The touch event to process -**/ -/**###Interface.Widget.sendTargetMessage : method -If the widget has a target and key property, set the key property or call the key method on the target using the widgets current value. -**/ -/**###Interface.Widget._background : method -returns Color. If the widget has a background color specified, return that, otherwise return the background color of the widget's parent panel. -**/ -/**###Interface.Widget._stroke : method -returns Color. If the widget has a stroke color specified, return that, otherwise return the stroke color of the widget's parent panel. -**/ -/**###Interface.Widget._fill : method -returns Color. If the widget has a fill color specified, return that, otherwise return the fill color of the widget's parent panel. -**/ - -/**###Interface.Widget._x : method -returns Number. Return the widget's x position as a pixel value relative to the position of the panel. Note that this method will always return the pixel value, even if the panel uses relative values to determine sizes and positions. -**/ -/**###Interface.Widget._y : method -returns Number. Return the widget's y position as a pixel value relative to the position of the panel. Note that this method will always return the pixel value, even if the panel uses relative values to determine sizes and positions. -**/ -/**###Interface.Widget._width : method -returns Number. Return the widget's width. Note that this method will always return a size in pixels, even if the panel uses relative values to determine sizes and positions. -**/ -/**###Interface.Widget._height : method -returns Number. Return the widget's height. Note that this method will always return a size in pixels, even if the panel uses relative values to determine sizes and positions. -**/ -var __widgetCount = 0; -var widgetDefaults = { - hasFocus : false, - requiresFocus : true, - min : 0, - max : 1, - value : 0, - lastValue : null, - name : null, - events : { - ontouchstart : null, - ontouchmove : null, - ontouchend : null, - onmousedown : null, - onmousemove : null, - onmouseup : null, - ontouchmousedown : null, - ontouchmousemove : null, - ontouchmouseup : null, - onvaluechange : null, - onboundschange : null, - }, -} -Interface.Widget = { - init : function( options ) { - this.added = false; - Interface.extend( this, widgetDefaults); - if( typeof options === 'undefined' ) options = {} - - this.name = options.name || this.type + "_" + __widgetCount++; - this.target = "OSC"; - this.key = "/" + this.name; - - Interface.extend( this, options); - - if(this.bounds) { - this.x = options.bounds[0]; - this.y = options.bounds[1]; - this.width = options.bounds[2]; - this.height = options.bounds[3]; - } - - if(this.colors) { - this.background = options.colors[0]; - this.fill = options.colors[1]; - this.stroke = options.colors[2]; - } - - this.focusedTouches = []; - - if(this.value) this.setValue(this.value, true); - - var bounds = this.bounds || [this.x, this.y, this.width, this.height], - x = this.x, y = this.y, width = this.width, height = this.height, value = this.value; - - Object.defineProperties(this, { - bounds : { - configurable: true, - get : function() { return bounds; }, - set : function(_bounds) { - bounds = _bounds; this.x = bounds[0]; this.y = bounds[1]; this.width = bounds[2]; this.height = bounds[3]; - if( this.onboundschange ) this.onboundschange() - } - }, - x : { - configurable: true, - get : function() { return x; }, - set : function(val) { this.clear(); x = val; if( this.onboundschange ) this.onboundschange(); this.refresh(); }, - }, - y : { - configurable: true, - get : function() { return y; }, - set : function(val) { this.clear(); y = val; if( this.onboundschange ) this.onboundschange(); this.refresh(); }, - }, - width : { - configurable: true, - get : function() { return width; }, - set : function(val) { this.clear(); width = val; if( this.onboundschange ) this.onboundschange(); this.refresh(); }, - }, - height : { - configurable: true, - get : function() { return height; }, - set : function(val) { this.clear(); height = val; if( this.onboundschange ) this.onboundschange(); this.refresh(); }, - }, - /*value : { - configurable: true, - get : function() { return value; }, - set : function(val) { if(value !== val) { value = val; this.refresh(); } }, - },*/ - }); - - Interface.widgets.push( this ); - }, - - clear : function() { - if( this.panel ) { // must check in case widget is Acc or Gyro - this.panel.ctx.clearRect( this._x(), this._y(), this._width(), this._height() ); - } - }, - - refresh : function() { - if(this.panel && this.panel.shouldDraw.indexOf(this) === -1) { - this.panel.shouldDraw.push(this); - } - }, - - setValue : function(value, doNotDraw) { - var r = this.max - this.min, - v = value; - - this.value = value; - - if(this.min !== 0 || this.max !== 1) { - v -= this.min; - this._value = v / r; - }else{ - this._value = this.value; - } - - if(!doNotDraw) this.refresh(); - }, - - hitTest : function(e) { - if(e.x >= this._x() && e.x <= this._x() + this._width()) { - if(e.y >= this._y() && e.y <= this._y() + this._height()) { - return true; - } - } - - return false; - }, - - mouseEvent : function(e) { - var isHit = this.hitTest(e); - var touchMouseName = convertMouseEvent(e.type); - - if(isHit || this.hasFocus || !this.requiresFocus) { - if(e.type === 'mousedown') this.hasFocus = true; - - if(this[e.type]) this[e.type](e, isHit); // normal event - - if(this['on'+e.type]) this['on'+e.type](e, isHit); // user defined event - if(this['on'+touchMouseName]) this['on'+touchMouseName](e, isHit); // user defined event - } - if(e.type === 'mouseup') this.hasFocus = false; - }, - - touchEvent : function(touch) { // event type is stored in touch by Panel - var isHit = this.hitTest(touch); - var touchMouseName = convertTouchEvent(touch.type); - if(isHit || this.hasFocus || !this.requiresFocus) { - if(touch.type === 'touchstart') { - this.focusedTouches.push(touch); - this.hasFocus = true; - } - if(this[touch.type]) - this[touch.type](touch, isHit); // normal event - - if(this['on'+touch.type]) this['on'+touch.type](touch, isHit); // user defined event - if(this['on'+touchMouseName]) this['on'+touchMouseName](touch, isHit); // user defined event - } - if(touch.type === 'touchend') { - for(var i = 0; i < this.focusedTouches.length; i++) { - if(this.focusedTouches[i].id === touch.id) { - this.focusedTouches.splice(i, 1); - if(this.focusedTouches.length === 0) this.hasFocus = false; - break; - } - } - } - }, - - draw : function() {}, - - sendTargetMessage : function() { - if(this.target && this.key) { - if(this.target === "OSC") { - if(Interface.OSC) { - if(typeof this.values === 'undefined') { - var tt = typeof this.value === 'string' ? 's' : 'f'; - Interface.OSC.send(this.key, tt, [ this.value ] ); - }else{ - if(typeof this.sendValues === 'undefined') { - var tt = ''; - for(var i = 0; i < this.values.length; i++) { - tt += typeof this.value === 'string' ? 's' : 'f'; - } - Interface.OSC.send( this.key, tt, this.values ); - }else{ - this.sendValues(); - } - } - } - }else if(this.target === "MIDI") { - if(Interface.MIDI && typeof this.values === 'undefined') { - Interface.MIDI.send( this.key[0],this.key[1],this.key[2], this.value ) - } - }else{ - if(typeof this.target[this.key] === 'function') { - this.target[this.key]( this.values || this.value ); - }else{ - this.target[this.key] = this.values || this.value; - } - } - } - }, - - _background : function() { return this.background || this.panel.childBackground; }, - _stroke : function() { return this.stroke || this.panel.childStroke; }, - _fill : function() { return this.fill || this.panel.childFill; }, - - _x : function() { return this.panel.useRelativeSizesAndPositions ? Math.floor(this.x * this.panel.width) : this.x; }, - _y : function() { return this.panel.useRelativeSizesAndPositions ? Math.floor(this.y * this.panel.height) : this.y; }, - _width : function() { return this.panel.useRelativeSizesAndPositions ? Math.floor(this.width * this.panel.width) : this.width; }, - _height : function() { return this.panel.useRelativeSizesAndPositions ? Math.floor(this.height * this.panel.height) : this.height; }, - - _font : function() { - var font = this.font || this.panel.font; - - return font; - }, - label:null, - - _serializeMe : [ - "background", "stroke", "fill", "x", "y", "width", "height", "value", - "label", "onmousedown", "onmousemove", "onmouseup", "ontouchmousedown", "ontouchmousemove", "ontouchmouseup", - "ontouchstart", "ontouchmove", "ontouchend", "onvaluechange", "name", "type", "target", "key" - ], -}; - - -Interface.HBox = function() { - var me = this - Interface.extend(this, { - type : 'HBox', - - children: [], - proxyPanel : { active:true, x:0, y:0, width:1, height:1, shouldDraw:[], useRelativeSizesAndPositions:true }, // needed for absolute widths / heights which are set in _init call - - add: function() { - for( var i = 0; i < arguments.length; i++ ) { - var child = arguments[ i ] - if( this.children.indexOf( child ) === -1 ) this.children.push( child ) - child.panel = this.proxyPanel - child.ctx = this.panel.ctx - } - - this.layout() - this.draw() - }, - - layout : function() { - var w = (this.width / this.children.length) / this.width, - _widthUsed = 0; - - for( var i = 0; i < this.children.length; i++ ) { - var child = this.children[ i ] - - child.x = _widthUsed + this.x - child.y = this.y * (1 / this.height) - - child.width = w - child.height = 1 - - _widthUsed += w - } - return this - }, - - draw: function() { - this.proxyPanel.width = this._width() - this.proxyPanel.height = this._height() - - for( var i = 0; i < this.children.length; i++ ) { - this.children[ i ].draw() - } - }, - - refresh: function() { - for( var i = 0; i < this.proxyPanel.shouldDraw.length; i++ ) { - this.proxyPanel.shouldDraw[ i ].draw() - } - this.proxyPanel.shouldDraw.length = 0 - }, - - mouseEvent: function(e){ - // e.x -= this._x() - // e.y -= this._y() - for( var i = 0; i < this.children.length; i++ ) { - var child = this.children[ i ] - - this.children[ i ].mouseEvent( e ) - } - }, - - touchEvent: function(e){ - // e.x -= this._x() - // e.y -= this._y() - for( var i = 0; i < this.children.length; i++ ) { - var child = this.children[ i ] - - this.children[ i ].touchEvent( e ) - } - }, - - _init: function() { - this.useRelativeSizesAndPositions = this.panel.useRelativeSizesAndPositions - this.proxyPanel.width = this._width() - this.proxyPanel.height = this._height() - this.proxyPanel.__proto__ = this.panel - - $.subscribe('/draw', this.refresh.bind( this ) ) - - Object.defineProperties(this, { - bounds : { - configurable: true, - get : function() { return bounds; }, - set : function(_bounds) { - bounds = _bounds; this.x = bounds[0]; this.y = bounds[1]; this.width = bounds[2]; this.height = bounds[3]; - this.layout() - this.draw() - } - }, - }) - } - }) - .init( arguments[0] ) -}; -Interface.HBox.prototype = Interface.Widget; - -Interface.VBox = function() { - Interface.extend(this, { - type : 'VBox', - - children: [], - proxyPanel : { active:true, x:0, y:0, width:1, height:1, shouldDraw:[], useRelativeSizesAndPositions:true }, // needed for absolute widths / heights which are set in _init call - - add: function() { - for( var i = 0; i < arguments.length; i++ ) { - var child = arguments[ i ] - this.children.push( child ) - child.panel = this.proxyPanel - child.ctx = this.panel.ctx - } - - this.layout() - this.draw() - }, - - layout : function() { - var h = this.height / this.children.length, - _heightUsed = 0; - - for( var i = 0; i < this.children.length; i++ ) { - var child = this.children[ i ] - - child.x = this.x - child.y = (this.y + _heightUsed ) * (1 / this.height) - // - child.width = 1 - child.height = h * ( 1/this.height) - - _heightUsed += h - } - - return this - }, - - draw: function() { - this.proxyPanel.width = this._width() - this.proxyPanel.height = this._height() - - for( var i = 0; i < this.children.length; i++ ) { - this.children[ i ].draw() - } - }, - - refresh: function() { - for( var i = 0; i < this.proxyPanel.shouldDraw.length; i++ ) { - this.proxyPanel.shouldDraw[ i ].draw() - } - this.proxyPanel.shouldDraw.length = 0 - }, - - mouseEvent: function(e){ - // e.x -= this._x() - // e.y -= this._y() - for( var i = 0; i < this.children.length; i++ ) { - var child = this.children[ i ] - - this.children[ i ].mouseEvent( e ) - } - }, - - touchEvent: function(e){ - e.x -= this._x() - e.y -= this._y() - for( var i = 0; i < this.children.length; i++ ) { - var child = this.children[ i ] - - this.children[ i ].touchEvent( e ) - } - }, - - _init: function() { - this.useRelativeSizesAndPositions = this.panel.useRelativeSizesAndPositions - this.proxyPanel.width = this._width() - this.proxyPanel.height = this._height() - this.proxyPanel.__proto__ = this.panel - - $.subscribe('/draw', this.refresh.bind( this ) ) - - Object.defineProperties(this, { - bounds : { - configurable: true, - get : function() { return bounds; }, - set : function(_bounds) { - bounds = _bounds; this.x = bounds[0]; this.y = bounds[1]; this.width = bounds[2]; this.height = bounds[3]; - this.layout() - this.draw() - } - }, - }) - } - }) - .init( arguments[0] ) -}; -Interface.VBox.prototype = Interface.Widget; - - -/**#Interface.Slider - Widget -A vertical or horizontal slider. - -## Example Usage## -`a = new Interface.Slider({ bounds:[0,0,1,.2], isVertical:false }); -panel = new Interface.Panel(); -panel.add(a); -` -## Constructor -**param** *properties*: Object. A dictionary of property values (see below) to set for the slider on initialization. -- - - - -**/ -/**###Interface.Slider.isVertical : property -Boolean. Whether or not the slider draws itself vertically or horizontally. Note this does not affect the boundaries of the slider, just the orientation of the slider's movement. -**/ - -Interface.Slider = function() { - Interface.extend(this, { - type : 'Slider', - isVertical : true, - serializeMe : ["isVertical"], - - draw : function() { - var x = this._x(), - y = this._y(), - width = this._width(), - height= this._height(); - - this.ctx.fillStyle = this._background(); - this.ctx.fillRect( x, y, width, height ); - - this.ctx.fillStyle = this._fill(); - - if(this.isVertical) { - this.ctx.fillRect( x, y + height - this._value * height, width, this._value * height); - }else{ - this.ctx.fillRect( x, y, width * this._value, height); - } - - if(this.label) { - this.ctx.fillStyle = this._stroke(); - this.ctx.textBaseline = 'middle'; - this.ctx.textAlign = 'center'; - this.ctx.font = this._font(); - this.ctx.fillText(this.label, x + width / 2, y + height / 2); - } - - this.ctx.strokeStyle = this._stroke(); - this.ctx.strokeRect( x, y, width, height ); - }, - - changeValue : function( xOffset, yOffset ) { - if(this.hasFocus || !this.requiresFocus) { - - this._value = this.isVertical ? 1 - (yOffset / this._height()) : xOffset / this._width(); - - if(this._value < 0) { - this._value = 0; - // this.hasFocus = false; - }else if(this._value > 1) { - this._value = 1; - // this.hasFocus = false; - } - - this.value = this.min + (this.max - this.min) * this._value; - - if(this.value !== this.lastValue) { - this.sendTargetMessage(); - if(this.onvaluechange) this.onvaluechange(); - this.refresh(); - this.lastValue = this.value; - } - } - }, - - mousedown : function(e, hit) { if(hit && Interface.mouseDown) this.changeValue( e.x - this._x(), e.y - this._y() ); }, - mousemove : function(e, hit) { if(hit && Interface.mouseDown) this.changeValue( e.x - this._x(), e.y - this._y() ); }, - mouseup : function(e, hit) { if(hit && Interface.mouseDown) this.changeValue( e.x - this._x(), e.y - this._y() ); }, - - touchstart : function(e, hit) { if(hit) this.changeValue( e.x - this._x(), e.y - this._y() ); }, - touchmove : function(e, hit) { if(hit) this.changeValue( e.x - this._x(), e.y - this._y() ); }, - touchend : function(e, hit) { if(hit) this.changeValue( e.x - this._x(), e.y - this._y() ); }, - }) - .init( arguments[0] ); -}; -Interface.Slider.prototype = Interface.Widget; - -/**#Interface.Crossfader - Widget -A horizontal crossfader. - -## Example Usage## -`a = new Interface.Crossfader({ bounds:[0,0,1,.2], crossfaderWidth:20 }); -panel = new Interface.Panel(); -panel.add(a); -` -## Constructor -**param** *properties*: Object. A dictionary of property values (see below) to set for the crossfader on initialization. -- - - - -**/ -/**###Interface.Crossfader.crossfaderWidth : property -Boolean. The width of the rectangle indicating the current position of the crossfader, in pixel values. TODO: use relative values when used by the panel. -**/ -Interface.Crossfader = function() { - Interface.extend(this, { - type : 'Crossfader', - crossfaderWidth: 30, - serializeMe : ["crossfaderWidth"], - - _value : .5, - - draw : function() { - var x = this._x(), - y = this._y(), - width = this._width(), - height= this._height(); - - this.ctx.fillStyle = this._background(); - this.ctx.fillRect( x, y, width, height ); - - this.ctx.fillStyle = this._fill(); - this.ctx.fillRect( x + (width - this.crossfaderWidth) * this._value, y, this.crossfaderWidth, height); - - this.ctx.strokeStyle = this._stroke(); - this.ctx.strokeRect( x, y, width, height ); - }, - - changeValue : function( xOffset, yOffset ) { - if(this.hasFocus || !this.requiresFocus) { - this._value = xOffset / this._width(); - - if(this._value < 0) { - this._value = 0; - //this.hasFocus = false; - }else if(this._value > 1) { - this._value = 1; - //this.hasFocus = false; - } - - this.value = this.min + (this.max - this.min) * this._value; - - if(this.value !== this.lastValue) { - this.sendTargetMessage(); - if(this.onvaluechange) this.onvaluechange(); - this.refresh(); - this.lastValue = this.value; - } - } - }, - - mousedown : function(e) { this.changeValue( e.x - this._x(), e.y - this._y() ); }, - mousemove : function(e) { this.changeValue( e.x - this._x(), e.y - this._y() ); }, - mouseup : function(e) { this.changeValue( e.x - this._x(), e.y - this._y() ); }, - - touchstart : function(e, hit) { if(hit) this.changeValue( e.x - this._x(), e.y - this._y() ); }, - touchmove : function(e, hit) { if(hit) this.changeValue( e.x - this._x(), e.y - this._y() ); }, - touchend : function(e, hit) { if(hit) this.changeValue( e.x - this._x(), e.y - this._y() ); }, - }) - .init( arguments[0] ); -}; -Interface.Crossfader.prototype = Interface.Widget; - -/**#Interface.Button - Widget -A button with a variety of on/off modes - -## Example Usage## -`a = new Interface.Button({ bounds:[0,0,.25,.25], mode:'contact', label:'test' }); -panel = new Interface.Panel(); -panel.add(a); -` -## Constructor -**param** *properties*: Object. A dictionary of property values (see below) to set for the button on initialization. -- - - - -**/ -/**###Interface.Button.mode : property -String. Can be 'toggle', 'momentary' or 'contact'. In toggle mode, the button turns on when it is pressed and off when it is pressed again. In momentary mode, the button turns on when pressed and off when released. In contact mode, the button briefly flashes when pressed and sends its value. -**/ -/**###Interface.Button.label : property -String. A text label to print in the center of the button. -**/ -Interface.Button = function() { - Interface.extend(this, { - type : 'Button', - _value: 0, - serializeMe : ["mode", "label"], - - mode : 'toggle', - isMouseOver : false, - isTouchOver : false, - label : null, - requiresFocus : false, - - draw : function() { - var x = this._x(), - y = this._y(), - width = this._width(), - height= this._height(); - - if(this._value) { - this.ctx.fillStyle = this._fill(); - }else{ - this.ctx.fillStyle = this._background(); - } - this.ctx.fillRect( x, y, width, height ); - - if(this.label !== null) { - this.ctx.fillStyle = this._stroke(); - this.ctx.textBaseline = 'middle'; - this.ctx.textAlign = 'center'; - this.ctx.font = this._font(); - this.ctx.fillText(this.label, x + width / 2, y + height / 2); - } - - this.ctx.strokeStyle = this._stroke(); - this.ctx.strokeRect( x, y, width, height ); - }, - - changeValue : function( xOffset, yOffset ) { - if(this.hasFocus || !this.requiresFocus) { - this._value = !this._value; - - this.value = this._value ? this.max : this.min; - - if(this.value !== this.lastValue || this.mode === 'contact') { - this.sendTargetMessage(); - if(this.onvaluechange) this.onvaluechange(); - this.draw(); - this.lastValue = this.value; - } - } - }, - setValue : function(value, doNotDraw) { - var r = this.max - this.min, - v = value; - - this.value = value; - - if(this.min !== 0 || this.max !== 1) { - v -= this.min; - this._value = v / r; - }else{ - this._value = this.value; - } - this.lastValue = this.value; - if(!doNotDraw && this.mode !== 'contact') this.refresh(); - }, - - mousedown : function(e, hit) { - if(hit && Interface.mouseDown) { - this.isMouseOver = true; - this.changeValue(); - if(this.mode === 'contact') { - var self = this; - setTimeout( function() { self._value = 0; self.draw(); }, 75); - } - } - }, - mousemove : function(e, hit) { - if(!this.requiresFocus && hit && Interface.mouseDown && !this.isMouseOver) { - this.isMouseOver = true; - if(this.mode !== 'contact') { - this.changeValue();// e.x - this.x, e.y - this.y ); - }else{ - this._value = 1; - this.draw(); - var self = this; - setTimeout( function() { self._value = 0; self.draw(); }, 75); - } - }else if(!hit && this.isMouseOver) { - console.log( 'moved off!' ) - this.isMouseOver = false; - } - }, - mouseup : function(e) { - if(this.mode === 'momentary') { - this.changeValue();// e.x - this.x, e.y - this.y ); - this.isMouseOver = false; - } - }, - - touchstart : function(e, hit) { - if(hit) { - this.isTouchOver = true; - this.changeValue(); - if(this.mode === 'contact') { - var self = this; - setTimeout( function() { self._value = 0; self.draw(); }, 75); - } - } - }, - touchmove : function(e, hit) { - if(!this.requiresFocus && hit && !this.isTouchOver) { - this.isTouchOver = true; - if(this.mode !== 'contact') { - this.changeValue();// e.x - this.x, e.y - this.y ); - - }else{ - this._value = 1; - this.draw(); - var self = this; - setTimeout( function() { self._value = 0; self.draw(); }, 75); - } - }else if(!hit && this.isTouchOver) { - this.isTouchOver = false; - } - }, - touchend : function(e) { - this.isTouchOver = false; - if(this.mode === 'momentary') - this.changeValue();// e.x - this.x, e.y - this.y ); - }, - }) - .init( arguments[0] ); -}; -Interface.Button.prototype = Interface.Widget; - - -/**#Interface.ButtonV - Widget -A button with a customizable shape and variety of on/off modes - -*contributed by Jonathan Simozar - -## Example Usage## -`a = new Interface.ButtonV({ - bounds:[.25,0,.125,.8], - points: [{x:1,y:0},{x:.5,y:0},{x:.5,y:.5},{x:0,y:.5},{x:0,y:1},{x:1,y:1},{x:1,y:0}], - mode:'contact', - label:'test', - textLocation : {x:.5, y:.75}, -}); - -panel = new Interface.Panel(); - -panel.add(a); -` - -## Constructor -**param** *properties*: Object. A dictionary of property values (see below) to set for the button on initialization. -- - - - -**/ -/**###Interface.ButtonV.points : property -Array. A set of coordinates used to customize the button shape. The coordinates are connected in the order of the indices. The first and last point must be the same. -**/ - -/**###Interface.ButtonV.mode : property -String. Can be 'toggle', 'momentary' or 'contact'. In toggle mode, the button turns on when it is pressed and off when it is pressed again. In momentary mode, the button turns on when pressed and off when released. In contact mode, the button briefly flashes when pressed and sends its value. -**/ - -/**###Interface.ButtonV.label : property -String. A text label to print at the textLocation coordinates of the button. -**/ - -/**###Interface.ButtonV.textLocation : property -Set. A set of x and y coordinates which position the the label within the bounds. -**/ - -Interface.ButtonV = function() { - Interface.extend(this, { - type : 'ButtonV', - _value: 0, - serializeMe : ["mode", "label"], - - mode : 'toggle', - isMouseOver : false, - isTouchOver : false, - label : null, - points : [{x : 0, y : 0}, {x : 0, y : 1}, {x : 1,y : 1}, {x : 1, y : 0}, {x : 0, y : 0}], - textLocation : {x:.5, y:.5}, - - draw : function() { - var x = this._x(), - y = this._y(), - i = 0, - width = this._width(), - height= this._height(); - - - - if(this._value) { - this.ctx.fillStyle = this._fill(); - }else{ - this.ctx.fillStyle = this._background(); - } - - this.ctx.beginPath(); - this.ctx.strokeStyle = this._stroke(); - - - for (i; i < this.points.length; i++) { - if (i === 0) { - this.ctx.moveTo(x + this.points[i].x*width, y + this.points[i].y*height); - } - else - this.ctx.lineTo(x + this.points[i].x*width, y + this.points[i].y*height); - } //this.points[i].x is how to reference points.x - this.ctx.lineTo(x + this.points[0].x*width, y + this.points[0].y*height); - this.ctx.closePath(); - this.ctx.fill(); - this.ctx.stroke(); - - - if(this.label !== null) { - this.ctx.fillStyle = this._stroke(); - this.ctx.textBaseline = 'middle'; - this.ctx.textAlign = 'center'; - this.ctx.font = this._font(); - this.ctx.fillText(this.label, x + width*this.textLocation.x, y + height*this.textLocation.y); - } - - }, - - changeValue : function( xOffset, yOffset ) { - if(this.hasFocus || !this.requiresFocus) { - this._value = !this._value; - - this.value = this._value ? this.max : this.min; - - if(this.value !== this.lastValue || this.mode === 'contact') { - this.sendTargetMessage(); - if(this.onvaluechange) this.onvaluechange(); - this.draw(); - this.lastValue = this.value; - } - } - }, - - hitTest : function(e) { - var w = this._width(), - h = this._height(), - x = this._x(), - y = this._y(); - if(e.x >= x && e.x <= x + w) { - if(e.y >= y && e.y <= y + h) { - var i = 0, - p = this.points, - sides = 0; - - for (i; i < p.length - 1; i++) { - if(p[i+1].x > p[i].x) { - if((p[i].x * w + x) <= e.x && e.x < (p[i+1].x * w + x)) { - var yval = (p[i+1].y - p[i].y)/(p[i+1].x - p[i].x) * h/w * (e.x - p[i].x * w + x) + p[i].y * h + y; - if(yval - e.y < 0) - sides++; - } - } - else if (p[i+1].x < p[i].x) { - if(p[i].x * w + x >= e.x && e.x > p[i+1].x * w + x) { - var yval = (p[i+1].y - p[i].y)/(p[i+1].x - p[i].x) * h/w * (e.x - p[i].x * w + x) + p[i].y * h + y; - if(yval - e.y < 0) - sides++; - } - } - } - if (sides % 2 == 1) - return true; - } - } - return false; - }, - - - setValue : function(value, doNotDraw) { - var r = this.max - this.min, - v = value; - - this.value = value; - - if(this.min !== 0 || this.max !== 1) { - v -= this.min; - this._value = v / r; - }else{ - this._value = this.value; - } - this.lastValue = this.value; - if(!doNotDraw && this.mode !== 'contact') this.refresh(); - }, - - mousedown : function(e, hit) { - if(hit && Interface.mouseDown) { - this.isMouseOver = true; - this.changeValue(); - if(this.mode === 'contact') { - var self = this; - setTimeout( function() { self._value = 0; self.draw(); }, 75); - } - } - }, - mousemove : function(e, hit) { - if(!this.requiresFocus && hit && Interface.mouseDown && !this.isMouseOver) { - this.isMouseOver = true; - if(this.mode !== 'contact') { - this.changeValue();// e.x - this.x, e.y - this.y ); - }else{ - this._value = 1; - this.draw(); - var self = this; - setTimeout( function() { self._value = 0; self.draw(); }, 75); - } - }else if(!hit && this.isMouseOver) { - this.isMouseOver = false; - if(this.mode !== 'contact') { - this.changeValue();// e.x - this.x, e.y - this.y ); - }else{ - this._value = 1; - this.draw(); - var self = this; - setTimeout( function() { self._value = 0; self.draw(); }, 75); - } - } - }, - mouseup : function(e) { - if(this.mode === 'momentary') { - if( this.requiresFocus || ( !this.requiresFocus && this.isMouseOver) ) { - this.isMouseOver = false; - this.changeValue(); - } - } - }, - - touchstart : function(e, hit) { - if(hit) { - this.isTouchOver = true; - this.changeValue(); - if(this.mode === 'contact') { - var self = this; - setTimeout( function() { self._value = 0; self.draw(); }, 75); - } - } - }, - touchmove : function(e, hit) { - if(!this.requiresFocus && hit && !this.isTouchOver) { - this.isTouchOver = true; - if(this.mode !== 'contact') { - this.changeValue();// e.x - this.x, e.y - this.y ); - }else{ - this._value = 1; - this.draw(); - var self = this; - setTimeout( function() { self._value = 0; self.draw(); }, 75); - } - }else if(!hit && this.isTouchOver) { - this.isTouchOver = false; - if(this.mode !== 'contact') { - this.changeValue();// e.x - this.x, e.y - this.y ); - }else{ - this._value = 1; - this.draw(); - var self = this; - setTimeout( function() { self._value = 0; self.draw(); }, 75); - } - }else if(!hit && this.isTouchOver) { - this.isTouchOver = false; - } - }, - touchend : function(e) { - if( this.momentary && this.requiresFocus || ( !this.requiresFocus && this.isTouchOver) ) { - this.changeValue(); - } - this.isTouchOver = false; - }, - }) - .init( arguments[0] ); -}; -Interface.ButtonV.prototype = Interface.Widget; - - -/**#Interface.Piano - Widget -A piano with adjustable ranges of pitches - -*contributed by Jonathan Simozar, with modifications by thecharlie - -## Example Usage## -`var c = new Interface.Piano({ - bounds:[0,0,.8,.5], - startletter : "C", - startoctave : 3, - endletter : "C", - endoctave : 5, - noteLabels : true, - target: synth, - onvaluechange : function() {this.target.note (this.frequency, this.value)}, -}); -panel = new Interface.Panel(); -panel.add(a); -` - -## Constructor -**param** *properties*: Object. A dictionary of property values (see below) to set for the piano on initialization. -- - - - -**/ - - -/**###Interface.Piano.onvaluechange : method -The event handler fired whenever a piano update is received. Used to fire the the event handler for when a button update is recieved. -**/ - - -/**###Interface.Piano.endoctave : property -Number. A number corresponding to the ending octave of the last note in the desired range. -**/ - -/**###Interface.Piano.startletter : property -String. A letter corresponding to the starting pitch for the desired range. To start on an accidental use sharps, not flats. For example, C#. -**/ - -/**###Interface.Piano.startoctave : property -Number. A number corresponding to the starting octave of the first note in the desired range. -**/ - -/**###Interface.Piano.endletter : property -String. A letter corresponding to the ending pitch for the desired range. To end on an accidental use sharps, not flats. For example, C#. -**/ - -/**###Interface.Piano.endoctave : property -Number. A number corresponding to the ending octave of the last note in the desired range. -**/ - -/**###Interface.Piano.noteLabels : property -Boolean. A boolean corresponding to showing the note labels when true and hiding the note labels when false. -**/ - -/**###Interface.Piano.target : property -Object. The instrument used to make sound on each key. -**/ - - - -Interface.Piano = function() { - Interface.extend(this, { - type : 'Piano', - _value: 0, - serializeMe : ['mode', 'label'], - mode : 'toggle', - isMouseOver : false, - isTouchOver : false, - label : null, - startletter : 'C', - startoctave : 3, - endletter : 'C', - endoctave : 5, - target : null, - noteLabels : false, - _initialized : false, - keyMap: [ 'Z','S','X','D','C','V','G','B','H','N','J','M',','], - children: [], - play: function( noteNum, duration ) { - if( isNaN(duration) ) { - duration = 4410 - } - if( typeof Gibber !== 'undefined' ) { duration = Gibber.Clock.time( duration ) } - - var child = this.children[ noteNum ] - if( child ) { - child.changeValue() - future( function() { if( child._value == 1 ) child.changeValue() }, duration ) - } - }, - onkeyup: function( e ) { - var c = Interface.keyCodeToChar[ e.keyCode ], - keyNum = this.keyMap.indexOf( c ), - child = this.children[ keyNum ] - - if( typeof child !== 'undefined' && child._value == 1 ) { - child.changeValue() - } - }, - onkeydown: function( e ) { - var c = Interface.keyCodeToChar[ e.keyCode ], - keyNum = this.keyMap.indexOf( c ), - child = this.children[ keyNum ] - - if( typeof child !== 'undefined' && child._value == 0 ) { - child.changeValue() - } - }, - onvaluechange : function() { this.values = [this.frequency,this.value] }, - onboundschange: function() { if( this._initialized) this.placeKeys() }, - - draw : function() { - for( var i = 0; i < this.children.length; i++ ) { this.children[i].refresh() } - return this - }, - - placeKeys: function() { - var x = this._x(), - y = this._y(), - width = this._width(), - height = this._height(), - octave = this.startoctave, - startnote = 0, - endnote = 0, - keylabel = ["0","C","C#/Db","D","D#/Eb","E","F","F#/Gb","G","G#/Ab","A","A#/Bb","B"], - keyid = ["0","C","C#","D","D#","E","F","F#","G","G#","A","A#","B"], - keynid = [0, 1, 2, 2, 3, 3, 4, 5, 5, 6, 6, 7, 7], - notes = ( endnote + this.endoctave * 12) - (startnote + this.startoctave * 12) + 1, - dist = ( keynid[ endnote ] + this.endoctave * 7 ) - ( keynid[ startnote ] + this.startoctave * 7 ) + 1, - j = 0; - - if( this._initialized ) { - this.clear() - for( var i = this.children.length - 1; i >= 0; i-- ) { - var key = this.children.pop() - this.panel.remove( key ) - } - } - - for (var i = 1; i < 13; i++) { - if ( this.startletter === keyid[ i ] ) startnote = i; - if ( this.endletter === keyid[ i ] ) endnote = i; - } - - if ( [ 2,4,7,9,11 ].indexOf( endnote ) > -1 ) dist--; - - for (var i = 0; i < notes - 1; i++ ) { - var points, textLocation, bg, fg, bounds, label - - switch( startnote ) { - case 1: - points = [{x:0,y:0},{x:.6,y:0},{x:.6,y:.625},{x:1,y:.625},{x:1,y:1},{x:0,y:1},{x:0,y:0}] // left - bg = this._fill() - textLocation = { x:.5, y:.75 } - fg = this._background() - bounds = [ j/dist*this.width + this.x, this.y, this.width/dist, this.height ] - label = this.noteLabels ? keylabel[startnote] + octave : null - break; - case 2: - points = [{x:.1,y:0},{x:.7,y:0},{x:.7,y:1},{x:.1,y:1},{x:.1,y:0}] //black - textLocation = { x:.3925, y:.5 } - bg = this._background() - fg = this._fill() - bounds = [(j-.5)/dist *this.width + this.x, this.y,this.width/dist,.625*this.height] - label = this.noteLabels ? keylabel[startnote] : null - break; - case 3: - points = [{x:.2,y:0},{x:.8,y:0},{x:.8,y:.625},{x:1,y:.625},{x:1,y:1},{x:0,y:1},{x:0,y:.625},{x:.2,y:.625},{x:.2,y:0}] // mid - textLocation = { x:.5, y:.75 } - bg = this._fill() - fg = this._background() - bounds = [j/dist*this.width + this.x,this.y,this.width/dist,this.height] - label = this.noteLabels ? keylabel[startnote] : null - break; - case 4: - points = [{x:.3,y:0},{x:.9,y:0},{x:.9,y:1},{x:.3,y:1},{x:.3,y:0}], //black - textLocation = {x:.6075, y:.5} - bg = this._background() - fg = this._fill() - bounds = [(j-.5)/dist *this.width + this.x, this.y,this.width/dist,.625*this.height] - label = this.noteLabels ? keylabel[startnote] : null - break; - case 5: - points = [{x:1,y:0},{x:.4,y:0},{x:.4,y:.625},{x:0,y:.625},{x:0,y:1},{x:1,y:1},{x:1,y:0}] //right - textLocation = {x:.5, y:.75} - bg = this._fill() - fg = this._background() - bounds = [j/dist*this.width+ this.x,this.y,this.width/dist,this.height] - label = this.noteLabels ? keylabel[startnote] : null - break; - case 6: - points = [{x:0,y:0},{x:0.57142857,y:0},{x:0.57142857,y:.625},{x:1,y:.625},{x:1,y:1},{x:0,y:1},{x:0,y:0}] //left - textLocation = {x:.5, y:.75} - bg = this._fill() - fg = this._background() - bounds = [j/dist*this.width+ this.x,this.y,this.width/dist,this.height] - label = this.noteLabels ? keylabel[startnote] : null - break; - case 7: - points = [{x:0.07142857,y:0},{x:0.64285714,y:0},{x:0.64285714,y:1},{x:0.07142857,y:1},{x:0.07142857,y:0}] //black - textLocation = {x:.3925, y:.5} - bg = this._background() - fg = this._fill() - bounds = [(j-.5)/dist*this.width + this.x, this.y,this.width/dist,.625*this.height] - label = this.noteLabels ? keylabel[startnote] : null - break; - case 8: - points = [{x:0.14285714,y:0},{x:0.71428571,y:0},{x:0.71428571,y:.625},{x:1,y:.625},{x:1,y:1},{x:0,y:1},{x:0,y:.625},{x:0.14285714,y:.625},{x:0.14285714,y:0}], //middle - textLocation = {x:.5, y:.75} - bg = this._fill() - fg = this._background() - bounds = [j/dist*this.width + this.x,this.y,this.width/dist,this.height] - label = this.noteLabels ? keylabel[startnote] : null - break; - case 9: - points = [{x:0.21428571,y:0},{x:0.78571428,y:0},{x:0.78571428,y:1},{x:0.21428571,y:1},{x:0.21428571,y:0}] //black - bg = this._background() - fg = this._fill() - bounds = [(j-.5)/dist*this.width + this.x, this.y,this.width/dist,.625*this.height] - label = this.noteLabels ? keylabel[startnote] : null - break; - case 10: - points = [{x:0.28571428,y:0},{x:0.85714285,y:0},{x:0.85714285,y:.625},{x:1,y:.625},{x:1,y:1},{x:0,y:1},{x:0,y:.625},{x:0.28571428,y:.625},{x:0.28571428,y:0}], //middle - bg = this._fill() - fg = this._background() - textLocation = {x:.5, y:.75} - bounds = [j/dist*this.width + this.x,this.y,this.width/dist,this.height] - label = this.noteLabels ? keylabel[startnote] : null - break; - case 11: - points = [{x:0.35714285,y:0},{x:0.92857142,y:0},{x:0.92857142,y:1},{x:0.35714285,y:1},{x:0.35714285,y:0}], //black - bg = this._background() - fg = this._fill() - textLocation = {x:.6075, y:.5} - bounds = [(j-.5)/dist*this.width + this.x, this.y,this.width/dist,.625*this.height] - label = this.noteLabels ? keylabel[startnote] : null - break; - case 12: - points = [{x:1,y:0},{x:0.42857142,y:0},{x:0.42857142,y:.625},{x:0,y:.625},{x:0,y:1},{x:1,y:1},{x:1,y:0}] //right - bg = this._fill() - fg = this._background() - textLocation = {x:.5, y:.75} - bounds = [j/dist*this.width+ this.x,this.y,this.width/dist,this.height] - label = this.noteLabels ? keylabel[startnote] : null - break; - } - - - var _key = new Interface.ButtonV({ - points: points, - textLocation : textLocation, - target : this.target, - onvaluechange: this.onvaluechange, - frequency: Math.pow(2,(startnote + 12*octave - 49)/12)*261.626, - background: bg, - fill: fg, - stroke: this._stroke(), - bounds: bounds, - label: label, - requiresFocus : false, - mode:'momentary', - }); - - if ( [ 2,4,7,9,11 ].indexOf( startnote ) === -1 ) j++; - - this.children.push(_key) - this.panel.add(_key); - - startnote++; - - if (startnote > 12) { - startnote = 1; - octave++; - } - } - if (startnote == 2 || startnote == 4 || startnote == 7 || startnote == 9 || startnote == 11) - var pkeys = new Interface.ButtonV({ - points: [{x:.166,y:0},{x:.5,y:0},{x:.5,y:1},{x:.166,y:1},{x:.166,y:0}], //black - target : this.target, - onvaluechange: this.onvaluechange, - background: this._background(), - frequency: Math.pow(2,(startnote + 12*octave - 49)/12)*261.626, - bounds:[(j-.5)/dist*this.width + this.x, this.y,this.width/dist,.625*this.height], - label: this.noteLabels ? keylabel[startnote] : null, - stroke: this._stroke(), - requiresFocus : false, - mode:'momentary' - }); - else if (startnote == 1) - var pkeys = new Interface.ButtonV({ - textLocation : {x:.5, y:.75}, - target : this.target, - onvaluechange: this.onvaluechange, - frequency: Math.pow(2,(startnote + 12*octave - 49)/12)*261.626, - background: this._fill(), - fill: this._background(), - stroke: this._stroke(), - bounds:[j/dist*this.width + this.x,this.y,this.width/dist,this.height], - label: this.noteLabels ? keylabel[startnote] + octave : null, - requiresFocus : false, - mode:'momentary' - }); - else if (startnote == 4) - var pkeys = new Interface.ButtonV({ - textLocation : {x:.5, y:.75}, - target : this.target, - onvaluechange: this.onvaluechange, - frequency: Math.pow(2,(startnote + 12*octave - 49)/12)*261.626, - background: this._fill(), - fill: this._background(), - stroke: this._stroke(), - bounds:[j/dist*this.width + this.x,this.y,this.width/dist,this.height], - label: this.noteLabels ? keylabel[startnote] : null, - requiresFocus : false, - mode:'momentary' - }); - else - var pkeys = new Interface.ButtonV({ - points: [{x:1,y:0},{x:.33,y:0},{x:.33,y:.625},{x:0,y:.625},{x:0,y:1},{x:1,y:1},{x:1,y:0}], //right - textLocation : {x:.5, y:.75}, - target : this.target, - onvaluechange: this.onvaluechange, - frequency: Math.pow(2,(startnote + 12*octave - 49)/12)*261.626, - background: this._fill(), - fill: this._background(), - stroke: this._stroke(), - bounds:[j/dist*this.width + this.x,this.y,this.width/dist,this.height], - label: this.noteLabels ? keylabel[startnote] : null, - requiresFocus : false, - mode:'momentary' - }); - - this.children.push(pkeys) - this.panel.add(pkeys); - - this._initialized = true - }, - _init : function() { - this.placeKeys() - } - }) - .init( arguments[0] ); -}; -Interface.Piano.prototype = Interface.Widget; - - - -/**#Interface.Knob - Widget -A virtual knob. - -## Example Usage## -`a = new Interface.Knob({ x:.1, y:.1, radius:.3 }); -panel = new Interface.Panel(); -panel.add(a); -` -## Constructor -**param** *properties*: Object. A dictionary of property values (see below) to set for the knob on initialization. -- - - - -**/ -/**###Interface.Knob.radius : property -Number. The size of the Knob. -**/ -/**###Interface.Knob.knobBuffer : property -Number. The size of the space in the middle of the knob. -**/ -/**###Interface.Knob.centerZero : property -Number. If true, the knob is centered at zero. Useful for panning knobs. -**/ -/**###Interface.Knob.usesRotation : property -Number. If true, the knob value is determined by the slope of the touch or mouse event in relation to the knob. When false, the user simply presses the knob and moves their finger/mouse up and down to change its value. -**/ - -Interface.Knob = function() { - Interface.extend(this, { - type : 'Knob', - _value: 0, - serializeMe : ["usesRotation", "knobBuffer"], - knobBuffer:3, - lastPosition: 0, - usesRotation: true, - - draw : function() { - var x = this._x(), - y = this._y(), - width = this._width(), - height= this._height(), - radius = width / 2; - - this.ctx.clearRect(x, y, radius * 2,radius * 2); - this.ctx.strokeStyle = this._stroke(); - - this.ctx.fillStyle = this._background(); // draw background of widget first - - var angle0 = Math.PI * .6; - var angle1 = Math.PI * .4; - - this.ctx.beginPath(); - - this.ctx.arc(x + radius, y + radius, radius - this.knobBuffer, angle0, angle1, false); - this.ctx.arc(x + radius, y + radius, (radius - this.knobBuffer) * .3 , angle1, angle0, true); - this.ctx.closePath(); - this.ctx.fill(); - - this.ctx.fillStyle = this._fill(); // now draw foreground... - - if(this.centerZero) { - var angle3 = Math.PI * 1.5; - var angle4; - if(this._value >= .5) { - angle4 = Math.PI * (1.5 + (this._value - .5) * 1.8); // from 1.5 to 2.4 - }else{ - angle4 = Math.PI * (1.5 - ((1 - this._value * 2) * .9)); // from 1.5 to .6 - } - if(this._value > Math.PI * 1.8) this._value -= Math.PI * 1.8; // wrap around - - this.ctx.beginPath(); - this.ctx.arc(x + radius, y + radius, radius - this.knobBuffer, angle3, angle4, (this._value < .5)); - this.ctx.arc(x + radius, y + radius, (radius - this.knobBuffer) * 0.3, angle4, angle3, (this._value > .5)); - this.ctx.closePath(); - - // if(this._value > .495 && this._value < .505) { // draw circle if centered? - // this.ctx.beginPath(); - // this.ctx.arc(this.x + radius , this.y + radius, (radius - this.knobBuffer) * .3, 0, Math.PI*2, true); - // this.ctx.closePath(); - // } - this.ctx.fill(); - } else { - if(!this.isInverted) { - var angle2 = Math.PI * .6 + this._value * 1.8 * Math.PI; - if(angle2 > 2 * Math.PI) angle2 -= 2 * Math.PI; - }else{ - var angle2 = Math.PI * (0.4 - (1.8 * this._value)); - } - - this.ctx.beginPath(); - - if(!this.isInverted) { - this.ctx.arc(x + radius, y + radius, radius - this.knobBuffer, angle0, angle2, false); - this.ctx.arc(x + radius, y + radius, (radius - this.knobBuffer) * .3, angle2, angle0, true); - } else { - this.ctx.arc(x + radius, y + radius, radius - this.knobBuffer, angle1, angle2 ,true); - this.ctx.arc(x + radius, y + radius, (radius - this.knobBuffer) * .3, angle2, angle1, false); - } - this.ctx.closePath(); - this.ctx.fill(); - } - - this.ctx.beginPath(); - this.ctx.arc(x + radius, y + radius, radius - this.knobBuffer, angle0, angle1, false); - this.ctx.arc(x + radius, y + radius, (radius - this.knobBuffer) * .3 , angle1, angle0, true); - this.ctx.closePath(); - - this.ctx.stroke(); - - if(this.label !== null) { - this.ctx.fillStyle = this._stroke(); - this.ctx.textBaseline = 'middle'; - this.ctx.textAlign = 'center'; - this.ctx.font = this._font(); - this.ctx.fillText(this.label, x + radius, y + radius * 2.25); - } - }, - - setValue : function(value, doNotDraw) { - var r = this.max - this.min, - v = value; - - this.lastValue = this.value; - - this.value = value; - - if(this.min !== 0 || this.max !== 1) { - v -= this.min; - this._value = v / r; - }else{ - this._value = this.value; - } - - if(this.value !== this.lastValue) { - this.sendTargetMessage(); - if(this.onvaluechange) this.onvaluechange(); - this.refresh(); - this.lastValue = this.value; - } - - if(!doNotDraw) this.refresh(); - }, - - changeValue : function( xOffset, yOffset ) { - if(this.hasFocus || !this.requiresFocus) { - var radius = this._width() / 2; - this.lastValue = this.value; - - if(!this.usesRotation) { - if (this.lastPosition != -1) { - this._value -= (yOffset - this.lastPosition) / (radius * 2); - } - }else{ - var xdiff = radius - xOffset; - var ydiff = radius - yOffset; - var angle = Math.PI + Math.atan2(ydiff, xdiff); - this._value = ((angle + (Math.PI * 1.5)) % (Math.PI * 2)) / (Math.PI * 2); - - if(this.lastRotationValue > .8 && this._value < .2) { - this._value = 1; - }else if(this.lastRotationValue < .2 && this._value > .8) { - this._value = 0; - } - } - - if (this._value > 1) this._value = 1; - if (this._value < 0) this._value = 0; - - this.lastRotationValue = this._value; - this.lastPosition = yOffset; - - var range = this.max - this.min; - this.value = this.min + this._value * range; - - if(this.value !== this.lastValue) { - this.sendTargetMessage(); - if(this.onvaluechange) this.onvaluechange(); - this.refresh(); - this.lastValue = this.value; - } - } - }, - - hitTest : function(e) { - if( e.x >= this._x() && e.x < this._x() + this._width() ) { - if( e.y >= this._y() && e.y < this._y() + this._width() ) { - return true; - } - } - - return false; - }, - - mousedown : function(e) { - this.lastPosition = e.y - this._y(); - this.changeValue( e.x - this._x(), e.y - this._y() ); - }, - mousemove : function(e) { this.changeValue( e.x - this._x(), e.y - this._y() ); }, - mouseup : function(e) {}, - - touchstart : function(e) { - this.lastPosition = e.y - this._y(); - this.changeValue( e.x - this._x(), e.y - this._y() ); - }, - touchmove : function(e) { this.changeValue( e.x - this._x(), e.y - this._y() ); }, - touchend : function(e) {}, - - _init : function() { - var width = this.width, - height = this.height; - Object.defineProperty(this, 'width', { - configurable: true, - get : function() { return width; }, - set : function(_width) { this.clear(); width = height = _width; this.refresh(); } - }); - Object.defineProperty(this, 'height', { - configurable: true, - get : function() { return height; }, - set : function(_height) { height = _height; } - }); - }, - }) - .init( arguments[0] ); -}; -Interface.Knob.prototype = Interface.Widget; - -function sign(n) { - if(n < 0) return -1; - return 1; -} -/**#Interface.XY - Widget -A multitouch XY controller with optional built-in physics. - -## Example Usage## -`a = new Interface.XY({ x:0, y:0, numChildren:2 }); -panel = new Interface.Panel(); -panel.add(a); -` -## Constructor -**param** *properties*: Object. A dictionary of property values (see below) to set for the XY on initialization. -- - - - -**/ -/**###Interface.XY.childWidth : property -Number. The size of the children, currently in pixels. TODO: use relative values when the panel is using relative sizes and positions. -**/ -/**###Interface.XY.usePhysics : property -Boolean. Wheter or not the physics engine should be turned on. -**/ -/**###Interface.XY.friction : property -Number. Default .9. The amount of friction in the physics system. High values mean children will decelerate quicker. -**/ -/**###Interface.XY.maxVelocity : property -Number. Default 10. The maximum velocity for each child. -**/ -/**###Interface.XY.detectCollisions : property -Boolean. Default true. When true, children bounce off one another. -**/ -/**###Interface.XY.values : property -Array. An array of objects taking the form {x,y} that store the x and y positions of every child. So, to get the x position of child #0: myXY.values[0].x -**/ -/**###Interface.XY.children : property -Array. An array of objects representing the various children of the widget. -**/ -/**###Interface.XY.animate : method -This is called to run the physics engine, draw widgets with updated positions, change values of widgets and call appropriate event handlers. -**/ - -Interface.XY = function() { - var self = this, - posDiff = {x:0, y:0}, - velDiff = {x:0, y:0}, - normal = {x:0, y:0}, - cDot = 0; - - Interface.extend(this, { - type : 'XY', - _value : 0, - serializeMe : ["childWidth", "childHeight", "numChildren", "usePhysics", "values", "friction", "maxVelocity", "detectCollisions", "fps"], - childWidth : 25, - childHeight : 25, - children : [], - values : [], // objects containing x and y values - _values : [], // serialized floats alternating between x and y - numChildren : 1, - usePhysics : true, - friction : .9, - activeTouch : null, - maxVelocity : 10, - detectCollisions : true, - touchCount : 0, - timer : null, - fps : 30, - outputInitialValues: true, - - rainbow: function() { - //console.log("RAINBOW", this.children.length); - for(var i = 0; i < this.children.length; i++) { - var child = this.children[i]; - child.fill = Interface.XY.colors[i % Interface.XY.colors.length]; //'rgba('+Math.round(Math.random()*255)+','+Math.round(Math.random()*255)+','+Math.round(Math.random()*255)+',.2)'; - //console.log("YUM", child.fill) - } - //this.refresh() - }, - remove: function() { this.stopAnimation(); Interface.widgets.splice( Interface.widgets.indexOf( this ), 1 ); }, - add : function() { if(this.usePhysics) this.startAnimation(); }, - startAnimation : function() { - if(this.timer === null) { - this.timer = setInterval( function() { self.refresh(); }, (1 / this.fps) * 1000); - } - }, - stopAnimation : function() { clearInterval(this.timer); this.timer = null; }, - - animate : function() { - var x = this._x(), - y = this._y(), - width = this._width(), - height = this._height(), - shouldrunvaluechange = false; - - for(var i = 0; i < this.children.length; i++) { - var moveX = moveY = false, - child = this.children[i]; - - if(child.x + child.vx < width && child.x + child.vx > 0) { - child.x += child.vx; - }else{ - if(child.x + child.vx >= width && child.vx > 0 ) { - child.vx *= -1; - }else if(child.x + child.vx <= 0 && child.vx < 0) { - child.vx *= -1; - }else{ - child.x += child.vx; - } - } - - if(child.y + child.vy < height && child.y + child.vy > 0) { - child.y += child.vy; - }else{ - if(child.y + child.vy >= height && child.vy > 0 ) { - child.vy *= -1; - }else if(child.y + child.vy <= 0 && child.vy < 0) { - child.vy *= -1; - }else{ - child.y += child.vy; - } - } - - child.vx *= this.friction; - child.vy *= this.friction; - - var newValueX = child.x / width; - var newValueY = child.y / height; - - var range = this.max - this.min; - if(this.values[child.id].x !== newValueX || this.values[child.id].y !== newValueY) { - this.values[child.id].x = this.min + range * newValueX; - this.values[child.id].y = this.min + range * newValueY; - shouldrunvaluechange = true; - } - - if(this.detectCollisions) { - if(!child.collideFlag) { - this.collisionTest(child); - }else{ - child.collideFlag = false; - } - } - - child.vx = Math.abs(child.vx) > this.maxVelocity ? this.maxVelocity * sign(child.vx) : child.vx; - child.vy = Math.abs(child.vy) > this.maxVelocity ? this.maxVelocity * sign(child.vy): child.vy; - } - if(shouldrunvaluechange) { - this.sendTargetMessage(); - - if(this.onvaluechange) { - this.onvaluechange(); - } - } - }, - - // MultiXY sends out all child values in serialized xy pairs - sendValues : function() { - var tt = ''; - this._values.length = 0; - for(var i = 0; i < this.values.length; i++) { - tt += 'ff'; - this._values.push( this.values[i].x ); - this._values.push( this.values[i].y ); - } - if(this.target === "OSC") { - if(Interface.OSC) { - Interface.OSC.send( this.key, tt, this._values ); - } - } - }, - - collisionTest : function(c1) { - var cw2 = (this.childWidth * 2) * (this.childWidth * 2); - for(var i = 0; i < this.children.length; i++) { - var c2 = this.children[i]; - if(c1.id !== c2.id) { - var distance = Math.pow(c1.x - c2.x, 2) + Math.pow(c1.y - c2.y, 2); - - if(distance < cw2) { // avoid square root by raising the distance check - this.collide(c1, c2) - } - } - } - }, - - collide : function(c1,c2) { - // posDiff, velDiff and normal are upvalues for gc performance - posDiff.x = c1.x - c2.x; - posDiff.y = c1.y - c2.y; - velDiff.x = c1.vx - c2.vx; - velDiff.y = c1.vy - c2.vy; - - cDot = Math.sqrt( Math.pow(posDiff.x, 2) + Math.pow(posDiff.y, 2) ); - - normal.x = posDiff.x / cDot; - normal.y = posDiff.y / cDot; - - var d = (normal.x * velDiff.x) + (normal.y * velDiff.y); - c2.vx = c1.vx + d * normal.x; - c2.vy = c1.vy + d * normal.y; - c1.vx = c2.vx - d * normal.x; - c1.vy = c2.vy - d * normal.y; - - c2.x -= normal.x; - c2.y -= normal.y; - c1.x += normal.x; - c1.y += normal.y; - - c1.vx = Math.abs(c1.vx) > this.maxVelocity ? this.maxVelocity * sign(c1.vx) : c1.vx; - c1.vy = Math.abs(c1.vy) > this.maxVelocity ? this.maxVelocity * sign(c1.vy) : c1.vy; - c2.vx = Math.abs(c2.vx) > this.maxVelocity ? this.maxVelocity * sign(c2.vx) : c2.vx; - c2.vy = Math.abs(c2.vy) > this.maxVelocity ? this.maxVelocity * sign(c2.vy) : c2.vy; - - c1.collideFlag = true; - c2.collideFlag = true; - }, - - draw : function() { - var x = this._x(), - y = this._y(), - width = this._width(), - height= this._height(); - - if(this.usePhysics) this.animate(); - - this.ctx.fillStyle = this._background(); - //this.ctx.fillRect( this.x, this.y, this.width, this.height ); - - this.ctx.strokeStyle = this._stroke(); - //this.ctx.strokeRect( this.x, this.y, this.width, this.height ); - - this.ctx.save(); - - this.ctx.beginPath(); - - this.ctx.moveTo(x, y); - this.ctx.lineTo(x + width, y); - this.ctx.lineTo(x + width, y + height); - this.ctx.lineTo(x, y + height); - this.ctx.lineTo(x, y); - this.ctx.fill(); - this.ctx.stroke(); - - this.ctx.clip(); - - this.ctx.fillStyle = this._fill(); - - for(var i = 0; i < this.children.length; i++) { - var child = this.children[i]; - this.ctx.lineWidth = 2 - this.ctx.fillStyle = child.fill || this._fill(); - - this.ctx.beginPath(); - - this.ctx.arc(x + child.x, y + child.y, this.childWidth, 0, Math.PI*2, true); - - this.ctx.closePath(); - - this.ctx.fill(); - this.ctx.stroke(); - //this.ctx.fillRect( this.x + child.x, this.y + child.y, this.childWidth, this.childHeight); - this.ctx.textBaseline = 'middle'; - this.ctx.textAlign = 'center'; - this.ctx.fillStyle = this._stroke(); - this.ctx.font = this._font(); - this.ctx.fillText(child.id, x + child.x, y + child.y); - } - - this.ctx.closePath(); - this.ctx.restore(); - }, - - changeValue : function( touch, xOffset, yOffset ) { - if(this.hasFocus || !this.requiresFocus) { - touch.x = xOffset; - if(touch.x < 0 ) touch.x = 0; - if(touch.x > this._width()) touch.x = this._width(); - - touch.y = yOffset;// - this.half; - if(touch.y < 0) touch.y = 0; - if(touch.y > this._height()) touch.y = this._height(); - this.values[touch.id].x = xOffset / this._width(); - this.values[touch.id].y = yOffset / this._height(); - - if(this.onvaluechange) this.onvaluechange(); - - if(!this.usePhysics) { - this.sendTargetMessage(); - this.refresh(); - } - } - }, - - makeChildren : function() { - for(var i = 0; i < this.numChildren; i++) { - var x = Math.random() * this._width(), - y = Math.random() * this._height() - - this.children.push({ id:i, x:x, y:y, vx:0, vy:0, collideFlag:false, isActive:false, lastPosition:null, }); - this.values.push({ x:null, y:null }); - } - }, - - touchEvent : function(touch) { - var isHit = this.hitTest(touch); - var touchMouseName = convertTouchEvent(touch.type); - - if(isHit) { - if(touch.type === 'touchstart') { - this.hasFocus = true; - this.touchCount++; - this.trackTouch(touch.x - this._x(), touch.y - this._y(), touch); - }else{ - if(this[touch.type]) - this[touch.type](touch, isHit, touch.childID); // normal event - } - - if(this['on'+touch.type]) this['on'+touch.type](touch, isHit, touch.childId); // user defined event - if(this['on'+touchMouseName]) this['on'+touchMouseName](touch, isHit); // user defined event - - }else if(touch.type === 'touchend'){ - this.touchCount--; - if(this.touchCount === 0) { - this.hasFocus = false; - }else if(this.touchCount < 0 ) { - this.touchCount = 0; - } - this.touchend(touch) - if(this['on'+touch.type]) this['on'+touch.type](touch, isHit, touch.childId); // user defined event - if(this['on'+touchMouseName]) this['on'+touchMouseName](touch, isHit); // user defined event - } - }, - - trackMouse : function(xPos, yPos, id) { - var closestDiff = 10000, - touchFound = null, - touchNum = null; - - for(var i = 0; i < this.children.length; i++) { - var touch = this.children[i], - xdiff = Math.abs(touch.x - xPos), - ydiff = Math.abs(touch.y - yPos); - - if(xdiff + ydiff < closestDiff) { - closestDiff = xdiff + ydiff; - - touchFound = touch; - touchNum = i; - } - } - - touchFound.isActive = true; - touchFound.vx = 0; - touchFound.vy = 0; - - if(touchFound != null) { - this.changeValue(touchFound, xPos, yPos); - } - - this.activeTouch = touchFound; - this.activeTouch.lastTouch = null; - - this.lastTouched = touchFound; - }, - - mousedown : function(e) { - if(this.hitTest(e)) { - this.trackMouse(e.x - this._x(), e.y - this._y()); - } - }, - mousemove : function(e) { - if(this.hitTest(e) && this.activeTouch !== null) { - if(this.activeTouch.lastTouch === null) { - this.activeTouch.lastTouch = {x:e.x - this._x(), y:e.y - this._y()}; - }else{ - var now = {x:e.x - this._x(), y:e.y - this._y()}; - this.activeTouch.velocity = {x:now.x - this.activeTouch.lastTouch.x, y:now.y - this.activeTouch.lastTouch.y }; - this.activeTouch.lastTouch = now; - } - - this.changeValue(this.activeTouch, e.x - this._x(), e.y - this._y()); - } - }, - mouseup : function(e) { - if(this.activeTouch !== null) { - this.activeTouch.vx = this.activeTouch.velocity.x; - this.activeTouch.vy = this.activeTouch.velocity.y; - this.activeTouch.lastTouch = null; - this.activeTouch = null; - } - for(var i = 0; i < this.children.length; i++) { - this.children[i].isActive = false; - } - }, - - trackTouch : function(xPos, yPos, _touch) { - var closestDiff = 10000; - var touchFound = null; - var touchNum = null; - - for(var i = 0; i < this.children.length; i++) { - var touch = this.children[i]; - var xdiff = Math.abs(touch.x - xPos); - var ydiff = Math.abs(touch.y - yPos); - - if(xdiff + ydiff < closestDiff && !touch.isActive) { - closestDiff = xdiff + ydiff; - touchFound = touch; - touchNum = i; - } - } - - touchFound.isActive = true; - touchFound.vx = 0; - touchFound.vy = 0; - touchFound.identifier = _touch.identifier; - touchFound.childID = touchNum; - - if(touchFound != null) - this.changeValue(touchFound, xPos, yPos); - - this.lastTouched = touchFound; - return touchFound.childID; - }, - touchstart : function(touch) { - // if(this.hitTest(touch)) { - // this.trackTouch(touch.x - this.x, touch.y - this.y, touch); - // } - }, - touchmove : function(touch) { - for(var t = 0; t < this.children.length; t++) { - _t = this.children[t]; - if(touch.identifier === _t.identifier) { - this.changeValue(_t, touch.x - this._x(), touch.y - this._y()); - - var now = {x:touch.x - this._x(), y:touch.y - this._y()}; - - if(_t.lastPosition !== null) { - _t.velocity = {x:now.x - _t.lastPosition.x, y:now.y - _t.lastPosition.y }; - } - _t.lastPosition = now; - } - } - }, - touchend : function(touch) { - var found = false; - var tu = null; - for(var t = 0; t < this.children.length; t++) { - var _t = this.children[t]; - - if(touch.identifier === _t.identifier) { - if( _t.velocity ) { - _t.vx = _t.velocity.x; - _t.vy = _t.velocity.y; - } - - _t.lastPosition = null; - _t.isActive = false; - - - found = true; - tu = t.childID; - } - } - if(found) { this.touchUp = tu; } - //if(!found) console.log("NOT FOUND", touch.identifier); - }, - - _init : function() { - this.makeChildren(); - if( this.outputInitialValues ) { - this.sendTargetMessage(); - } - }, - }) - .init( arguments[0] ); - - this.requiresFocus = false; // is a widget default... must set after init. - this.half = this.childWidth / 2; - - var numChildren = this.numChildren; - Object.defineProperty(this, 'numChildren', { - get : function() { return numChildren; }, - set : function(_numChildren) { - var temp = _numChildren; - while(_numChildren > numChildren) { - this.children.push({ id:this.children.length, x:Math.random() * this._width(), y:Math.random() * this._height(), vx:0, vy:0, collideFlag:false, isActive:false, lastPosition:null, }); - this.values.push({ x:null, y: null}); - numChildren++; - } - - while(_numChildren < numChildren) { - this.chidren.pop(); - this.values.pop(); - numChildren--; - } - this.refresh(); - numChildren = _numChildren; - } - }); -}; - -Interface.XY.prototype = Interface.Widget; -Interface.XY.colors = [ - 'rgba(255,0,0,.35)', - 'rgba(0,255,0,.35)', - 'rgba(0,0,255,.35)', - 'rgba(0,255,255,.35)', - 'rgba(255,0,255,.35)', - 'rgba(255,255,0,.35)', -]; -/**#Interface.Menu - Widget -A multi-option dropdown menu. -## Example Usage## -`a = new Interface.Menu({x:0, y:0, options:['red', 'yellow', 'green'] }); -a.onvaluechange = function() { b.background = this.value; } -b = new Interface.Slider({x:.5, y:.5, width:.2, height:.3}); -panel = new Interface.Panel(); -panel.add(a,b); -` -## Constructor -**param** *properties*: Object. A dictionary of property values (see below) to set for the menu on initialization. -- - - - -**/ -/**###Interface.Menu.options : property -Array. A list of values found in the menu. -**/ -/**###Interface.Menu.css : property -Object. A dictionary of css keys / values to be applied to the menu. -**/ -/**###Interface.Menu.onvaluechange : method -The event handler fired whenever the selected menu option changes. - -param **newValue** Number or String. The new menu value. -param **oldValue** Number or String. The previous menu value. -**/ -Interface.Menu = function() { - Interface.extend(this, { - type : 'Menu', - _value: 0, - serializeMe : ["options", "fontSize"], - options: [], - fontSize:15, - touchEvent: function(e) { // we have to simulate this since the actual event was cancelled to avoid scrolling behavior - if(this.hitTest(e)) { - e.stopPropagation(); - /*var evt = document.createEvent('TouchEvent'); - evt.initUIEvent('touchstart', true, true); - - evt.view = window; - evt.screenX = e.screenX; - evt.screenY = e.screenY; - evt.clientX = e.clientX; - evt.clientY = e.clientY; - evt.bubbles = false; - evt.view = window; - evt.altKey = false; - evt.ctrlKey = false; - evt.shiftKey = false; - evt.metaKey = false; - - this.element.dispatchEvent(evt);*/ - } - }, - _init : function() { - this.element = $(" tag. -## Example Usage## -`a = new Interface.TextField({x:0, y:0, width:.5, height:.5, value:'starting value', onvaluechange: function() { alert( this.value ); } }); -panel = new Interface.Panel(); -panel.add(a); -` -## Constructor -**param** *properties*: Object. A dictionary of property values (see below) to set for the textfield on initialization. -- - - - -**/ -/**###Interface.TextField.fontSize : property -Number. The size in pixels for the font used in the text field -**/ -/**###Interface.TextField.css : property -Object. Extra css that you would like to apply to the input element -**/ -Interface.TextField = function() { - Interface.extend(this, { - type : 'TextField', - serializeMe : ["fontSize"], - fontSize: 15, - touchEvent: function(e) { // we have to simulate this since the actual event was cancelled to avoid scrolling behavior - if(this.hitTest(e)) { - var evt = document.createEvent('TouchEvent'); - evt.initUIEvent('touchstart', true, true); - - evt.view = window; - evt.screenX = e.screenX; - evt.screenY = e.screenY; - evt.clientX = e.clientX; - evt.clientY = e.clientY; - evt.bubbles = false; - evt.view = window; - evt.altKey = false; - evt.ctrlKey = false; - evt.shiftKey = false; - evt.metaKey = false; - - this.element.dispatchEvent(evt); - } - }, - _init : function() { - this.element = $(""); - - if(this.value !== 0) { - this.element.val( this.value ); - } - this.element.css({ - position:'absolute', - backgroundColor:this._background(), - color:this._fill(), - left: this._x() + this.panel.x, - top: this._y() + this.panel.y, - width: this._width(), - height: this._height(), - fontSize: this.fontSize, - display:'block', - border: '1px solid ' + this._stroke(), - }); - - if(this.css) this.element.css( this.css ); - - var self = this; - this.element.change( - function(obj) { - var oldValue = self.value; - self.value = self.element.val(); - self.sendTargetMessage(); - self.onvaluechange(self.value, oldValue); - } - ); - - $(this.container).append(this.element); - }, - }) - .init( arguments[0] ); -}; -Interface.TextField.prototype = Interface.Widget; - -/**#Interface.MultiSlider - Widget -Multiple vertical sliders that share the same event handlers and colors. When a MultiSlider sends OSC, it comes in the form of an integer (representing the -number of the slide moved) and a float (representing the value of the slider moved). Any onvaluechange method attached to the MultiSlider widget should have a -similar signature; see the example below. -## Example Usage## -`b = new Interface.Label({ bounds:[.5,.5,.5,.5], size:12 }); -a = new Interface.MultiSlider({ - bounds:[0,0,.5,.5], - fill:'red', - count:8, - onvaluechange : function( sliderNumber, sliderValue) { b.setValue('number : ' + sliderNumber + ', value : ' + sliderValue) }, -}); -panel = new Interface.Panel(); -panel.add(a,b); -` -## Constructor -**param** *properties*: Object. A dictionary of property values (see below) to set for the multislider on initialization. -- - - - -**/ -/**###Interface.MultiSlider.count : property -Number. The number of sliders in the widget -**/ -Interface.MultiSlider = function() { - Interface.extend(this, { - type : 'MultiSlider', - isVertical : true, - serializeMe : ["isVertical", "count", "values"], - values: [], - _values: [], - count:16, - - draw : function() { - var x = this._x(), - y = this._y(), - width = this._width(), - height= this._height(), - sliderWidth = width / this.count; - - this.ctx.fillStyle = this._background(); - this.ctx.fillRect( x, y, width, height ); - - this.ctx.fillStyle = this._fill(); - this.ctx.strokeStyle = this._stroke(); - - for(var i = 0; i < this.count; i++) { - var sliderX = i * sliderWidth + x; - - this.ctx.fillRect( sliderX, y + height - this._values[i] * height, sliderWidth, this._values[i] * height); - this.ctx.strokeRect( sliderX, y, sliderWidth, height ); - } - }, - setValue : function( sliderNum, value ) { - this.values[ sliderNum ] = value - this._values[ sliderNum ] = value - this.refresh() - }, - resetValues : function() { - for( var i = 0; i < this.count; i++ ) { - this.values[ i ] = this.min + (this.max - this.min) * this._values[ i ]; - - if(this.target !== "OSC") { - this.sendTargetMessage(); - }else{ - if(Interface.OSC) - Interface.OSC.send( this.key, 'if', [ sliderHit, this.values[ sliderHit ] ] ); - } - if(this.onvaluechange) this.onvaluechange(sliderHit, this.values[ sliderHit ]); - } - - this.refresh(); - }, - changeValue : function( xOffset, yOffset ) { - if(this.hasFocus || !this.requiresFocus) { - var width = this._width(), - sliderWidth = width / this.count, - sliderHit = Math.floor( xOffset / sliderWidth ) - _value = 0; - - _value = 1 - ( yOffset / this._height() ); - - if(_value < 0) { - _value = 0; - // this.hasFocus = false; - }else if(_value > 1) { - _value = 1; - // this.hasFocus = false; - } - - this.values[ sliderHit ] = this.min + (this.max - this.min) * _value; - this._values[ sliderHit ] = _value; - - if(this.target !== "OSC") { - this.sendTargetMessage(); - }else{ - if(Interface.OSC) - Interface.OSC.send( this.key, 'if', [ sliderHit, this.values[ sliderHit ] ] ); - } - if(this.onvaluechange) this.onvaluechange(sliderHit, this.values[ sliderHit ]); - this.refresh(); - //this.lastValue = this.value; - //} - } - }, - - mousedown : function(e, hit) { if(hit && Interface.mouseDown) this.changeValue( e.x - this._x(), e.y - this._y() ); }, - mousemove : function(e, hit) { if(hit && Interface.mouseDown) this.changeValue( e.x - this._x(), e.y - this._y() ); }, - mouseup : function(e, hit) { if(hit && Interface.mouseDown) this.changeValue( e.x - this._x(), e.y - this._y() ); }, - - touchstart : function(e, hit) { if(hit) this.changeValue( e.x - this._x(), e.y - this._y() ); }, - touchmove : function(e, hit) { if(hit) this.changeValue( e.x - this._x(), e.y - this._y() ); }, - touchend : function(e, hit) { if(hit) this.changeValue( e.x - this._x(), e.y - this._y() ); }, - onvaluechange : function(id, value) {}, - }) - .init( arguments[0] ); - - var x = this.x, - y = this.y, - width = this.width, - height = this.height, - bounds = [x,y,width,height], - count = this.count; - - delete this.bounds; - - Object.defineProperties(this, { - x : { - get : function() { return x; }, - set: function(_x) { x = _x; this.refresh(); } - }, - y : { - get : function() { return y; }, - set: function(_y) { y = _y; this.refresh(); } - }, - width : { - get : function() { return width; }, - set: function(_width) { width = _width; this.refresh(); } - }, - height : { - get : function() { return height; }, - set: function(_height) { height = _height; this.refresh(); } - }, - bounds : { - get : function() { return bounds; }, - set : function(_bounds) { bounds = _bounds; x = bounds[0]; y = bounds[1]; width = bounds[2]; height = bounds[3]; this.refresh(); } - }, - count : { - get : function() { return count; }, - set : function(_count) { count = _count; this.refresh(); }, - } - }) -}; -Interface.MultiSlider.prototype = Interface.Widget; - -/**#Interface.MultiButton - Widget -Multiple buttons that share the same event handlers and colors. When a MultiButton sends OSC, it comes in the form of three integers representing the row of the button -pressed, the column of the button pressed, and the button's value. Any onvaluechange method attached to the MultiButton widget should have a -similar signature; see the example below. -## Example Usage## -`b = new Interface.Label({ bounds:[.5,.5,.5,.5], size:12 }); -a = new Interface.MultiButton({ - bounds:[0,0,.5,.5], - fill:'white', - rows: 4, - columns: 4, - onvaluechange : function( row, column, value) { b.setValue('row : ' + row + ', column : ' + column + ', value : ' + value) }, -}); -panel = new Interface.Panel(); -panel.add(a,b); -` -## Constructor -**param** *properties*: Object. A dictionary of property values (see below) to set for the multibutton on initialization. -- - - - -**/ -/**###Interface.MultiButton.rows : property -Number. The number of rows of buttons in the widgets. When combined with the columns property this determines the overall number of buttons in the widget. -**/ -/**###Interface.MultiButton.columns : property -Number. The number of columns of buttons in the widgets. When combined with the rows property this determines the overall number of buttons in the widget. -**/ -/**###Interface.MultiButton.mode : property -String. Can be 'toggle', 'momentary' or 'contact'. In toggle mode, the button turns on when it is pressed and off when it is pressed again. In momentary mode, the button turns on when pressed and off when released. In contact mode, the button briefly flashes when pressed and sends its value. -**/ -Interface.MultiButton = function() { - Interface.extend(this, { - type : 'MultiButton', - mode : 'toggle', - serializeMe : ["mode", "rows", "columns", "requiresFocus"], - rows: 8, - values: [], - _values: [], - lastValues: [], - mouseOver : null, - columns: 8, - - draw : function() { - var x = this._x(), - y = this._y(), - width = this._width(), - height= this._height(), - childWidth = width / this.columns, - childHeight = height / this.rows; - - this.ctx.strokeStyle = this._stroke(); - - for(var i = 0; i < this.rows; i++) { - for(var j = 0; j < this.columns; j++) { - var _x = x + j * childWidth, - _y = y + i * childHeight, - btn = i * this.columns + j; - - if(this._values[ btn ]) { - this.ctx.fillStyle = this._fill(); - }else{ - this.ctx.fillStyle = this._background(); - } - this.ctx.fillRect( _x, _y, childWidth, childHeight ); - this.ctx.strokeRect( _x, _y, childWidth, childHeight ); - } - } - }, - - setValue : function( row, col, value ) { - var btnNum = row * this.columns + col - - this._values[ btnNum ] = this.values[ btnNum ] = this.lastValues[ btnNum ] = value - this.draw() - }, - - changeValue : function( xOffset, yOffset ) { - if(this.hasFocus || !this.requiresFocus) { - var width = this._width(), - height = this._height(), - buttonWidth = width / this.columns, - columnHit = Math.floor( xOffset / buttonWidth ), - buttonHeight = height / this.rows, - rowHit = Math.floor( yOffset / buttonHeight), - buttonHit = (rowHit * this.columns) + columnHit, - _value = 0; - - - if( buttonHit !== this.mouseOver ) { - this._values[ buttonHit ] = !this._values[ buttonHit ]; - - this.values[ buttonHit ] = this._values[ buttonHit ] ? this.max : this.min; - - if(this.values[ buttonHit ] !== this.lastValues[ buttonHit ] || this.mode === 'contact') { - if(this.target !== "OSC") { - this.sendTargetMessage(); - }else{ - if(Interface.OSC) - Interface.OSC.send( this.key, 'iif', [ rowHit, columnHit, this.values[ buttonHit ] ] ); - } - - if(this.onvaluechange) this.onvaluechange( rowHit, columnHit, this.values[ buttonHit ]); - - this.draw(); - this.lastValues[ buttonHit ] = this.values[ buttonHit ]; - - if(this.mode === 'contact') { - var self = this; - setTimeout( function() { self._values[ buttonHit ] = 0; self.draw(); }, 75); - } - } - - this.mouseOver = buttonHit; - } - } - }, - - mousedown : function(e, hit) { - if(hit && Interface.mouseDown) { - this.changeValue( e.x - this._x(), e.y - this._y() ); - } - }, - mousemove : function(e, hit) { - if(hit && Interface.mouseDown) { - this.changeValue( e.x - this._x(), e.y - this._y() ); - } - }, - mouseup : function(e, hit) { - if(hit && Interface.mouseDown) this.changeValue( e.x - this._x(), e.y - this._y() ); - this.mouseOver = null; - }, - - touchstart : function(e, hit) { if(hit) this.changeValue( e.x - this._x(), e.y - this._y() ); }, - touchmove : function(e, hit) { if(hit) this.changeValue( e.x - this._x(), e.y - this._y() ); }, - touchend : function(e, hit) { if(hit) this.changeValue( e.x - this._x(), e.y - this._y() ); }, - }) - .init( arguments[0] ); - - this.requiresFocus = false; - var x = this.x, - y = this.y, - width = this.width, - height = this.height, - bounds = [x,y,width,height] - rows = this.rows, - columns = this.columns; - - delete this.bounds; - - Object.defineProperties(this, { - x : { - get : function() { return x; }, - set: function(_x) { x = _x; this.refresh(); } - }, - y : { - get : function() { return y; }, - set: function(_y) { y = _y; this.refresh(); } - }, - width : { - get : function() { return width; }, - set: function(_width) { width = _width; this.refresh(); } - }, - height : { - get : function() { return height; }, - set: function(_height) { height = _height; this.refresh(); } - }, - bounds : { - get : function() { return bounds; }, - set : function(_bounds) { bounds = _bounds; x = bounds[0]; y = bounds[1]; width = bounds[2]; height = bounds[3]; this.refresh(); } - }, - rows : { - get : function() { return rows; }, - set : function(_rows) { rows = _rows; this.refresh(); }, - }, - columns : { - get : function() { return columns; }, - set : function(_columns) { columns = _columns; this.refresh(); }, - }, - }); -}; -Interface.MultiButton.prototype = Interface.Widget; - -/**#Interface.Accelerometer - Widget -Access to the Accelerometer. Unlike the Orientation widget, this is only found on mobile devices. - -## Example Usage## -`var a = new Interface.Panel(); -var accelerometer = new Interface.Accelerometer({ - onvaluechange : function(_x,_y,_z) { - x.setValue(_x); - y.setValue(_y); - z.setValue(_z); - } -}).start(); -var x = new Interface.Slider({ - label: 'x', - bounds:[.05,.05,.2,.9] -}); -var y = new Interface.Slider({ - label: 'y', - bounds:[.25,.05,.2,.9] -}); -var z = new Interface.Slider({ - label: 'z', - bounds:[.45,.05,.2,.9] -}); - -a.background = 'black'; -a.add(x,y,z); -` -## Constructor -**param** *properties*: Object. A dictionary of property values (see below) to set for the accelerometer on initialization. -- - - - -**/ -/**###Interface.Accelerometer.x : property -Number. A read-only property that gives the current accleration on the x-axis -**/ -/**###Interface.Accelerometer.y : property -Number. A read-only property that gives the current accleration on the y-axis -**/ -/**###Interface.Accelerometer.z : property -Number. A read-only property that gives the current accleration on the z-axis -**/ -/**###Interface.Accelerometer.start : method -Starts emitting values from the Accelerometer measurements -**/ -/**###Interface.Accelerometer.stop : method -Stop emitting values from the Accelerometer measurements -**/ -/**###Interface.Accelerometer.onvaluechange : method -The event handler fired whenever an accelerometer update is received - -param **x** Number. The x-acceleration of the sensor -param **y** Number. The y-acceleration of the sensor -param **x** Number. The z-acceleration of the sensor -**/ -Interface.Accelerometer = function() { - var self = this, - metersPerSecondSquared = 9.80665; - - Interface.extend(this, { - type:"Accelerometer", - - serializeMe : ["delay"], - delay : 100, // measured in ms - min: 0, - max: 1, - values : [0,0,0], - - update : function(event) { - var acceleration = event.acceleration; - self.x = self.values[0] = self.min + ((((0 - self.hardwareMin) + acceleration.x) / self.hardwareRange ) * self.max); - self.y = self.values[1] = self.min + ((((0 - self.hardwareMin) + acceleration.y) / self.hardwareRange ) * self.max); - self.z = self.values[2] = self.min + ((((0 - self.hardwareMin) + acceleration.z) / self.hardwareRange ) * self.max); - - if(typeof self.onvaluechange !== 'undefined') { - self.onvaluechange(self.x, self.y, self.z); - } - - self.sendTargetMessage(); - }, - start : function() { - window.addEventListener('devicemotion', this.update, true); - return this; - }, - stop : function() { - window.removeEventListener('devicemotion', this.update); - return this; - }, - }) - .init( arguments[0] ); - - if(!Interface.isAndroid) { - this.hardwareMin = -2.307 * metersPerSecondSquared; // as found here: http://www.iphonedevsdk.com/forum/iphone-sdk-development/4822-maximum-accelerometer-reading.html - this.hardwareMax = 2.307 * metersPerSecondSquared; // -1 to 1 works much better for devices without gyros to measure tilt, -2 to 2 much better to measure force - }else{ - this.hardwareMin = metersPerSecondSquared; - this.hardwareMax = metersPerSecondSquared; - } - - this.hardwareRange = this.hardwareMax - this.hardwareMin; -}; -Interface.Accelerometer.prototype = Interface.Widget; - -/**#Interface.Orientation - Widget -Access to the device's Orientation. This is only found on mobile devices, with the exception of Google Chrome, which provides pitch and roll. - -## Example Usage## -`var a = new Interface.Panel() - -var orientation = new Interface.Orientation({ - onvaluechange : function(_pitch, _roll, _yaw, _heading) { - pitch.setValue(_pitch); - roll.setValue(_roll); - yaw.setValue(_yaw); - } -}); -var pitch = new Interface.Slider({ - label: 'pitch', - bounds:[.05,.05,.2,.9] -}); -var roll = new Interface.Slider({ - label: 'roll', - bounds:[.25,.05,.2,.9] -}); -var yaw = new Interface.Slider({ - label: 'yaw', - bounds:[.45,.05,.2,.9] -}); - -a.add(pitch, roll, yaw); -` -## Constructor -**param** *properties*: Object. A dictionary of property values (see below) to set for the orientation on initialization. -- - - - -**/ -/**###Interface.Orientation.pitch : property -Number. A read-only property that gives the current orientation on the x-axis -**/ -/**###Interface.Orientation.roll : property -Number. A read-only property that gives the current orientation on the y-axis -**/ -/**###Interface.Orientation.yaw : property -Number. A read-only property that gives the current orientation on the z-axis -**/ -/**###Interface.Orientation.start : method -Starts emitting values from the Orientation measurements -**/ -/**###Interface.Orientation.stop : method -Stop emitting values from the Orientation measurements -**/ -/**###Interface.Orientation.onvaluechange : method -The event handler fired whenever an orientation update is received - -param **pitch** Number. The pitch of the sensor -param **roll** Number. The roll of the sensor -param **yaw** Number. The yaw of the sensor -param **heading** Number. The heading of the sensor, this corresponds to the compass direction detected. -**/ -Interface.Orientation = function() { - var _self = this; - - Interface.extend(this, { - type:"Orientation", - serializeMe : ["delay"], - delay : 100, // measured in ms - values : [0,0,0], - update : function(orientation) { - _self.roll = _self.values[0] = _self.min + ((90 + orientation.gamma) / 180 ) * _self.max ; - _self.pitch = _self.values[1] = _self.min + ((180 + orientation.beta) / 360 ) * _self.max ; - _self.yaw = _self.values[2] = _self.min + (orientation.alpha / 360 ) * _self.max ; - - if( !isNaN(orientation.webkitCompassHeading) ) { - _self.heading = _self.min + ((orientation.webkitCompassHeading / 360 ) * _self.max ); - } - - _self.sendTargetMessage(); - - if(typeof _self.onvaluechange !== 'undefined') { - _self.onvaluechange(_self.pitch, _self.roll, _self.yaw, _self.heading); - } - }, - start : function() { - window.addEventListener('deviceorientation', function (event) { - _self.update(event); - }, true); - return this; - }, - stop : function() { - window.removeEventListener('deviceorientation'); - }, - }) - .init( arguments[0] ); -}; -Interface.Orientation.prototype = Interface.Widget; - -Interface.Range = function() { - Interface.extend(this, { - type:"Range", - serializeMe : ["handleSize"], - handleSize: 20, - values:[0,1], - _values:[0,1], - draw : function() { - var x = this._x(), - y = this._y(), - width = this._width(), - height= this._height(); - - this.ctx.fillStyle = this._background(); - this.ctx.clearRect(x, y, width, height); - - var rightHandlePos = x + (this._values[1] * width) - this.handleSize; - var leftHandlePos = x + this._values[0] * width; - - this.ctx.fillStyle = this._background(); - this.ctx.fillRect(x, y, width, height); - - this.ctx.fillStyle = this._fill(); - this.ctx.fillRect(leftHandlePos, y, rightHandlePos - leftHandlePos, height); - - this.ctx.fillStyle = this._stroke(); - this.ctx.fillRect(leftHandlePos, y, this.handleSize, height); - - //this.ctx.fillStyle = "rgba(0,255,0,.25)"; - this.ctx.fillRect(rightHandlePos, y, this.handleSize, height); - - this.ctx.strokeStyle = this._stroke(); - this.ctx.strokeRect(x, y, width, height); - }, - changeValue : function( xOffset, yOffset ) { - if(this.hasFocus || !this.requiresFocus) { - var value = this.isVertical ? 1 - (yOffset / this._height()) : xOffset / this._width(); - - if(value < 0) { - value = 0; - }else if(value > 1) { - value = 1; - } - - var range = this.max - this.min - if(Math.abs( value - this._values[0]) < Math.abs( value - this._values[1])) { - this._values[0] = value; - this.values[0] = this.min + range * value; - }else{ - this._values[1] = value; - this.values[1] = this.min + range * value; - } - - this.refresh(); - - if(this.values[0] !== this.lastLeftValue || this.values[1] !== this.lastRightValue) { - if(this.onvaluechange) this.onvaluechange(this.values[0], this.values[1]); - this.refresh(); - this.lastLeftValue = this.values[0]; - this.lastRightValue = this.values[1]; - this.sendTargetMessage(); - } - } - }, - - mousedown : function(e, hit) { if(hit && Interface.mouseDown) this.changeValue( e.x - this._x(), e.y - this._y() ); }, - mousemove : function(e, hit) { if(hit && Interface.mouseDown) this.changeValue( e.x - this._x(), e.y - this._y() ); }, - mouseup : function(e, hit) { if(hit && Interface.mouseDown) this.changeValue( e.x - this._x(), e.y - this._y() ); }, - - touchstart : function(e, hit) { if(hit) this.changeValue( e.x - this._x(), e.y - this._y() ); }, - touchmove : function(e, hit) { if(hit) this.changeValue( e.x - this._x(), e.y - this._y() ); }, - touchend : function(e, hit) { if(hit) this.changeValue( e.x - this._x(), e.y - this._y() ); }, - }) - .init( arguments[0] ); -} -Interface.Range.prototype = Interface.Widget; - -Interface.Paint = function() { - Interface.extend( this, { - lines: [], - startTime: 0, - isAnimating: false, - animationPoint: 0, - timer: null, - shouldLoop: true, - prevTimestamp:null, - values:[0,0], - draw : function() { - var x = this._x(), - y = this._y(), - width = this._width(), - height= this._height(); - - this.ctx.fillStyle = this._background(); - //this.ctx.fillRect( this.x, this.y, this.width, this.height ); - - this.ctx.strokeStyle = this._stroke(); - //this.ctx.strokeRect( this.x, this.y, this.width, this.height ); - - this.ctx.save(); - - this.ctx.beginPath(); - - this.ctx.moveTo(x, y); - this.ctx.lineTo(x + width, y); - this.ctx.lineTo(x + width, y + height); - this.ctx.lineTo(x, y + height); - this.ctx.lineTo(x, y); - this.ctx.fill(); - this.ctx.stroke(); - - this.ctx.clip(); - - this.ctx.fillStyle = this._fill(); - - if( this.lines.length >= 1 ) { - this.ctx.lineWidth = 8 - for( var i = 0; i < this.lines.length; i++ ) { - var points = this.lines[ i ] - - if( points.length >= 2 ) { - this.ctx.moveTo( points[0].x * width, points[0].y * height ) - - this.ctx.beginPath() - - for ( var j = 1; j < points.length - 2; j++ ) { - var xc = ( points[ j ].x + points[ j + 1 ].x ) / 2 - var yc = ( points[ j ].y + points[ j + 1 ].y ) / 2 - this.ctx.quadraticCurveTo( points[ j ].x * width, points[ j ].y * height, xc * width, yc * height ) - } - - this.ctx.stroke() - //this.ctx.closePath(); - } - } - } - - this.ctx.restore(); - }, - - /* - touchEvent : function(touch) { - var isHit = this.hitTest(touch); - var touchMouseName = convertTouchEvent(touch.type); - - if(isHit) { - if(touch.type === 'touchstart') { - this.hasFocus = true; - this.touchCount++; - this.trackTouch(touch.x - this._x(), touch.y - this._y(), touch); - }else{ - if(this[touch.type]) - this[touch.type](touch, isHit, touch.childID); // normal event - } - - if(this['on'+touch.type]) this['on'+touch.type](touch, isHit, touch.childId); // user defined event - if(this['on'+touchMouseName]) this['on'+touchMouseName](touch, isHit); // user defined event - - }else if(touch.type === 'touchend'){ - this.touchCount--; - if(this.touchCount === 0) { - this.hasFocus = false; - }else if(this.touchCount < 0 ) { - this.touchCount = 0; - } - this.touchend(touch) - if(this['on'+touch.type]) this['on'+touch.type](touch, isHit, touch.childId); // user defined event - if(this['on'+touchMouseName]) this['on'+touchMouseName](touch, isHit); // user defined event - } - }, - - trackMouse : function(xPos, yPos, id) {}, - */ - animate : function(co) { - var me = this; - - if( this.isAnimating === false ) return - //console.log( this.lineNo, this._lines.length, this.speedMod ) - var line = this.lines[ 0 ] - - // if( typeof this.line === 'undefined' ) { - // this.context.fillStyle = '#fff' - // this.context.fillRect( 0, 0, this.canvas[0].width, this.canvas[0].height ) - // this.lineNo = this.pointNo = this.lines.length = 0 - // if( this.shouldLoop ) setTimeout( function() { me.drawBackground(); me.animate() }, this.endTime ) - // return - // } - var point = line[ this.animationPoint ], - nextPoint = line[ this.animationPoint + 1 ] - - //console.log( point, nextPoint ) - - if( this.animationPoint >= line.length - 1 ) { - if( this.shouldLoop ) { - this.animationPoint = 0 - this.draw() - //var time = this.shouldLoop ? this.endTime : (this.point.timestamp - this.prevTimestamp) - this.timeout = setTimeout( function() { me.animate() }, 5 ) - return - }else{ - this.ctx.fillStyle = this._background() - this.ctx.fillRect( 0, 0, this._width(), this._height() ) - if( this.speedMod !== 0 ) return - } - } - - this.ctx.save(); - this.ctx.strokeStyle = '#f00' - this.ctx.lineWidth = 8 - - this.ctx.beginPath() - - this.ctx.moveTo( point.x * this._width(), point.y * this._height() ) - // var xc = ( point.x + nextPoint.x ) / 2 - // var yc = ( point.y + nextPoint.y ) / 2 - // this.ctx.quadraticCurveTo( nextPoint.x * this._width(), nextPoint.y * this._height(), xc * this._width(), yc * this._height() ) - this.ctx.lineTo( nextPoint.x * this._width(), nextPoint.y * this._height() ) - - this.ctx.stroke() - this.ctx.restore() - - this.timeout = setTimeout( function() { me.animate() }, point.timestamp - this.prevTimestamp ) - this.prevTimestamp = point.timestamp - - this.animationPoint++ - - this.values = [ point.x, point.y ] - this.sendTargetMessage() - }, - - startAnimation: function() { - var self = this - - this.animate() - }, - stopAnimation: function() { - if( this.timer ) { - clearInterval( this.timer ) - } - }, - mousedown : function(e) { - if(this.hitTest(e)) { - - this.lines = [] - this.animationPoint = 0 - - if( this.lines.length === 0 ) { - this.startTime = Date.now() - }else{ - // if( this.lines[ this.lines.length - 1 ].length < 2 ) { - // this.lines.pop() - // } - } - - this.lines.push( [] ) - this.isDrawing = true; - this.isAnimating = false; - } - }, - mousemove : function(e) { - if(this.hitTest(e) && this.activeTouch !== null) { - //ctx.fillStyle = '#000' - - //if( e.pageX > canvas.width ) isDrawing = false - if( this.isDrawing ) { - var points = this.lines[ this.lines.length - 1 ] - if( points ) { - points.push({ x:e.x / this._width(), y:e.y / this._height(), timestamp: Date.now() - this.startTime }) - this.draw() - } - } - } - }, - mouseup : function(e) { - this.isDrawing = false - if( this.lines.length > 0 ) { - this.isAnimating = true; - this.animate() - } - }, - touchstart : function(touch) { - if(this.hitTest(touch)) { - this.lines = [] - this.animationPoint = 0 - - if( this.lines.length === 0 ) { - this.startTime = Date.now() - } - - this.lines.push( [] ) - this.isDrawing = true; - this.isAnimating = false; - } - - this.activeTouch = touch - }, - - touchmove : function(touch) { - if(this.hitTest(touch) && this.activeTouch !== null) { - if( this.isDrawing ) { - var points = this.lines[ this.lines.length - 1 ] - if( points ) { - points.push({ x:touch.x / this._width(), y:touch.y / this._height(), timestamp: Date.now() - this.startTime }) - this.draw() - } - } - } - }, - - touchend : function(touch) { - this.isDrawing = false - if( this.lines.length > 0 ) { - this.isAnimating = true; - this.animate() - } - }, - }) - .init( arguments[0] ); -} -Interface.Paint.prototype = Interface.Widget; - -Interface.Patchbay = function() { - Interface.extend(this, { - type:"Patchbay", - points: [], - minWidth:80, - cableWidth:5, - start:null, - over:null, - connections:[], - rowLength:null, - selectedConnection: null, - patchOutlineWidth:3, - - draw : function() { - var x = this._x(), y = this._y(), width = this._width(), height = this._height(), - length = this.points.length - - this.ctx.fillStyle = this._background(); - this.ctx.strokeStyle = this._stroke(); - this.ctx.clearRect(x, y, width, height); - - this.layout() - this.drawSegments() - this.drawPatchPoints() - this.drawConnections() - //this.drawLabels() - }, - - layout: function() { - var x = this._x(), y = this._y(), width = this._width(), height = this._height() - - this.rows = 1 - - this.patchWidth = width / this.points.length - - if( this.patchWidth < this.minWidth ) { - this.patchWidth = this.minWidth - } - - this.rows = Math.ceil( (this.patchWidth * this.points.length) / width ) - - this.patchHeight = height / this.rows - - - this.columns = Math.floor( width / this.patchWidth ) - }, - - drawSegments : function() { - var x = this._x(), y = this._y(), width = this._width(), height = this._height(), - length = this.points.length - - this.ctx.fillStyle = this._fill(); - - var totalWidth = 0, row = 1 - - //console.log("SEGMENT, START:", this.start, 'OVER:', this.over ) - for( var i = 0; i < this.points.length; i++ ) { - if( this.start === i ) { - this.ctx.fillStyle = "#777" - this.ctx.fillRect(x + totalWidth, y + (this.patchHeight * (row-1)), this.patchWidth, this.patchHeight ); - }else if( this.over === i ) { - this.ctx.fillStyle = "#744" - this.ctx.fillRect(x + totalWidth, y + (this.patchHeight * (row-1)), this.patchWidth, this.patchHeight ); - } - - this.ctx.fillStyle = this._stroke() - this.ctx.textBaseline = 'middle' - this.ctx.textAlign = 'center' - this.ctx.font = this._font() - this.ctx.font = 'normal 12px Helvetica' - - if( typeof this.points[i].name !== 'undefined' ) { - this.ctx.fillText( this.points[ i ].name , totalWidth + this.patchWidth / 2, y + ((row-1) * this.patchHeight + .1 * this.patchHeight) ) - } - - if( typeof this.points[i].name2 !== 'undefined' ) { - this.ctx.fillText( this.points[ i ].name2 , totalWidth + this.patchWidth / 2, y + ((row-1) * this.patchHeight + .9 * this.patchHeight) ) - } - - totalWidth += this.patchWidth - - this.points[ i ].row = row - - if( totalWidth + this.patchWidth > width ) { - totalWidth = 0 - row++ - } - } - }, - - drawPatchPoints : function() { - var x = this._x(), y = this._y(), width = this._width(), height = this._height(), - length = this.points.length - - this.ctx.fillStyle = this._background(); - - var totalWidth = 0, row = 1 - for( var i = 0; i < this.points.length; i++ ) { - //this.ctx.fillRect(totalWidth, y, patchWidth, patchHeight); - this.ctx.beginPath() - this.ctx.arc( totalWidth + this.patchWidth / 2, y + this.patchHeight / 2 + (this.patchHeight * (row-1)), this.patchWidth/4, 0, Math.PI*2, true); - this.ctx.closePath() - - this.ctx.fill() - - this.ctx.lineWidth = this.patchOutlineWidth - this.ctx.stroke() - - this.points[i].row = row - - this.ctx.lineWidth = 1 - this.ctx.strokeRect(totalWidth, y + (this.patchHeight * (row-1)), this.patchWidth, this.patchHeight ); - - totalWidth += this.patchWidth - if( totalWidth + this.patchWidth > width ) { - totalWidth = 0 - row++ - } - } - - //console.log("TOTAL ROWS = ", row ) - }, - - drawConnections : function() { - var x = this._x(), y = this._y(), width = this._width(), height = this._height() - - this.ctx.lineWidth = this.cableWidth - - for( var i = 0; i < this.connections.length; i++ ) { - var connection = this.connections[ i ], - origin = this.connections[ i ][ 0 ], - destination = this.connections[ i ][ 1 ], - startX = x + this.patchWidth * (origin % this.columns) + this.patchWidth / 2, - startY = y + (this.patchHeight / 2) + (this.patchHeight * Math.floor(origin / this.columns) ), - endX = x + this.patchWidth * (destination % this.columns) + this.patchWidth / 2, - endY = y + (this.patchHeight / 2) + (this.patchHeight * Math.floor(destination / this.columns) ), - ctrl1X = startX, - ctrl1Y = startY + this.patchHeight * .5, - ctrl2X = endX, - ctrl2Y = endY + this.patchHeight * .5 - - //console.log( "ORIGIN", this.points[origin].row, "DESTINATION", this.points[destination].row ) - if( connection.selected ) { - this.ctx.strokeStyle = '#0f0' - }else{ - var grd = this.ctx.createLinearGradient(startX, startY, endX, endY); - - grd.addColorStop( 0.000, 'rgba(64, 64, 64, 1.000)' ) - grd.addColorStop( 1.000, 'rgba(204, 204, 204, 1.000)' ) - - - this.ctx.strokeStyle = grd - } - - this.ctx.beginPath(); - this.ctx.moveTo( startX, startY ) - this.ctx.bezierCurveTo( ctrl1X, ctrl1Y, ctrl2X, ctrl2Y, endX, endY ); - this.ctx.stroke() - - connection.edge = [startX, startY, ctrl1X, ctrl1Y, ctrl2X, ctrl2Y, endX, endY] - } - }, - - _init : function() { - var x = this._x(), - y = this._y(), - width = this._width(), - height= this._height() - - this.patchWidth = width / this.points.length - this.patchHeight = height - this.rows = 1 - }, - - createConnection : function( connection ) { - var start = this.points[ connection[0] ], - end = this.points[ connection[1] ] - - if( end.output !== false ) { - this.connections.push( connection ) - - if( this.onconnection ) { - this.onconnection( start, end ) - } - } - }, - - changeValue : function( xOffset, yOffset ) { }, - - hitTestEdges: function(e) { - var hit = false, - x = e.x - this._x(), - y = e.y - this._y() - - for( var i = 0; i < this.connections.length; i++ ) { - var edge = this.connections[ i ].edge - - this.ctx.beginPath() - this.ctx.moveTo( edge[0], edge[1] ) - this.ctx.bezierCurveTo( edge[2], edge[3], edge[4], edge[5], edge[6], edge[7] ); - if( this.ctx.isPointInStroke( x,y ) ) { - this.connections.forEach( function( elem, index, array ){ - elem.selected = false - }) - - this.connections[ i ].selected = true - this.selectedConnection = this.connections[ i ] - - hit = true - - break; - } - } - - return hit - }, - - mousedown : function(e, hit) { - if( hit && Interface.mouseDown ) { - if( !this.hitTestEdges( e ) ) { - //this.start = Math.floor( ( e.x - this._x() / this._width() / this.rows ) / ( this._width() / this.points.length / this.rows ) ) - var _x = Math.floor( ( e.x - this._x() / this._width() ) / ( this._width() / this.columns ) ), - _y = Math.floor( ( e.y - this._y() / this._height()) / ( this._height() / this.rows ) ) - - this.start = _y * this.columns + _x - - if( this.selectedConnection !== null ) { - this.selectedConnection.selected = false - this.selectedConnection = null - } - } - - this.draw() - } - }, - mousemove : function(e, hit) { - if( hit && Interface.mouseDown ) { - var _x = Math.floor( ( e.x - this._x() / this._width() ) / ( this._width() / this.columns) ), - _y = Math.floor( ( e.y - this._y() / this._height()) / ( this._height() / this.rows ) ) - - var prevOver = this.over - this.over = _y * this.columns + _x - - if( this.over !== prevOver ) { - this.draw() - } - } - }, - mouseup : function(e, hit) { - if( hit ) { - var _x = Math.floor( ( e.x - this._x() / this._width() ) / ( this._width() / this.columns ) ), - _y = Math.floor( ( e.y - this._y() / this._height()) / ( this._height() / this.rows ) ), - over = _y * this.columns + _x - - // var over = Math.floor( ( e.x - this._x() / this._width() / this.rows ) / ( this._width() / this.points.length / this.rows ) ) - - if( this.start !== over && this.start !== null ) { - var connection = [ this.start, over ], - isFound = false - - for( var i = 0; i < this.connections.length; i++ ) { - if( this.connections[i][0] === connection[0] && this.connections[i][1] === connection[1] ) { - isFound = true - } - } - - if( !isFound ) this.createConnection( connection ) - } - } - - this.over = null - this.start = null - this.draw() - }, - - onkeydown: function(e) { - var key = Interface.keyCodeToChar[ e.keyCode ] - - if( key === 'Delete' || key === 'Backspace' ) { - if( this.selectedConnection !== null ) { - this.deleteConnection( this.selectedConnection ) - e.preventDefault() - } - } - }, - - deleteConnection: function( connection ) { - this.connections.splice( this.connections.indexOf( connection ), 1 ) - - if( this.ondisconnection ) { this.ondisconnection( this.points[ connection[0] ], this.points[ connection[1] ] ) } - - this.draw() - }, - - touchstart : function(e, hit) { if(hit) this.changeValue( e.x - this._x(), e.y - this._y() ); }, - touchmove : function(e, hit) { if(hit) this.changeValue( e.x - this._x(), e.y - this._y() ); }, - touchend : function(e, hit) { if(hit) this.changeValue( e.x - this._x(), e.y - this._y() ); }, - }) - .init( arguments[0] ); -} -Interface.Patchbay.prototype = Interface.Widget; - -Interface.defineChildProperties = function(widget, properties) { - for(var j = 0; j < properties.length; j++) { - (function() { - var key = properties[j]; - var val = widget[key] - Object.defineProperty(widget, key, { - get: function() { return val; }, - set: function(_val) { - val = _val; - for(var i = 0; i < widget.children.length; i++) { - widget.children[i][key] = val; - } - } - }); - })(); - } -}; - -// pub/sub for jquery && zepto, see https://github.com/martinjuhasz/pubsub-zepto/blob/master/pubsub.js -(function ($) { - var cache = {}; - - $.publish = function(/* String */topic, /* Array? */args){ - if(typeof cache[topic] === 'object') { - cache[topic].forEach(function(property){ - property.apply($, args || []); - }); - } - }; - - $.subscribe = function(/* String */topic, /* Function */callback){ - if(!cache[topic]){ - cache[topic] = []; - } - cache[topic].push(callback); - return [topic, callback]; // Array - }; - - $.unsubscribe = function(/* Array */handle){ - var t = handle[0]; - cache[t] && $.each(cache[t], function(idx){ - if(this == handle[1]){ - cache[t].splice(idx, 1); - } - }); - }; - -})(window.jQuery); \ No newline at end of file diff --git a/examples/deps/nexusUI.js b/examples/deps/nexusUI.js new file mode 100644 index 00000000..de9192f5 --- /dev/null +++ b/examples/deps/nexusUI.js @@ -0,0 +1,6602 @@ +(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'; + + 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=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 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 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 + + ``` + +*/ + +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 + + ``` + +*/ + + 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: + impulse   1 on click
+ toggle   1 on click, 0 on release _(default)_
+ aftertouch   1, x, y on click; x, y on move; 0, x, y on release
+ ```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 + + ``` + +*/ + + +// 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 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.
This is also the widget's data output (See widget.val).
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 + + ``` + +*/ + +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 + + ``` + +*/ + +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
+ */ + 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 + + ``` + +*/ + +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 + + ``` + +*/ + +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.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 + ``` + +*/ + +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 + + ``` + +*/ + +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.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;j1 || this.multitouch) { + this.keysinuse = new Array(); + for (var j=0;j1 || this.multitouch) { + this.keysinuse = new Array(); + for (var j=0;j + ``` + +*/ + + +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 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 + + ``` + +*/ + +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 + + ``` + +*/ + +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=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 + + ``` + +*/ + +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 + + ``` + +*/ +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.
**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;i1) { + + for (var i=0;i 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 + ``` + +*/ + +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=1) { + for (var k=0;k=1) { + for (var i=0;i + ``` + +*/ + +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 + + ``` + +*/ + +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 + + ``` + +*/ + +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 + + ``` + +*/ + +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 **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 + + ``` + +*/ + +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 = '' + 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 + ``` + +*/ + +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 + + ``` + +*/ + +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 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.height/(this.strings.length*3)) { + + this.pluck(i) + + } + } + } +} + +string.prototype.release = function() { + for (var i = 0;i **Notes:** Clicking on this widget toggles it inactive or active.
+ We recommend not calling .init() on this object after the original initialization, because it will add additional redundant tilt listeners to your document. + ```html + + ``` + +*/ + +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 + + ``` + +*/ + +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)
**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 + + ``` + +*/ + +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 + ``` + +*/ + +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]); diff --git a/examples/deps/prism.js b/examples/deps/prism.js new file mode 100644 index 00000000..47515c3e --- /dev/null +++ b/examples/deps/prism.js @@ -0,0 +1,4 @@ +/* http://prismjs.com/download.html?themes=prism&languages=clike+javascript */ +self="undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{};var Prism=function(){var e=/\blang(?:uage)?-(?!\*)(\w+)\b/i,t=self.Prism={util:{encode:function(e){return e instanceof n?new n(e.type,t.util.encode(e.content),e.alias):"Array"===t.util.type(e)?e.map(t.util.encode):e.replace(/&/g,"&").replace(/e.length)break e;if(!(d instanceof a)){g.lastIndex=0;var m=g.exec(d);if(m){c&&(f=m[1].length);var y=m.index-1+f,m=m[0].slice(f),v=m.length,k=y+v,b=d.slice(0,y+1),w=d.slice(k+1),O=[p,1];b&&O.push(b);var N=new a(l,u?t.tokenize(m,u):m,h);O.push(N),w&&O.push(w),Array.prototype.splice.apply(r,O)}}}}}return r},hooks:{all:{},add:function(e,n){var a=t.hooks.all;a[e]=a[e]||[],a[e].push(n)},run:function(e,n){var a=t.hooks.all[e];if(a&&a.length)for(var r,i=0;r=a[i++];)r(n)}}},n=t.Token=function(e,t,n){this.type=e,this.content=t,this.alias=n};if(n.stringify=function(e,a,r){if("string"==typeof e)return e;if("[object Array]"==Object.prototype.toString.call(e))return e.map(function(t){return n.stringify(t,a,e)}).join("");var i={type:e.type,content:n.stringify(e.content,a,r),tag:"span",classes:["token",e.type],attributes:{},language:a,parent:r};if("comment"==i.type&&(i.attributes.spellcheck="true"),e.alias){var l="Array"===t.util.type(e.alias)?e.alias:[e.alias];Array.prototype.push.apply(i.classes,l)}t.hooks.run("wrap",i);var o="";for(var s in i.attributes)o+=s+'="'+(i.attributes[s]||"")+'"';return"<"+i.tag+' class="'+i.classes.join(" ")+'" '+o+">"+i.content+""},!self.document)return self.addEventListener?(self.addEventListener("message",function(e){var n=JSON.parse(e.data),a=n.language,r=n.code;self.postMessage(JSON.stringify(t.util.encode(t.tokenize(r,t.languages[a])))),self.close()},!1),self.Prism):self.Prism;var a=document.getElementsByTagName("script");return a=a[a.length-1],a&&(t.filename=a.src,document.addEventListener&&!a.hasAttribute("data-manual")&&document.addEventListener("DOMContentLoaded",t.highlightAll)),self.Prism}();"undefined"!=typeof module&&module.exports&&(module.exports=Prism);; +Prism.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\w\W]*?\*\//g,lookbehind:!0},{pattern:/(^|[^\\:])\/\/.*?(\r?\n|$)/g,lookbehind:!0}],string:/("|')(\\\n|\\?.)*?\1/g,"class-name":{pattern:/((?:(?:class|interface|extends|implements|trait|instanceof|new)\s+)|(?:catch\s+\())[a-z0-9_\.\\]+/gi,lookbehind:!0,inside:{punctuation:/(\.|\\)/}},keyword:/\b(if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/g,"boolean":/\b(true|false)\b/g,"function":{pattern:/[a-z0-9_]+\(/gi,inside:{punctuation:/\(/}},number:/\b-?(0x[\dA-Fa-f]+|\d*\.?\d+([Ee]-?\d+)?)\b/g,operator:/[-+]{1,2}|!|<=?|>=?|={1,3}|&{1,2}|\|?\||\?|\*|\/|~|\^|%/g,ignore:/&(lt|gt|amp);/gi,punctuation:/[{}[\];(),.:]/g};; +Prism.languages.javascript=Prism.languages.extend("clike",{keyword:/\b(break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|false|finally|for|function|get|if|implements|import|in|instanceof|interface|let|new|null|package|private|protected|public|return|set|static|super|switch|this|throw|true|try|typeof|var|void|while|with|yield)\b/g,number:/\b-?(0x[\dA-Fa-f]+|\d*\.?\d+([Ee][+-]?\d+)?|NaN|-?Infinity)\b/g,"function":/(?!\d)[a-z0-9_$]+(?=\()/gi}),Prism.languages.insertBefore("javascript","keyword",{regex:{pattern:/(^|[^/])\/(?!\/)(\[.+?]|\\.|[^/\r\n])+\/[gim]{0,3}(?=\s*($|[\r\n,.;})]))/g,lookbehind:!0}}),Prism.languages.markup&&Prism.languages.insertBefore("markup","tag",{script:{pattern:/[\w\W]*?<\/script>/gi,inside:{tag:{pattern:/|<\/script>/gi,inside:Prism.languages.markup.tag.inside},rest:Prism.languages.javascript},alias:"language-javascript"}});; diff --git a/examples/deps/qwerty-hancock.js b/examples/deps/qwerty-hancock.js index cfd515a9..c8add9e9 100644 --- a/examples/deps/qwerty-hancock.js +++ b/examples/deps/qwerty-hancock.js @@ -177,7 +177,7 @@ key.el.style.borderRight = 0; key.el.style.height = settings.height + 'px'; key.el.style.width = key.width + 'px'; - key.el.style.borderRadius = '0 0 5px 5px'; + key.el.style.borderRadius = '0 0 2px 2px'; }; /** diff --git a/examples/envelope.html b/examples/envelope.html index 0a7be2c7..137fe86c 100644 --- a/examples/envelope.html +++ b/examples/envelope.html @@ -5,77 +5,73 @@ Envelope - - - - + - - - - + + + + + + + + + + -
-
- ADSR Envelope -
-
- Click the button to trigger the attack/decay portion of the envelope. - Let go of the button to trigger the release. -
-
-
- - - +
+ ADSR Envelope +
+
+ Click the button to trigger the attack/decay portion of the envelope. + Let go of the button to trigger the release. +
+
+
+
+
+ + + + + \ No newline at end of file diff --git a/examples/index.html b/examples/index.html index 9edc572b..df055a4e 100644 --- a/examples/index.html +++ b/examples/index.html @@ -12,7 +12,7 @@ - + diff --git a/examples/json.html b/examples/json.html index d10cbf6b..2f3ecd22 100644 --- a/examples/json.html +++ b/examples/json.html @@ -6,36 +6,58 @@ - - - - - - - + + + + + + + + + + - -
-
- JSON Sandbox -
-
- GUIs are limited. The power of a library like Tone comes from being able to set any value you want, - not just the predescribed ranges of a GUI. Instruments and effects can be created and set - with object descriptions like the one above. Edit the values of the JSON descriptions for the score - and the MonoSynths then click 'reload' to hear your changes. + +
+ JSON Sandbox +
+
+ GUIs are limited. The power of a library like Tone comes from being able to set any value you want, + not just the predescribed ranges of a GUI. Instruments and effects can be created and set + with object descriptions like the ones below. Edit the values of the JSON descriptions for the score + and the synth then hit "enter" to hear your changes. +
+
+
+
+
-
+
+ +
+
- - var synth = new Tone.MonoSynth(synthOptions); - synth.setVolume(-10); - synth.toMaster(); + - new GUI.TopBar(Tone); + - - \ No newline at end of file diff --git a/examples/noises.html b/examples/noises.html index 7e0e1186..ac087efa 100644 --- a/examples/noises.html +++ b/examples/noises.html @@ -6,77 +6,62 @@ - - - - - - - + + + + + + + + + + - -
-
- Noise -
-
- Tone has 3 different types of noise. Here's a little noise song. -
-
-
-
-
- - + \ No newline at end of file diff --git a/examples/player.html b/examples/player.html index e9a24c86..f2070c63 100644 --- a/examples/player.html +++ b/examples/player.html @@ -7,63 +7,71 @@ - - - - - - + + + + + + + + -
-
- Player -
-
- Press and hold to hear a 10ms grain of audio. -
-
-
Loading...
-
+ +
+ Player +
+
+ Press "on" to hear a grain from the player. Change the start and end points of the grain by adjusting the range slider.
- + + - \ No newline at end of file diff --git a/examples/ExampleList.js b/examples/scripts/ExampleList.js similarity index 95% rename from examples/ExampleList.js rename to examples/scripts/ExampleList.js index 3bedfffb..feed6dfd 100644 --- a/examples/ExampleList.js +++ b/examples/scripts/ExampleList.js @@ -15,7 +15,6 @@ var ExampleList = { "AutoPanner" : "autoPanner", "PingPongDelay" : "pingPongDelay", "Buses" : "buses", - "Convolver" : "convolver" }, "Instruments" : { "MonoSynth" : "monoSynth", diff --git a/examples/scripts/Interface.js b/examples/scripts/Interface.js new file mode 100644 index 00000000..b9a4144a --- /dev/null +++ b/examples/scripts/Interface.js @@ -0,0 +1,231 @@ +/* globals Tone, nx */ + +//nexusUI setup +nx.showLabels = true; +nx.colorize("accent", "#D76767"); +nx.colorize("fill", "#fff"); + +var Interface = {}; + +$(function(){ + var topbar = $("
").attr("id", "TopBar"); + $("body").prepend(topbar); + $("
") + .attr("id", "Homepage") + .attr("title", "github") + .html("Tone.js") + .appendTo(topbar); + $("
") + .attr("id", "Examples") + .attr("title", "examples") + .html("examples") + .appendTo(topbar); + //get the master output + if (Tone && Tone.Master){ + var meter = new Tone.Meter(2); + Tone.Master.connect(meter); + var meterElement = $("
").attr("id", "Meter").appendTo(topbar); + var leftLevel = $("
").addClass("Level") + .attr("id", "Left") + .appendTo(meterElement); + var rightLevel = $("
").addClass("Level") + .attr("id", "Right") + .appendTo(meterElement); + function update(){ + requestAnimationFrame(update); + var leftHeight = 100 - Math.max(Math.min(Math.abs(meter.getDb(0)), 100), 0); + var rightHeight = 100 - Math.max(Math.min(Math.abs(meter.getDb(1)), 100), 0); + leftLevel.height(leftHeight + "%"); + rightLevel.height(rightHeight + "%"); + } + update(); + } +}); + +$(window).resize(function(){ + for (var name in nx.widgets){ + var widg = nx.widgets[name]; + widg.init(); + } +}); + +Interface.Rack = function(id, name, collapsible){ + var element = $("#"+id); + element.addClass("Rack"); + var title = $("
").addClass("Title").text(name); + element.prepend(title); + title.on("click touch", function(){ + element.toggleClass("Expanded"); + }); + if (collapsible){ + element.addClass("Collapsible"); + } + return { + close : function(){ + element.removeClass("Expanded"); + }, + open : function(){ + element.addClass("Expanded"); + } + }; +}; + +Interface.Toggle = function(container, callback){ + var toggle = nx.add("toggle", { + parent : container + }); + toggle.on("value", function(val){ + callback(val === 1); + }); +}; + +Interface.DropDown = function(container, options, callback){ + var select = nx.add("select", { + parent : container + }); + select.choices = options; + select.init(); + select.on("text", function(val){ + callback(val); + }); +}; + +Interface.ContinuousControl = function(container, type, node, parameter, min, max, exp){ + min = min || 0; + max = max || 1; + exp = exp || 1; + var isTone = (node[parameter] instanceof Tone || node[parameter] instanceof AudioParam); + var currentValue = isTone?node[parameter].value : node[parameter]; + currentValue = nx.scale(currentValue, min, max, 0, 1); + currentValue = Math.pow(currentValue, 1/exp); + var slider = nx.add(type, { + parent : container, + }); + slider.val.value = currentValue; + slider.label = parameter; + slider.on("value", function(val){ + val = Math.pow(val, exp); + var scaledVal = nx.scale(val, 0, 1, min, max); + if (isTone){ + node[parameter].value = scaledVal; + } else { + node[parameter] = scaledVal; + } + }); + slider.draw(); + slider.listen = function(){ + requestAnimationFrame(slider.listen); + var val = node[parameter]; + if (isTone){ + val = node[parameter].value; + } + val = nx.scale(val, min, max, 0, 1); + val = Math.pow(val, 1/exp); + if (val !== slider.val.value){ + slider.val.value = val; + slider.draw(); + } + }; + return slider; +}; + +Interface.Slider = function(container, node, parameter, min, max, exp){ + return Interface.ContinuousControl(container, "slider", node, parameter, min, max, exp); +}; + +Interface.HorizontalSlider = function(container, node, parameter, min, max, exp){ + var slider = Interface.Slider(container, node, parameter, min, max, exp); + $(slider.canvas).addClass("HorizontalSlider"); + return slider; +}; + +Interface.Code = function(container, codeID){ + Interface.Rack(container, "Code", true); + var element = $("#"+container); + var codeContainer = $("").addClass("language-javascript Code"); + element.append(codeContainer); + var code = $("#"+codeID); + var codeText = code.text(); + var lines = codeText.split("\n"); + //remove the same level of indentation for everyone + while(lines[1].charAt(0)==="\t"){ + for (var i = 0; i < lines.length; i++){ + var line = lines[i]; + lines[i] = line.substr(1); + } + } + codeText = lines.join("\n"); + codeContainer.text(codeText); + codeContainer.addClass("Code"); +}; + +Interface.Knob = function(container, node, parameter, min, max, exp){ + return Interface.ContinuousControl(container, "dial", node, parameter, min, max, exp); +}; + +Interface.Momentary = function(container, callback){ + var button = nx.add("button", { + "parent" : container + }); + button.on("press", function(val){ + callback(val === 1); + }); + +}; + +Interface.AmplitudeEnvelope = function(container, node){ + var element = $("#"+container); + var group = $("
").addClass("Envelope") + .appendTo(element); + var attack = Interface.Knob(group[0], node, "attack", 0.001, 2, 2); + var decay = Interface.Knob(group[0], node, "decay", 0.0, 2, 2); + var sustain = Interface.Knob(group[0], node, "sustain", 0, 1, 2); + var release = Interface.Knob(group[0], node, "release", 0.001, 4, 2); + var labels = $("
").attr("id", "Labels") + .appendTo(group); + $("
").appendTo(labels).addClass("Label").text("attack"); + $("
").appendTo(labels).addClass("Label").text("decay"); + $("
").appendTo(labels).addClass("Label").text("sustain"); + $("
").appendTo(labels).addClass("Label").text("release"); + + return { + listen : function(){ + attack.listen(); + decay.listen(); + release.listen(); + sustain.listen(); + } + }; +}; + +Interface.Loading = function(containerID, callback){ + var element = $("#"+containerID); + element.addClass("LoadingBar"); + var loader = $("
").appendTo(element) + .attr("id", "Loader"); + Tone.Buffer.onprogress = function(percent){ + loader.width((percent * 100) + "%"); + }; + Tone.Buffer.onload = function(){ + element.css({ + height: 0, + opacity : 0, + margin: 0 + }); + setTimeout(function(){ + element.remove(); + }, 500); + if (callback){ + callback(); + } + }; +}; + +Interface.Range = function(containerID, callback){ + var range = nx.add("range", { + "parent" : containerID + }); + range.label = ""; + range.on("*", callback); + return range; +}; diff --git a/examples/signalMath.html b/examples/signalMath.html index 1451dfd6..a1be3f4e 100644 --- a/examples/signalMath.html +++ b/examples/signalMath.html @@ -11,7 +11,7 @@ - + diff --git a/examples/style/examples.css b/examples/style/examples.css new file mode 100644 index 00000000..87b6c4aa --- /dev/null +++ b/examples/style/examples.css @@ -0,0 +1,173 @@ +/** + * GLOBAL + */ +canvas { + border-radius: 0px !important; + margin-top: 5px; } + +/* FOR MOBILES */ +@media (max-width: 320px) { + #TopBar #Homepage a { + font-size: 16px !important; } + + #Content { + font-size: 10px !important; + max-width: 100% !important; } } +body { + margin: 0px; } + +#Explanation { + font-family: monospace; + background-color: #EAEAEA; + padding: 10px; + color: black; + font-size: 14px; + margin: 10px; + width: calc(100% - 4 * 10px); } + +#Explanation a { + color: white; + font-family: monospace; + font-size: 14px; } + +/* TOP BAR */ +#TopBar { + background-color: black; + height: 40px; + margin: 10px; + position: relative; + width: calc(100% - 2 * 10px); + font-family: monospace; } + #TopBar #Homepage { + position: relative; + float: left; + margin-left: 20px; + font-size: 24px; + line-height: 40px; + color: white; } + #TopBar #Homepage a { + font-size: 24px; + color: white; + text-transform: none; + text-decoration: none; + margin-left: 0px; } + #TopBar #Examples { + left: 50%; + height: 40px; + line-height: 40px; + position: absolute; } + #TopBar #Examples a { + position: absolute; + font-size: 14px; + left: 50%; + transform: translate(-50%, 0); + text-align: center; + color: white; + text-transform: none; + text-decoration: none; + margin-left: 0px; } + #TopBar #Meter { + width: 50px; + height: 90%; + position: absolute; + right: 10px; + top: 5%; } + #TopBar #Meter .Level { + height: 100%; + position: absolute; + width: 47%; + background-color: #59d72e; + bottom: 0px; } + #TopBar #Meter #Left { + left: 0px; } + #TopBar #Meter #Right { + right: 0px; } + +#Content { + font-family: monospace; + position: relative; + margin: auto; + height: auto; + margin-top: 20px; + width: 75%; + min-width: 320px; + max-width: 640px; + height: auto; + /** + * THE RACK + */ + /** + * CODE BLOCK + */ + /** + * ENVELOPE + */ + /** + * LOADING BAR + */ } + #Content .Rack { + width: 100%; + margin-bottom: 10px; + background-color: #EAEAEA; + border: 10px solid #EAEAEA; + position: relative; } + #Content .Rack .Title { + width: 100%; + height: 20px; + line-height: 20px; + margin: 0px 0 5px; + background-color: black; + font-size: 1.2em; + color: white; + cursor: pointer; + text-align: center; } + #Content .Rack.Collapsible .Title:after { + content: "\25B2"; + position: absolute; + right: 5px; } + #Content .Rack.Collapsible.Expanded .Title:after { + transform: rotate(-180deg); } + #Content .Rack.Collapsible { + overflow: hidden; + height: 20px; } + #Content .Rack.Collapsible.Expanded { + height: auto; } + #Content .Code { + width: 100%; + font-family: monospace; + height: auto; + text-align: left; + display: inline-block; + font-size: 14px; + /*padding: $Margin;*/ + /*line-height: ;*/ + background-color: #EAEAEA; } + #Content .Envelope { + display: inline-block; } + #Content .Envelope canvas { + width: 25%; + height: calc(100% - 8px - 10px); + position: relative; } + #Content .Envelope #Labels { + width: 100%; + height: 8px; } + #Content .Envelope #Labels .Label { + text-transform: uppercase; + width: 25%; + text-align: center; + font-size: 8px; + display: inline-block; } + #Content .LoadingBar { + width: 100%; + height: 20px; + margin: 10px; + text-align: center; + border: 1px solid black; + transition: all 0.4s; } + #Content .LoadingBar #Loader { + height: 100%; + width: 0%; + background-color: #EAEAEA; + transition: width 0.2s; } + +/*# sourceMappingURL=examples.css.map */ diff --git a/examples/style/prism.css b/examples/style/prism.css new file mode 100644 index 00000000..1f060aca --- /dev/null +++ b/examples/style/prism.css @@ -0,0 +1,137 @@ +/* http://prismjs.com/download.html?themes=prism&languages=clike+javascript */ +/** + * prism.js default theme for JavaScript, CSS and HTML + * Based on dabblet (http://dabblet.com) + * @author Lea Verou + */ + +code[class*="language-"], +pre[class*="language-"] { + color: black; + text-shadow: 0 1px white; + font-family: Consolas, Monaco, 'Andale Mono', monospace; + direction: ltr; + text-align: left; + white-space: pre; + word-spacing: normal; + word-break: normal; + line-height: 1.5; + + -moz-tab-size: 4; + -o-tab-size: 4; + tab-size: 4; + + -webkit-hyphens: none; + -moz-hyphens: none; + -ms-hyphens: none; + hyphens: none; +} + +pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection, +code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection { + text-shadow: none; + background: #b3d4fc; +} + +pre[class*="language-"]::selection, pre[class*="language-"] ::selection, +code[class*="language-"]::selection, code[class*="language-"] ::selection { + text-shadow: none; + background: #b3d4fc; +} + +@media print { + code[class*="language-"], + pre[class*="language-"] { + text-shadow: none; + } +} + +/* Code blocks */ +pre[class*="language-"] { + padding: 1em; + margin: .5em 0; + overflow: auto; +} + +:not(pre) > code[class*="language-"], +pre[class*="language-"] { + background: #f5f2f0; +} + +/* Inline code */ +:not(pre) > code[class*="language-"] { + padding: .1em; + border-radius: .3em; +} + +.token.comment, +.token.prolog, +.token.doctype, +.token.cdata { + color: slategray; +} + +.token.punctuation { + color: #999; +} + +.namespace { + opacity: .7; +} + +.token.property, +.token.tag, +.token.boolean, +.token.number, +.token.constant, +.token.symbol, +.token.deleted { + color: #905; +} + +.token.selector, +.token.attr-name, +.token.string, +.token.char, +.token.builtin, +.token.inserted { + color: #690; +} + +.token.operator, +.token.entity, +.token.url, +.language-css .token.string, +.style .token.string { + color: #a67f59; + background: hsla(0, 0%, 100%, .5); +} + +.token.atrule, +.token.attr-value, +.token.keyword { + color: #07a; +} + +.token.function { + color: #DD4A68; +} + +.token.regex, +.token.important, +.token.variable { + color: #e90; +} + +.token.important, +.token.bold { + font-weight: bold; +} +.token.italic { + font-style: italic; +} + +.token.entity { + cursor: help; +} +