├── .gitignore ├── .gitmodules ├── README.md ├── app ├── clip-matrix.js ├── controller.js ├── marco-root.js ├── meter-slider.js ├── return-panel.js ├── screen.js ├── send-panel.js ├── track-panel.js ├── track.js └── volume-panel.js ├── canvas.ui ├── README.md ├── button.js ├── mootools.js ├── slider.js ├── touch-tracker.js └── widget.js ├── index.html ├── node ├── bin │ ├── cygcrypto-0.9.8.dll │ ├── cyggcc_s-1.dll │ ├── cygiconv-2.dll │ ├── cygintl-8.dll │ ├── cygpath.exe │ ├── cygssl-0.9.8.dll │ ├── cygstdc++-6.dll │ ├── cygwin1.dll │ ├── cygz.dll │ ├── env.exe │ ├── gzip.exe │ ├── man.exe │ ├── node-waf │ ├── node.exe │ ├── npm │ ├── npm-get-uid-gid │ ├── npm-get-uid-gid@0.3.14 │ ├── npm@0.3.14 │ ├── read-package-json │ ├── read-package-json@0.3.14 │ ├── runnode.cmd │ ├── setenv.cmd │ ├── shell.cmd │ ├── su.exe │ ├── sudo.cmd │ └── tar.exe └── etc │ └── resolv.conf ├── osc ├── node-jspack │ └── jspack.js └── osc.js ├── server.bat └── server.js /.gitignore: -------------------------------------------------------------------------------- 1 | *~ -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "node-osc"] 2 | path = node-osc 3 | url = git://github.com:vnoise/node-osc.git 4 | [submodule "Socket.IO-node"] 5 | path = Socket.IO-node 6 | url = git://github.com:vnoise/Socket.IO-node.git 7 | [submodule "LiveOSC"] 8 | path = LiveOSC 9 | url = git://github.com:vnoise/LiveOSC.git 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | vTouch 2 | ====== 3 | 4 | vTouch is an webbased ableton live remote control running in html5 canvas. 5 | 6 | Quickstart 7 | ========== 8 | 9 | 1. Copy the folder vtouch/LiveOSC to the Remote Scripts Folder of Ableton Live. 10 | 2. Start Ableton Live and activate the remote script named LiveOSC in Preferences / MIDI. 11 | 3. Load "LiveOSC Control.adg" from LiveOSC into your Liveset to control levelmeter and clip position updates. 12 | 4. Run server.bat or node server.js 13 | 5. Navigate your browser to http://localhost or http://localhost#touch for Firefox Multitouch 14 | 6. Please wait 10 seconds until the UI gets responsiv after the initial update. This a current limitation of LiveOSC and we will fix this soon. 15 | -------------------------------------------------------------------------------- /app/clip-matrix.js: -------------------------------------------------------------------------------- 1 | var Clip = new Class({ 2 | Extends: Button, 3 | 4 | initialize: function(options) { 5 | //[state: 0 = no clip, 1 = has clip, 2 = playing, 3 = triggered] 6 | this._state = 0; 7 | this.bgColor = '#3a3637'; 8 | this._color = '#3a3637'; 9 | this._clipPos = 0; 10 | this.label = ""; 11 | 12 | Button.prototype.initialize.call(this, options); 13 | }, 14 | 15 | clipPos: function(pos){ 16 | this._clipPos = pos; 17 | }, 18 | 19 | clipInfo: function(state) { 20 | this._state = state; 21 | }, 22 | 23 | clipName: function(name,color){ 24 | this.label = name; 25 | this._color = color; 26 | }, 27 | 28 | drawCanvas: function(context) { 29 | //this.drawBackground(context, this._state == 0 ? this.bgColor : this._color); 30 | this.drawBackground(context, this._color); 31 | this.drawLabel(context); 32 | 33 | if (this._state == 2){ 34 | context.fillStyle = "rgba(255,255,255,0.5)"; 35 | context.fillRect(0, 0, this.width * this._clipPos, this.height); 36 | } 37 | 38 | if (this._state == 2 || this._state == 3 ){ 39 | this.drawBorder(context); 40 | } 41 | }, 42 | 43 | onTouchDown: function(event) { 44 | //this._state = ! this._state; 45 | this.fireEvent("click", [this.track, this.clip, this._state]); 46 | return true; 47 | } 48 | 49 | }); 50 | 51 | var ClipMatrix = new Class({ 52 | Extends: Widget, 53 | 54 | initialize: function(options) { 55 | Widget.prototype.initialize.call(this, options); 56 | //console.log("sdksdkjsdjksjdksjdksdjksdjskd"); 57 | 58 | //this.listen('/live/device/param', this.onDeviceParam.bind(this)); 59 | this.listen('/live/name/track', this.onLiveName.bind(this)); 60 | this.listen("/live/clip/info", this.onClipInfo.bind(this)); 61 | this.listen("/live/name/clip", this.onClipName.bind(this)); 62 | this.listen("/live/clip/position", this.onClipPosition.bind(this)) 63 | 64 | this.xOffset = 0; 65 | this.yOffset = 0; 66 | this.cols = 16; 67 | this.rows = 8; 68 | 69 | this.names = []; 70 | this.matrix = []; 71 | 72 | for (var x = 0; x < this.cols; x++) { 73 | this.matrix[x] = []; 74 | this.names[x] = this.add({ 75 | type: Button, 76 | borderWidth: 0, 77 | bgColor: '#11' 78 | }); 79 | for (var y = 0; y < this.rows; y++) { 80 | this.matrix[x][y] = this.add({ 81 | type: Clip, 82 | track: x, 83 | clip: y, 84 | fontSize: 8, 85 | bgColor: "#333033", 86 | fgColor: "#333033", 87 | marginRight: 10, 88 | label: "", 89 | on: { 90 | click: this.onClick.bind(this) 91 | } 92 | }); 93 | } 94 | } 95 | }, 96 | 97 | doLayout: function() { 98 | var gap = 5; 99 | var w = this.width / 8; 100 | var h = this.height / 8; 101 | var top = this.height * 0.1; 102 | 103 | this.children.each(function(child) { 104 | child.visible = false; 105 | }); 106 | 107 | for (var x = 0; x < 8; x++) { 108 | var label = this.names[x + this.xOffset]; 109 | label.extent(x * w, 0, w - gap, top - gap); 110 | label.visible = true; 111 | 112 | for (var y = 0; y < 8; y++) { 113 | var child = this.matrix[x + this.xOffset][y + this.yOffset]; 114 | child.extent(x * w, y * h + top, w - gap, h - gap); 115 | child.visible = true; 116 | } 117 | } 118 | }, 119 | 120 | onClipPosition: function(track, clip, position, length, loop_start,loop_end){ 121 | this.matrix[track][clip].clipPos(position / length); 122 | }, 123 | 124 | //(int track) (int device) (int param) (int value) (str name) 125 | /*onDeviceParam: function(track, device, parameter,value,name){ 126 | console.log("onDeviceParam: "+track+", "+device+", "+parameter+","+value+","+name); 127 | }, 128 | */ 129 | onClipName: function(track, clip, name,color) { 130 | r = (color >> 16) & 0xff; 131 | g = (color >> 8) & 0xff; 132 | b = (color >> 0) & 0xff; 133 | color = "rgb("+r+","+g+","+b+")"; 134 | console.log(track +","+clip); 135 | if (this.matrix[track][clip] != undefined){ 136 | this.matrix[track][clip].clipName(name,color); 137 | } 138 | }, 139 | 140 | onClipInfo: function(track, clip, state) { 141 | //[state: 0 = no clip, 1 = has clip, 2 = playing, 3 = triggered] 142 | this.matrix[track][clip].clipInfo(state); 143 | }, 144 | 145 | onLiveName: function(track, name){ 146 | this.names[track].label = name; 147 | }, 148 | 149 | requestUpdate: function() { 150 | 151 | this.send("/live/name/clip",'s', "query"); 152 | this.send("/live/name/track",'s', "query"); 153 | }, 154 | 155 | onClick: function(track, clip, state) { 156 | this.send("/live/play/clipslot", "ii", track, clip) ; 157 | 158 | } 159 | }); 160 | 161 | -------------------------------------------------------------------------------- /app/controller.js: -------------------------------------------------------------------------------- 1 | var Controller = new Class({ 2 | 3 | Implements: Events, 4 | 5 | initialize: function() { 6 | this.connect(); 7 | }, 8 | 9 | send: function(address, types) { 10 | var message = { 11 | address: address, 12 | args: [], 13 | types: types 14 | }; 15 | 16 | for (var i = 2; i < arguments.length; i++) { 17 | message.args.push(arguments[i]); 18 | } 19 | 20 | if (message.args.length != message.types.length) { 21 | console.log("warning: args.length != types.length"); 22 | } 23 | 24 | var socket = this.socket; 25 | socket.send(message); 26 | /*setTimeout(function() { 27 | socket.send(message); 28 | }, 1);*/ 29 | }, 30 | 31 | connect: function() { 32 | this.socket = new io.Socket(location.host); 33 | this.socket.on('connect', this.onConnect.bind(this)); 34 | this.socket.on('message', this.onMessage.bind(this)); 35 | this.socket.on('bundle', this.onBundle.bind(this)); 36 | this.socket.on('disconnect', this.onDisconnect.bind(this)); 37 | this.socket.connect(); 38 | }, 39 | 40 | onConnect: function() { 41 | this.root = new Screen({ 42 | controller: this 43 | }); 44 | 45 | // console.log("socket.io connected"); 46 | }, 47 | 48 | onMessage: function(message) { 49 | //console.log("onMessage_Controlle.js"); 50 | if (message.address == "#bundle"){ 51 | console.log("--bundle-----"); 52 | console.log("receive onMessage: addr-> " + message.address + " args-> " + message.args); 53 | }else{ 54 | //console.log("--message-----"); 55 | //console.log("receive onMessage: addr-> " + message.address + " args-> " + message.args); 56 | } 57 | //console.log("-------"); 58 | //console.log("receive onMessage.decode(): addr-> " + message.decode()); 59 | //console.log(decode(message)); 60 | this.fireEvent(message.address, message.args); 61 | }, 62 | 63 | onBundle: function(bundle){ 64 | console.log("Bundle") 65 | /*bundle = OSCBundle() 66 | bundle.append(print1) 67 | bundle.append('/foo', (123, 456)) 68 | */ 69 | bundlebinary = bundle.getBinary(); 70 | //hexDump(bundlebinary); 71 | //print decodeOSC(bundlebinary); 72 | console.log(decodeOSC(bundlebinary)); 73 | }, 74 | 75 | 76 | onDisconnect: function() { 77 | // console.log('socket.io disconnected'); 78 | } 79 | 80 | }); -------------------------------------------------------------------------------- /app/marco-root.js: -------------------------------------------------------------------------------- 1 | var RootPanel = new Class({ 2 | Extends: Widget, 3 | 4 | initialize: function(options){ 5 | Widget.prototype.initialize.call(this, options); 6 | 7 | this.trackPanel = this.add({ type: TrackPanel }); 8 | this.clipMatrix = this.add({ type: ClipMatrix }); 9 | this.layout = "vertical"; 10 | 11 | //this.clipMatrix = this.add({ type: ClipMatrix}); 12 | //this.layout = "vertical"; 13 | //this.clipMatrix.requestUpdate() 14 | 15 | /*this.mutePanel = this.add({ type: MuteButtonPanel}); 16 | this.soloPanel = this.add({ type: SoloButtonPanel}); 17 | this.armPanel = this.add({ type: ArmButtonPanel}); 18 | */ 19 | 20 | /*this.mutePanel = this.root.add({ 21 | type: MuteButton, 22 | bgColor: "#FF0000", 23 | frontColor: "#FFFF00", 24 | labelColor: "#00FF00" 25 | }); 26 | */ 27 | } 28 | 29 | }); -------------------------------------------------------------------------------- /app/meter-slider.js: -------------------------------------------------------------------------------- 1 | var MeterSlider = new Class({ 2 | Extends: Slider, 3 | 4 | initialize: function(options) { 5 | this.meter = 0; 6 | 7 | Slider.prototype.initialize.call(this, options); 8 | }, 9 | 10 | drawCanvas: function(context) { 11 | Slider.prototype.drawCanvas.call(this, context); 12 | 13 | context.fillStyle = "rgba(255,255,255,0.5)"; 14 | context.fillRect(0, this.height, this.width, (this.height * this.meter)*0.6 * -1); 15 | 16 | } 17 | }); 18 | -------------------------------------------------------------------------------- /app/return-panel.js: -------------------------------------------------------------------------------- 1 | var ReturnPanel = new Class({ 2 | Extends: TrackPanel, 3 | 4 | initialize: function(options) { 5 | this.messages = { 6 | volume: '/live/return/volume', 7 | meter: '/live/return/meter', 8 | mute: '/live/return/mute', 9 | solo: '/live/return/solo', 10 | arm: '/live/return/arm' 11 | }; 12 | 13 | TrackPanel.prototype.initialize.call(this, options); 14 | } 15 | }); 16 | -------------------------------------------------------------------------------- /app/screen.js: -------------------------------------------------------------------------------- 1 | var ScreenButton = new Class({ 2 | Extends: Button, 3 | 4 | initialize: function(options) { 5 | this.active = false; 6 | 7 | Button.prototype.initialize.call(this, options); 8 | }, 9 | 10 | drawCanvas: function(context) { 11 | this.drawBackground(context, this.active ? this.fgColor : this.bgColor); 12 | this.drawLabel(context); 13 | }, 14 | 15 | onTouchDown: function(event) { 16 | this._parent.children.each(function(button) { 17 | button.active = false 18 | }); 19 | this.active = true; 20 | this.fireEvent("click"); 21 | return true; 22 | } 23 | 24 | }); 25 | 26 | var Screen = new Class({ 27 | Extends: Widget, 28 | 29 | initialize: function(options) { 30 | Widget.prototype.initialize.call(this, options); 31 | 32 | this.tabs = this.add({ layout: 'horizontal' }); 33 | this.navi = this.add({ layout: 'vertical' }); 34 | 35 | this.navi.add({ 36 | type: Button, 37 | label: '<', 38 | marginTop: 5, 39 | on: { 40 | click: function() { 41 | //debugger; 42 | console.log("sd"); 43 | console.log(this.clipMatrix); 44 | if (this.clipMatrix.xOffset > 0) { 45 | this.clipMatrix.xOffset -= 1; 46 | this.volumePanel.xOffset -= 1; 47 | this.sendPanel.xOffset -= 1; 48 | } 49 | }.bind(this) 50 | } 51 | }); 52 | 53 | this.navi.add({ 54 | type: Button, 55 | label: '>', 56 | marginTop: 5, 57 | on: { 58 | click: function() { 59 | //debugger; 60 | console.log("sd"); 61 | console.log(this.clipMatrix); 62 | if (this.clipMatrix.xOffset < 56) { 63 | this.clipMatrix.xOffset += 1; 64 | this.volumePanel.xOffset += 1; 65 | this.sendPanel.xOffset += 1; 66 | } 67 | }.bind(this) 68 | } 69 | }); 70 | 71 | //this.volumePanel = this.add({ type: VolumePanel }); 72 | //this.addPanel("Volume", this.volumePanel); 73 | //this.volumePanel.requestUpdate(); 74 | //this.push(this.volumePanel); 75 | 76 | 77 | /*this.clipMatrix = this.add({ type: ClipMatrix }); 78 | #this.volumePanel = this.add({ type: VolumePanel }); 79 | this.sendPanel = this.add({ type: SendPanel }); 80 | this.returnPanel = this.add({ type: ReturnPanel }); 81 | debugger; 82 | */ 83 | /*this.mainView = this.add({ type: MainView, active: true }); 84 | this.mainView.addPanel(this.clipMatrix); 85 | this.mainView.addPanel(this.volumePanel); 86 | */ 87 | 88 | /*this.panels = []; 89 | //this.addPanel("Main", this.mainView ); 90 | this.addPanel("Clip", this.clipMatrix); 91 | this.addPanel("Volume", this.volumePanel); 92 | this.addPanel("Sends", this.sendPanel); 93 | this.addPanel("Returns", this.returnPanel); 94 | 95 | this.activePanel = this.clipMatrix; 96 | */ 97 | this.panels = []; 98 | this.clipMatrix = this.addPanel("Clip", { type: ClipMatrix }); 99 | this.volumePanel = this.addPanel("Volume", { type: VolumePanel }); 100 | this.sendPanel = this.addPanel("Send", { type: SendPanel }); 101 | this.returnPanel = this.addPanel("Return", { type: ReturnPanel }); 102 | 103 | this.activePanel = this.panels[0]; 104 | this.tabs.children[0].active = true; 105 | 106 | for (var i = 0; i < 16; i++) { 107 | this.send('/live/name/track', 'i', i); 108 | } 109 | }, 110 | 111 | addPanel: function(label, config) { 112 | var panel = this.add(config); 113 | this.panels.push(panel); 114 | this.tabs.add({ 115 | type: ScreenButton, 116 | label: label, 117 | bgColor: "#333", 118 | fgColor: "#ccc", 119 | marginRight: 5, 120 | on: { 121 | click: this.onClickButton.bind(this, panel) 122 | } 123 | }); 124 | panel.requestUpdate(); 125 | return panel; 126 | }, 127 | 128 | onClickButton: function(panel) { 129 | this.activePanel = panel; 130 | }, 131 | 132 | hidePanels: function() { 133 | this.panels.each(function(panel) { 134 | panel.visible = false; 135 | }); 136 | }, 137 | 138 | doLayout: function() { 139 | this.tabs.extent(0, 0, this.width, this.height * 0.1 - 5); 140 | this.tabs.doLayout(); 141 | 142 | this.navi.extent(0, this.height * 0.1 - 5, this.width * 0.1 - 5, this.height * 0.9); 143 | this.navi.doLayout(); 144 | 145 | /*this.volumePanel.visible = true; 146 | this.volumePanel.extent(0, this.height * 0.1 - 5, this.width * 0.1 - 5, this.height * 0.9); 147 | this.volumePanel.doLayout(); 148 | */ 149 | 150 | this.hidePanels(); 151 | 152 | this.activePanel.visible = true; 153 | this.activePanel.extent(this.width * 0.1, this.height * 0.1, this.width * 0.9, this.height * 0.9); 154 | this.activePanel.doLayout(); 155 | } 156 | 157 | }); -------------------------------------------------------------------------------- /app/send-panel.js: -------------------------------------------------------------------------------- 1 | var SendPanel = new Class({ 2 | Extends: Widget, 3 | 4 | initialize: function(options) { 5 | Widget.prototype.initialize.call(this, options); 6 | 7 | this.xOffset = 0; 8 | this.yOffset = 0; 9 | 10 | this.listen('/live/name/track', this.onLiveName.bind(this)); 11 | this.listen('/live/send', this.onLiveSend.bind(this)); 12 | 13 | this.cols = 16; 14 | this.sends = 2; 15 | this.names = []; 16 | this.matrix = []; 17 | 18 | for (var i = 0; i < this.cols; i++) { 19 | this.names[i] = this.add({ 20 | type: Button, 21 | borderWidth: 0, 22 | bgColor: '#11' 23 | }); 24 | 25 | this.matrix[i] = []; 26 | for (var j = 0; j < this.sends; j++) { 27 | this.matrix[i][j] = this.add({ 28 | type: Slider, 29 | marginRight: 10, 30 | on: { 31 | change: this.onSliderChange.bind(this, i, j) 32 | } 33 | }); 34 | } 35 | } 36 | }, 37 | 38 | requestUpdate: function() { 39 | for (var i = 0; i < this.cols; i++) { 40 | for (var j = 0; j < this.sends; j++) { 41 | this.send('/live/send', 'ii', i, j); 42 | } 43 | } 44 | }, 45 | 46 | doLayout: function() { 47 | var gap = 5; 48 | var w = this.width / 8; 49 | var h = this.height * 0.8 / 2; 50 | var top = this.height * 0.1; 51 | 52 | this.children.each(function(child) { 53 | child.visible = false; 54 | }); 55 | 56 | for (var x = 0; x < 8; x++) { 57 | var label = this.names[x + this.xOffset]; 58 | label.extent(x * w, 0, w - gap, top - gap); 59 | label.visible = true; 60 | 61 | for (var y = 0; y < 2; y++) { 62 | var child = this.matrix[x + this.xOffset][y + this.yOffset]; 63 | child.extent(x * w, top + y * h, w - gap, h - gap); 64 | child.visible = true; 65 | } 66 | } 67 | }, 68 | 69 | onSliderChange: function(track, send, value) { 70 | this.send('/live/send', 'iif', track, send, value); 71 | }, 72 | 73 | onLiveName: function(track, name){ 74 | this.names[track].label = name; 75 | }, 76 | 77 | onLiveSend: function(track, send, value){ 78 | this.matrix[track][send].setValueTimed(value); 79 | } 80 | 81 | }); 82 | -------------------------------------------------------------------------------- /app/track-panel.js: -------------------------------------------------------------------------------- 1 | var TrackPanel = new Class({ 2 | Extends: Widget, 3 | 4 | initialize: function(options) { 5 | Widget.prototype.initialize.call(this, options); 6 | 7 | if (this.messages.name) { 8 | this.listen(this.messages.name, this.onLiveName.bind(this)) 9 | } 10 | 11 | this.listen(this.messages.volume, this.onLiveVolume.bind(this)); 12 | this.listen(this.messages.meter, this.onLiveMeter.bind(this)) 13 | this.listen(this.messages.mute, this.onLiveMute.bind(this)) 14 | this.listen(this.messages.solo, this.onLiveSolo.bind(this)) 15 | this.listen(this.messages.arm, this.onLiveArm.bind(this)) 16 | 17 | this.cols = 16; 18 | this.xOffset = 0; 19 | 20 | for (var i = 0; i < this.cols; i++) { 21 | this.add({ 22 | type: Track, 23 | track: i, 24 | on: { 25 | volume: this.onVolume.bind(this), 26 | mute: this.onMute.bind(this), 27 | solo: this.onSolo.bind(this), 28 | arm: this.onArm.bind(this) 29 | } 30 | }); 31 | } 32 | }, 33 | 34 | doLayout: function() { 35 | var gap = 5; 36 | var w = this.width / 8; 37 | var h = this.height; 38 | 39 | this.children.each(function(child) { 40 | child.visible = false; 41 | }); 42 | 43 | for (var x = 0; x < 8; x++) { 44 | var child = this.children[x + this.xOffset]; 45 | child.visible = true; 46 | child.extent(x * w, 0, w - gap, h - gap); 47 | child.doLayout(); 48 | } 49 | }, 50 | 51 | requestUpdate: function() { 52 | console.log("request update") 53 | for (var i = 0; i < this.cols; i++) { 54 | this.send(this.messages.volume, 'i', i); 55 | this.send(this.messages.mute, 'i', i); 56 | this.send(this.messages.solo, 'i', i); 57 | this.send(this.messages.arm, 'i', i); 58 | } 59 | }, 60 | 61 | onLiveName: function(track, name){ 62 | this.children[track].name.label = name; 63 | }, 64 | 65 | onLiveMeter: function(track, pan, value){ 66 | this.children[track].volume.meter = value; 67 | }, 68 | 69 | onLiveVolume: function(track, value) { 70 | this.children[track].volume.setValueTimed(value); 71 | }, 72 | 73 | onLiveMute: function(track, state) { 74 | this.children[track].mute.state = state ; 75 | }, 76 | 77 | onLiveSolo: function(track, state) { 78 | this.children[track].solo.state = state; 79 | }, 80 | 81 | onLiveArm: function(track, state) { 82 | this.children[track].arm.state = state; 83 | }, 84 | 85 | onVolume: function(track, value) { 86 | console.log("SEND VOLUME-->"+value) 87 | this.send(this.messages.volume, "if", track, value) 88 | }, 89 | 90 | onMute: function(track, state) { 91 | this.send(this.messages.mute, "ii", track, state ? 1 : 0 ) 92 | }, 93 | 94 | onSolo: function(track, state) { 95 | this.send(this.messages.solo, "ii", track, state ? 1 : 0) 96 | }, 97 | 98 | onArm: function(track, state) { 99 | this.send(this.messages.arm, "ii", track, state ? 1 : 0) 100 | } 101 | }); 102 | -------------------------------------------------------------------------------- /app/track.js: -------------------------------------------------------------------------------- 1 | var Track = new Class({ 2 | Extends: Widget, 3 | 4 | initialize: function(options) { 5 | Slider.prototype.initialize.call(this, options); 6 | 7 | this.layout = 'vertical'; 8 | 9 | this.name = this.add({ 10 | type: Button, 11 | borderWidth: 0, 12 | bgColor: '#11' 13 | }); 14 | 15 | this.volume = this.add({ 16 | type: MeterSlider, 17 | sizeHint: 7, 18 | marginTop: 5, 19 | on: { 20 | change: function(value) { 21 | this.fireEvent('volume', [this.track, value]); 22 | }.bind(this) 23 | } 24 | }); 25 | 26 | this.mute = this.add({ 27 | type: ToggleButton, 28 | bgColor: '#ffac00', 29 | fgColor: '#3a3637', 30 | fontColor: 'rgba(255,255,255,0.5)', 31 | marginTop: 5, 32 | label: this.track, 33 | on: { 34 | click: function(state) { 35 | this.fireEvent('mute', [this.track, state]); 36 | }.bind(this) 37 | } 38 | }); 39 | 40 | this.solo = this.add({ 41 | type: ToggleButton, 42 | bgColor: '#3a3637', 43 | fgColor: '#0b9eff', 44 | fontColor: 'rgba(255,255,255,0.5)', 45 | marginTop: 5, 46 | label: 'S', 47 | on: { 48 | click: function(state) { 49 | this.fireEvent('solo', [this.track, state]); 50 | }.bind(this) 51 | } 52 | }); 53 | 54 | this.arm = this.add({ 55 | type: ToggleButton, 56 | bgColor: '#3a3637', 57 | fgColor: '#ff283f', 58 | fontColor: 'rgba(255,255,255,0.5)', 59 | label: 'O', 60 | marginTop: 5, 61 | on: { 62 | click: function(state) { 63 | this.fireEvent('arm', [this.track, state]); 64 | }.bind(this) 65 | } 66 | }); 67 | } 68 | }); 69 | -------------------------------------------------------------------------------- /app/volume-panel.js: -------------------------------------------------------------------------------- 1 | var VolumePanel = new Class({ 2 | Extends: TrackPanel, 3 | 4 | initialize: function(options) { 5 | this.messages = { 6 | volume: '/live/volume', 7 | name: '/live/name/track', 8 | meter: '/live/track/meter', 9 | mute: '/live/mute', 10 | solo: '/live/solo', 11 | arm: '/live/arm' 12 | }; 13 | 14 | TrackPanel.prototype.initialize.call(this, options); 15 | } 16 | }); 17 | -------------------------------------------------------------------------------- /canvas.ui/README.md: -------------------------------------------------------------------------------- 1 | canvas.ui 2 | ========= 3 | 4 | canvas.ui is a HTML5 Canvas UI Framework with Multitouch support. 5 | 6 | Features 7 | ======== 8 | 9 | canvas.ui is a light library for 10 | 11 | 1. hiding different event models: apple touch, firefox touch or stock mouse events. 12 | 2. delegating touch events to the right widget in a widget tree. 13 | 3. drawing ui widgets on the html5 canvas element. 14 | 4. managing layout for child widgets. 15 | 16 | Usage 17 | ===== 18 | 19 | Following simple example assumes a canvas element in a html document: 20 | 21 |
22 | 
23 | <html>
24 |   <head>
25 |     <script src="/canvas.ui/mootools.js" type="text/javascript"></script>
26 |     <script src="/canvas.ui/touch-tracker.js" type="text/javascript"></script>
27 |     <script src="/canvas.ui/widget.js" type="text/javascript"></script>
28 |     <script src="/canvas.ui/slider.js" type="text/javascript"></script>
29 |     <script src="/app.js" type="text/javascript"></script>
30 |   </head>
31 |   <body onLoad="onLoad()" style="background:#000;margin:0">
32 |     <canvas id="canvas" width="800" height="600"></canvas>
33 |   </body>
34 | </html>
35 | 
36 | 
37 | 38 | Let's just create a root widget and add a slider with the label 'Pitch'. 39 | 40 | The slider fires the change event, so we can pass an event handler to the constructor. 41 | 42 |
43 | 
44 | function onLoad() {
45 |         var widget = new Widget({
46 |             canvas: document.getElementById('canvas'),
47 |             layout: 'vertical',
48 |             width: 800,
49 |             height: 600
50 |         });
51 | 
52 |         var slider = widget.add({ 
53 |             type: Slider, 
54 |             label: 'Pitch',
55 |             on: {
56 |                 change: function(value) {
57 |                     console.log("value changed to: " + value);
58 |                 }
59 |             }
60 |         });
61 | }
62 | 
63 | 
64 | -------------------------------------------------------------------------------- /canvas.ui/button.js: -------------------------------------------------------------------------------- 1 | var Button = new Class({ 2 | Extends: Widget, 3 | 4 | initialize: function(options) { 5 | this.label = null; 6 | this.textAlign = "center"; 7 | this.fontColor = "#fff"; 8 | this.fontName = "Arial"; 9 | this.fontSize = 10; 10 | this.bgColor = "#666"; 11 | this.borderWidth = 3; 12 | this.borderColor = "rgba(255,255,255,1)"; 13 | 14 | Widget.prototype.initialize.call(this, options); 15 | }, 16 | 17 | drawBorder: function(context) { 18 | var w = this.borderWidth / 2; 19 | context.strokeStyle = this.borderColor; 20 | context.lineWidth = this.borderWidth; 21 | context.strokeRect(w, w, this.width - w, this.height - w); 22 | }, 23 | 24 | drawBackground: function(context, style) { 25 | context.fillStyle = style; 26 | context.fillRect(0, 0, this.width, this.height); 27 | }, 28 | 29 | drawLabel: function(context) { 30 | context.textAlign = this.textAlign; 31 | context.fillStyle = this.fontColor; 32 | context.font = (this.height * this.fontSize / 30) + "px " + this.fontName; 33 | context.fillText(this.label, this.width / 2, this.height / 2 + this.height * this.fontSize / 100); 34 | }, 35 | 36 | drawCanvas: function(context) { 37 | this.drawBackground(context, this.bgColor); 38 | 39 | if (this.label) { 40 | this.drawLabel(context); 41 | } 42 | 43 | if (this.borderWidth > 0) { 44 | this.drawBorder(context); 45 | } 46 | }, 47 | 48 | onTouchDown: function(event) { 49 | this.fireEvent("click"); 50 | return true; 51 | } 52 | 53 | }); 54 | 55 | var ToggleButton = new Class({ 56 | Extends: Button, 57 | 58 | initialize: function(options) { 59 | this.state = false; 60 | Button.prototype.initialize.call(this, options); 61 | }, 62 | 63 | drawCanvas: function(context) { 64 | this.drawBackground(context, this.state ? this.fgColor : this.bgColor); 65 | this.drawLabel(context); 66 | }, 67 | 68 | onTouchDown: function(event) { 69 | this.state = ! this.state; 70 | this.fireEvent("click", this.state); 71 | return true; 72 | } 73 | 74 | }); 75 | 76 | -------------------------------------------------------------------------------- /canvas.ui/mootools.js: -------------------------------------------------------------------------------- 1 | /* 2 | --- 3 | MooTools: the javascript framework 4 | 5 | web build: 6 | - http://mootools.net/core/7c56cfef9dddcf170a5d68e3fb61cfd7 7 | 8 | packager build: 9 | - packager build Core/Core Core/Array Core/String Core/Number Core/Function Core/Object Core/Event Core/Browser Core/Class Core/Class.Extras Core/Slick.Parser Core/Slick.Finder Core/Element Core/Element.Style Core/Element.Event Core/Element.Dimensions Core/Fx Core/Fx.CSS Core/Fx.Tween Core/Fx.Morph Core/Fx.Transitions Core/Request Core/Request.HTML Core/Request.JSON Core/Cookie Core/JSON Core/DOMReady Core/Swiff 10 | 11 | /* 12 | --- 13 | 14 | name: Core 15 | 16 | description: The heart of MooTools. 17 | 18 | license: MIT-style license. 19 | 20 | copyright: Copyright (c) 2006-2010 [Valerio Proietti](http://mad4milk.net/). 21 | 22 | authors: The MooTools production team (http://mootools.net/developers/) 23 | 24 | inspiration: 25 | - Class implementation inspired by [Base.js](http://dean.edwards.name/weblog/2006/03/base/) Copyright (c) 2006 Dean Edwards, [GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php) 26 | - Some functionality inspired by [Prototype.js](http://prototypejs.org) Copyright (c) 2005-2007 Sam Stephenson, [MIT License](http://opensource.org/licenses/mit-license.php) 27 | 28 | provides: [Core, MooTools, Type, typeOf, instanceOf, Native] 29 | 30 | ... 31 | */ 32 | 33 | (function(){ 34 | 35 | this.MooTools = { 36 | version: '1.3.2', 37 | build: 'c9f1ff10e9e7facb65e9481049ed1b450959d587' 38 | }; 39 | 40 | // typeOf, instanceOf 41 | 42 | var typeOf = this.typeOf = function(item){ 43 | if (item == null) return 'null'; 44 | if (item.$family) return item.$family(); 45 | 46 | if (item.nodeName){ 47 | if (item.nodeType == 1) return 'element'; 48 | if (item.nodeType == 3) return (/\S/).test(item.nodeValue) ? 'textnode' : 'whitespace'; 49 | } else if (typeof item.length == 'number'){ 50 | if (item.callee) return 'arguments'; 51 | if ('item' in item) return 'collection'; 52 | } 53 | 54 | return typeof item; 55 | }; 56 | 57 | var instanceOf = this.instanceOf = function(item, object){ 58 | if (item == null) return false; 59 | var constructor = item.$constructor || item.constructor; 60 | while (constructor){ 61 | if (constructor === object) return true; 62 | constructor = constructor.parent; 63 | } 64 | return item instanceof object; 65 | }; 66 | 67 | // Function overloading 68 | 69 | var Function = this.Function; 70 | 71 | var enumerables = true; 72 | for (var i in {toString: 1}) enumerables = null; 73 | if (enumerables) enumerables = ['hasOwnProperty', 'valueOf', 'isPrototypeOf', 'propertyIsEnumerable', 'toLocaleString', 'toString', 'constructor']; 74 | 75 | Function.prototype.overloadSetter = function(usePlural){ 76 | var self = this; 77 | return function(a, b){ 78 | if (a == null) return this; 79 | if (usePlural || typeof a != 'string'){ 80 | for (var k in a) self.call(this, k, a[k]); 81 | if (enumerables) for (var i = enumerables.length; i--;){ 82 | k = enumerables[i]; 83 | if (a.hasOwnProperty(k)) self.call(this, k, a[k]); 84 | } 85 | } else { 86 | self.call(this, a, b); 87 | } 88 | return this; 89 | }; 90 | }; 91 | 92 | Function.prototype.overloadGetter = function(usePlural){ 93 | var self = this; 94 | return function(a){ 95 | var args, result; 96 | if (usePlural || typeof a != 'string') args = a; 97 | else if (arguments.length > 1) args = arguments; 98 | if (args){ 99 | result = {}; 100 | for (var i = 0; i < args.length; i++) result[args[i]] = self.call(this, args[i]); 101 | } else { 102 | result = self.call(this, a); 103 | } 104 | return result; 105 | }; 106 | }; 107 | 108 | Function.prototype.extend = function(key, value){ 109 | this[key] = value; 110 | }.overloadSetter(); 111 | 112 | Function.prototype.implement = function(key, value){ 113 | this.prototype[key] = value; 114 | }.overloadSetter(); 115 | 116 | // From 117 | 118 | var slice = Array.prototype.slice; 119 | 120 | Function.from = function(item){ 121 | return (typeOf(item) == 'function') ? item : function(){ 122 | return item; 123 | }; 124 | }; 125 | 126 | Array.from = function(item){ 127 | if (item == null) return []; 128 | return (Type.isEnumerable(item) && typeof item != 'string') ? (typeOf(item) == 'array') ? item : slice.call(item) : [item]; 129 | }; 130 | 131 | Number.from = function(item){ 132 | var number = parseFloat(item); 133 | return isFinite(number) ? number : null; 134 | }; 135 | 136 | String.from = function(item){ 137 | return item + ''; 138 | }; 139 | 140 | // hide, protect 141 | 142 | Function.implement({ 143 | 144 | hide: function(){ 145 | this.$hidden = true; 146 | return this; 147 | }, 148 | 149 | protect: function(){ 150 | this.$protected = true; 151 | return this; 152 | } 153 | 154 | }); 155 | 156 | // Type 157 | 158 | var Type = this.Type = function(name, object){ 159 | if (name){ 160 | var lower = name.toLowerCase(); 161 | var typeCheck = function(item){ 162 | return (typeOf(item) == lower); 163 | }; 164 | 165 | Type['is' + name] = typeCheck; 166 | if (object != null){ 167 | object.prototype.$family = (function(){ 168 | return lower; 169 | }).hide(); 170 | 171 | } 172 | } 173 | 174 | if (object == null) return null; 175 | 176 | object.extend(this); 177 | object.$constructor = Type; 178 | object.prototype.$constructor = object; 179 | 180 | return object; 181 | }; 182 | 183 | var toString = Object.prototype.toString; 184 | 185 | Type.isEnumerable = function(item){ 186 | return (item != null && typeof item.length == 'number' && toString.call(item) != '[object Function]' ); 187 | }; 188 | 189 | var hooks = {}; 190 | 191 | var hooksOf = function(object){ 192 | var type = typeOf(object.prototype); 193 | return hooks[type] || (hooks[type] = []); 194 | }; 195 | 196 | var implement = function(name, method){ 197 | if (method && method.$hidden) return; 198 | 199 | var hooks = hooksOf(this); 200 | 201 | for (var i = 0; i < hooks.length; i++){ 202 | var hook = hooks[i]; 203 | if (typeOf(hook) == 'type') implement.call(hook, name, method); 204 | else hook.call(this, name, method); 205 | } 206 | 207 | var previous = this.prototype[name]; 208 | if (previous == null || !previous.$protected) this.prototype[name] = method; 209 | 210 | if (this[name] == null && typeOf(method) == 'function') extend.call(this, name, function(item){ 211 | return method.apply(item, slice.call(arguments, 1)); 212 | }); 213 | }; 214 | 215 | var extend = function(name, method){ 216 | if (method && method.$hidden) return; 217 | var previous = this[name]; 218 | if (previous == null || !previous.$protected) this[name] = method; 219 | }; 220 | 221 | Type.implement({ 222 | 223 | implement: implement.overloadSetter(), 224 | 225 | extend: extend.overloadSetter(), 226 | 227 | alias: function(name, existing){ 228 | implement.call(this, name, this.prototype[existing]); 229 | }.overloadSetter(), 230 | 231 | mirror: function(hook){ 232 | hooksOf(this).push(hook); 233 | return this; 234 | } 235 | 236 | }); 237 | 238 | new Type('Type', Type); 239 | 240 | // Default Types 241 | 242 | var force = function(name, object, methods){ 243 | var isType = (object != Object), 244 | prototype = object.prototype; 245 | 246 | if (isType) object = new Type(name, object); 247 | 248 | for (var i = 0, l = methods.length; i < l; i++){ 249 | var key = methods[i], 250 | generic = object[key], 251 | proto = prototype[key]; 252 | 253 | if (generic) generic.protect(); 254 | 255 | if (isType && proto){ 256 | delete prototype[key]; 257 | prototype[key] = proto.protect(); 258 | } 259 | } 260 | 261 | if (isType) object.implement(prototype); 262 | 263 | return force; 264 | }; 265 | 266 | force('String', String, [ 267 | 'charAt', 'charCodeAt', 'concat', 'indexOf', 'lastIndexOf', 'match', 'quote', 'replace', 'search', 268 | 'slice', 'split', 'substr', 'substring', 'toLowerCase', 'toUpperCase' 269 | ])('Array', Array, [ 270 | 'pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift', 'concat', 'join', 'slice', 271 | 'indexOf', 'lastIndexOf', 'filter', 'forEach', 'every', 'map', 'some', 'reduce', 'reduceRight' 272 | ])('Number', Number, [ 273 | 'toExponential', 'toFixed', 'toLocaleString', 'toPrecision' 274 | ])('Function', Function, [ 275 | 'apply', 'call', 'bind' 276 | ])('RegExp', RegExp, [ 277 | 'exec', 'test' 278 | ])('Object', Object, [ 279 | 'create', 'defineProperty', 'defineProperties', 'keys', 280 | 'getPrototypeOf', 'getOwnPropertyDescriptor', 'getOwnPropertyNames', 281 | 'preventExtensions', 'isExtensible', 'seal', 'isSealed', 'freeze', 'isFrozen' 282 | ])('Date', Date, ['now']); 283 | 284 | Object.extend = extend.overloadSetter(); 285 | 286 | Date.extend('now', function(){ 287 | return +(new Date); 288 | }); 289 | 290 | new Type('Boolean', Boolean); 291 | 292 | // fixes NaN returning as Number 293 | 294 | Number.prototype.$family = function(){ 295 | return isFinite(this) ? 'number' : 'null'; 296 | }.hide(); 297 | 298 | // Number.random 299 | 300 | Number.extend('random', function(min, max){ 301 | return Math.floor(Math.random() * (max - min + 1) + min); 302 | }); 303 | 304 | // forEach, each 305 | 306 | var hasOwnProperty = Object.prototype.hasOwnProperty; 307 | Object.extend('forEach', function(object, fn, bind){ 308 | for (var key in object){ 309 | if (hasOwnProperty.call(object, key)) fn.call(bind, object[key], key, object); 310 | } 311 | }); 312 | 313 | Object.each = Object.forEach; 314 | 315 | Array.implement({ 316 | 317 | forEach: function(fn, bind){ 318 | for (var i = 0, l = this.length; i < l; i++){ 319 | if (i in this) fn.call(bind, this[i], i, this); 320 | } 321 | }, 322 | 323 | each: function(fn, bind){ 324 | Array.forEach(this, fn, bind); 325 | return this; 326 | } 327 | 328 | }); 329 | 330 | // Array & Object cloning, Object merging and appending 331 | 332 | var cloneOf = function(item){ 333 | switch (typeOf(item)){ 334 | case 'array': return item.clone(); 335 | case 'object': return Object.clone(item); 336 | default: return item; 337 | } 338 | }; 339 | 340 | Array.implement('clone', function(){ 341 | var i = this.length, clone = new Array(i); 342 | while (i--) clone[i] = cloneOf(this[i]); 343 | return clone; 344 | }); 345 | 346 | var mergeOne = function(source, key, current){ 347 | switch (typeOf(current)){ 348 | case 'object': 349 | if (typeOf(source[key]) == 'object') Object.merge(source[key], current); 350 | else source[key] = Object.clone(current); 351 | break; 352 | case 'array': source[key] = current.clone(); break; 353 | default: source[key] = current; 354 | } 355 | return source; 356 | }; 357 | 358 | Object.extend({ 359 | 360 | merge: function(source, k, v){ 361 | if (typeOf(k) == 'string') return mergeOne(source, k, v); 362 | for (var i = 1, l = arguments.length; i < l; i++){ 363 | var object = arguments[i]; 364 | for (var key in object) mergeOne(source, key, object[key]); 365 | } 366 | return source; 367 | }, 368 | 369 | clone: function(object){ 370 | var clone = {}; 371 | for (var key in object) clone[key] = cloneOf(object[key]); 372 | return clone; 373 | }, 374 | 375 | append: function(original){ 376 | for (var i = 1, l = arguments.length; i < l; i++){ 377 | var extended = arguments[i] || {}; 378 | for (var key in extended) original[key] = extended[key]; 379 | } 380 | return original; 381 | } 382 | 383 | }); 384 | 385 | // Object-less types 386 | 387 | ['Object', 'WhiteSpace', 'TextNode', 'Collection', 'Arguments'].each(function(name){ 388 | new Type(name); 389 | }); 390 | 391 | // Unique ID 392 | 393 | var UID = Date.now(); 394 | 395 | String.extend('uniqueID', function(){ 396 | return (UID++).toString(36); 397 | }); 398 | 399 | 400 | 401 | })(); 402 | 403 | 404 | /* 405 | --- 406 | 407 | name: Array 408 | 409 | description: Contains Array Prototypes like each, contains, and erase. 410 | 411 | license: MIT-style license. 412 | 413 | requires: Type 414 | 415 | provides: Array 416 | 417 | ... 418 | */ 419 | 420 | Array.implement({ 421 | 422 | /**/ 423 | every: function(fn, bind){ 424 | for (var i = 0, l = this.length; i < l; i++){ 425 | if ((i in this) && !fn.call(bind, this[i], i, this)) return false; 426 | } 427 | return true; 428 | }, 429 | 430 | filter: function(fn, bind){ 431 | var results = []; 432 | for (var i = 0, l = this.length; i < l; i++){ 433 | if ((i in this) && fn.call(bind, this[i], i, this)) results.push(this[i]); 434 | } 435 | return results; 436 | }, 437 | 438 | indexOf: function(item, from){ 439 | var len = this.length; 440 | for (var i = (from < 0) ? Math.max(0, len + from) : from || 0; i < len; i++){ 441 | if (this[i] === item) return i; 442 | } 443 | return -1; 444 | }, 445 | 446 | map: function(fn, bind){ 447 | var results = []; 448 | for (var i = 0, l = this.length; i < l; i++){ 449 | if (i in this) results[i] = fn.call(bind, this[i], i, this); 450 | } 451 | return results; 452 | }, 453 | 454 | some: function(fn, bind){ 455 | for (var i = 0, l = this.length; i < l; i++){ 456 | if ((i in this) && fn.call(bind, this[i], i, this)) return true; 457 | } 458 | return false; 459 | }, 460 | /**/ 461 | 462 | clean: function(){ 463 | return this.filter(function(item){ 464 | return item != null; 465 | }); 466 | }, 467 | 468 | invoke: function(methodName){ 469 | var args = Array.slice(arguments, 1); 470 | return this.map(function(item){ 471 | return item[methodName].apply(item, args); 472 | }); 473 | }, 474 | 475 | associate: function(keys){ 476 | var obj = {}, length = Math.min(this.length, keys.length); 477 | for (var i = 0; i < length; i++) obj[keys[i]] = this[i]; 478 | return obj; 479 | }, 480 | 481 | link: function(object){ 482 | var result = {}; 483 | for (var i = 0, l = this.length; i < l; i++){ 484 | for (var key in object){ 485 | if (object[key](this[i])){ 486 | result[key] = this[i]; 487 | delete object[key]; 488 | break; 489 | } 490 | } 491 | } 492 | return result; 493 | }, 494 | 495 | contains: function(item, from){ 496 | return this.indexOf(item, from) != -1; 497 | }, 498 | 499 | append: function(array){ 500 | this.push.apply(this, array); 501 | return this; 502 | }, 503 | 504 | getLast: function(){ 505 | return (this.length) ? this[this.length - 1] : null; 506 | }, 507 | 508 | getRandom: function(){ 509 | return (this.length) ? this[Number.random(0, this.length - 1)] : null; 510 | }, 511 | 512 | include: function(item){ 513 | if (!this.contains(item)) this.push(item); 514 | return this; 515 | }, 516 | 517 | combine: function(array){ 518 | for (var i = 0, l = array.length; i < l; i++) this.include(array[i]); 519 | return this; 520 | }, 521 | 522 | erase: function(item){ 523 | for (var i = this.length; i--;){ 524 | if (this[i] === item) this.splice(i, 1); 525 | } 526 | return this; 527 | }, 528 | 529 | empty: function(){ 530 | this.length = 0; 531 | return this; 532 | }, 533 | 534 | flatten: function(){ 535 | var array = []; 536 | for (var i = 0, l = this.length; i < l; i++){ 537 | var type = typeOf(this[i]); 538 | if (type == 'null') continue; 539 | array = array.concat((type == 'array' || type == 'collection' || type == 'arguments' || instanceOf(this[i], Array)) ? Array.flatten(this[i]) : this[i]); 540 | } 541 | return array; 542 | }, 543 | 544 | pick: function(){ 545 | for (var i = 0, l = this.length; i < l; i++){ 546 | if (this[i] != null) return this[i]; 547 | } 548 | return null; 549 | }, 550 | 551 | hexToRgb: function(array){ 552 | if (this.length != 3) return null; 553 | var rgb = this.map(function(value){ 554 | if (value.length == 1) value += value; 555 | return value.toInt(16); 556 | }); 557 | return (array) ? rgb : 'rgb(' + rgb + ')'; 558 | }, 559 | 560 | rgbToHex: function(array){ 561 | if (this.length < 3) return null; 562 | if (this.length == 4 && this[3] == 0 && !array) return 'transparent'; 563 | var hex = []; 564 | for (var i = 0; i < 3; i++){ 565 | var bit = (this[i] - 0).toString(16); 566 | hex.push((bit.length == 1) ? '0' + bit : bit); 567 | } 568 | return (array) ? hex : '#' + hex.join(''); 569 | } 570 | 571 | }); 572 | 573 | 574 | 575 | 576 | /* 577 | --- 578 | 579 | name: String 580 | 581 | description: Contains String Prototypes like camelCase, capitalize, test, and toInt. 582 | 583 | license: MIT-style license. 584 | 585 | requires: Type 586 | 587 | provides: String 588 | 589 | ... 590 | */ 591 | 592 | String.implement({ 593 | 594 | test: function(regex, params){ 595 | return ((typeOf(regex) == 'regexp') ? regex : new RegExp('' + regex, params)).test(this); 596 | }, 597 | 598 | contains: function(string, separator){ 599 | return (separator) ? (separator + this + separator).indexOf(separator + string + separator) > -1 : this.indexOf(string) > -1; 600 | }, 601 | 602 | trim: function(){ 603 | return this.replace(/^\s+|\s+$/g, ''); 604 | }, 605 | 606 | clean: function(){ 607 | return this.replace(/\s+/g, ' ').trim(); 608 | }, 609 | 610 | camelCase: function(){ 611 | return this.replace(/-\D/g, function(match){ 612 | return match.charAt(1).toUpperCase(); 613 | }); 614 | }, 615 | 616 | hyphenate: function(){ 617 | return this.replace(/[A-Z]/g, function(match){ 618 | return ('-' + match.charAt(0).toLowerCase()); 619 | }); 620 | }, 621 | 622 | capitalize: function(){ 623 | return this.replace(/\b[a-z]/g, function(match){ 624 | return match.toUpperCase(); 625 | }); 626 | }, 627 | 628 | escapeRegExp: function(){ 629 | return this.replace(/([-.*+?^${}()|[\]\/\\])/g, '\\$1'); 630 | }, 631 | 632 | toInt: function(base){ 633 | return parseInt(this, base || 10); 634 | }, 635 | 636 | toFloat: function(){ 637 | return parseFloat(this); 638 | }, 639 | 640 | hexToRgb: function(array){ 641 | var hex = this.match(/^#?(\w{1,2})(\w{1,2})(\w{1,2})$/); 642 | return (hex) ? hex.slice(1).hexToRgb(array) : null; 643 | }, 644 | 645 | rgbToHex: function(array){ 646 | var rgb = this.match(/\d{1,3}/g); 647 | return (rgb) ? rgb.rgbToHex(array) : null; 648 | }, 649 | 650 | substitute: function(object, regexp){ 651 | return this.replace(regexp || (/\\?\{([^{}]+)\}/g), function(match, name){ 652 | if (match.charAt(0) == '\\') return match.slice(1); 653 | return (object[name] != null) ? object[name] : ''; 654 | }); 655 | } 656 | 657 | }); 658 | 659 | 660 | /* 661 | --- 662 | 663 | name: Number 664 | 665 | description: Contains Number Prototypes like limit, round, times, and ceil. 666 | 667 | license: MIT-style license. 668 | 669 | requires: Type 670 | 671 | provides: Number 672 | 673 | ... 674 | */ 675 | 676 | Number.implement({ 677 | 678 | limit: function(min, max){ 679 | return Math.min(max, Math.max(min, this)); 680 | }, 681 | 682 | round: function(precision){ 683 | precision = Math.pow(10, precision || 0).toFixed(precision < 0 ? -precision : 0); 684 | return Math.round(this * precision) / precision; 685 | }, 686 | 687 | times: function(fn, bind){ 688 | for (var i = 0; i < this; i++) fn.call(bind, i, this); 689 | }, 690 | 691 | toFloat: function(){ 692 | return parseFloat(this); 693 | }, 694 | 695 | toInt: function(base){ 696 | return parseInt(this, base || 10); 697 | } 698 | 699 | }); 700 | 701 | Number.alias('each', 'times'); 702 | 703 | (function(math){ 704 | var methods = {}; 705 | math.each(function(name){ 706 | if (!Number[name]) methods[name] = function(){ 707 | return Math[name].apply(null, [this].concat(Array.from(arguments))); 708 | }; 709 | }); 710 | Number.implement(methods); 711 | })(['abs', 'acos', 'asin', 'atan', 'atan2', 'ceil', 'cos', 'exp', 'floor', 'log', 'max', 'min', 'pow', 'sin', 'sqrt', 'tan']); 712 | 713 | 714 | /* 715 | --- 716 | 717 | name: Function 718 | 719 | description: Contains Function Prototypes like create, bind, pass, and delay. 720 | 721 | license: MIT-style license. 722 | 723 | requires: Type 724 | 725 | provides: Function 726 | 727 | ... 728 | */ 729 | 730 | Function.extend({ 731 | 732 | attempt: function(){ 733 | for (var i = 0, l = arguments.length; i < l; i++){ 734 | try { 735 | return arguments[i](); 736 | } catch (e){} 737 | } 738 | return null; 739 | } 740 | 741 | }); 742 | 743 | Function.implement({ 744 | 745 | attempt: function(args, bind){ 746 | try { 747 | return this.apply(bind, Array.from(args)); 748 | } catch (e){} 749 | 750 | return null; 751 | }, 752 | 753 | /**/ 754 | bind: function(bind){ 755 | var self = this, 756 | args = (arguments.length > 1) ? Array.slice(arguments, 1) : null; 757 | 758 | return function(){ 759 | if (!args && !arguments.length) return self.call(bind); 760 | if (args && arguments.length) return self.apply(bind, args.concat(Array.from(arguments))); 761 | return self.apply(bind, args || arguments); 762 | }; 763 | }, 764 | /**/ 765 | 766 | pass: function(args, bind){ 767 | var self = this; 768 | if (args != null) args = Array.from(args); 769 | return function(){ 770 | return self.apply(bind, args || arguments); 771 | }; 772 | }, 773 | 774 | delay: function(delay, bind, args){ 775 | return setTimeout(this.pass((args == null ? [] : args), bind), delay); 776 | }, 777 | 778 | periodical: function(periodical, bind, args){ 779 | return setInterval(this.pass((args == null ? [] : args), bind), periodical); 780 | } 781 | 782 | }); 783 | 784 | 785 | 786 | 787 | /* 788 | --- 789 | 790 | name: Object 791 | 792 | description: Object generic methods 793 | 794 | license: MIT-style license. 795 | 796 | requires: Type 797 | 798 | provides: [Object, Hash] 799 | 800 | ... 801 | */ 802 | 803 | (function(){ 804 | 805 | var hasOwnProperty = Object.prototype.hasOwnProperty; 806 | 807 | Object.extend({ 808 | 809 | subset: function(object, keys){ 810 | var results = {}; 811 | for (var i = 0, l = keys.length; i < l; i++){ 812 | var k = keys[i]; 813 | if (k in object) results[k] = object[k]; 814 | } 815 | return results; 816 | }, 817 | 818 | map: function(object, fn, bind){ 819 | var results = {}; 820 | for (var key in object){ 821 | if (hasOwnProperty.call(object, key)) results[key] = fn.call(bind, object[key], key, object); 822 | } 823 | return results; 824 | }, 825 | 826 | filter: function(object, fn, bind){ 827 | var results = {}; 828 | for (var key in object){ 829 | var value = object[key]; 830 | if (hasOwnProperty.call(object, key) && fn.call(bind, value, key, object)) results[key] = value; 831 | } 832 | return results; 833 | }, 834 | 835 | every: function(object, fn, bind){ 836 | for (var key in object){ 837 | if (hasOwnProperty.call(object, key) && !fn.call(bind, object[key], key)) return false; 838 | } 839 | return true; 840 | }, 841 | 842 | some: function(object, fn, bind){ 843 | for (var key in object){ 844 | if (hasOwnProperty.call(object, key) && fn.call(bind, object[key], key)) return true; 845 | } 846 | return false; 847 | }, 848 | 849 | keys: function(object){ 850 | var keys = []; 851 | for (var key in object){ 852 | if (hasOwnProperty.call(object, key)) keys.push(key); 853 | } 854 | return keys; 855 | }, 856 | 857 | values: function(object){ 858 | var values = []; 859 | for (var key in object){ 860 | if (hasOwnProperty.call(object, key)) values.push(object[key]); 861 | } 862 | return values; 863 | }, 864 | 865 | getLength: function(object){ 866 | return Object.keys(object).length; 867 | }, 868 | 869 | keyOf: function(object, value){ 870 | for (var key in object){ 871 | if (hasOwnProperty.call(object, key) && object[key] === value) return key; 872 | } 873 | return null; 874 | }, 875 | 876 | contains: function(object, value){ 877 | return Object.keyOf(object, value) != null; 878 | }, 879 | 880 | toQueryString: function(object, base){ 881 | var queryString = []; 882 | 883 | Object.each(object, function(value, key){ 884 | if (base) key = base + '[' + key + ']'; 885 | var result; 886 | switch (typeOf(value)){ 887 | case 'object': result = Object.toQueryString(value, key); break; 888 | case 'array': 889 | var qs = {}; 890 | value.each(function(val, i){ 891 | qs[i] = val; 892 | }); 893 | result = Object.toQueryString(qs, key); 894 | break; 895 | default: result = key + '=' + encodeURIComponent(value); 896 | } 897 | if (value != null) queryString.push(result); 898 | }); 899 | 900 | return queryString.join('&'); 901 | } 902 | 903 | }); 904 | 905 | })(); 906 | 907 | 908 | 909 | 910 | /* 911 | --- 912 | 913 | name: Class 914 | 915 | description: Contains the Class Function for easily creating, extending, and implementing reusable Classes. 916 | 917 | license: MIT-style license. 918 | 919 | requires: [Array, String, Function, Number] 920 | 921 | provides: Class 922 | 923 | ... 924 | */ 925 | 926 | (function(){ 927 | 928 | var Class = this.Class = new Type('Class', function(params){ 929 | if (instanceOf(params, Function)) params = {initialize: params}; 930 | 931 | var newClass = function(){ 932 | reset(this); 933 | if (newClass.$prototyping) return this; 934 | this.$caller = null; 935 | var value = (this.initialize) ? this.initialize.apply(this, arguments) : this; 936 | this.$caller = this.caller = null; 937 | return value; 938 | }.extend(this).implement(params); 939 | 940 | newClass.$constructor = Class; 941 | newClass.prototype.$constructor = newClass; 942 | newClass.prototype.parent = parent; 943 | 944 | return newClass; 945 | }); 946 | 947 | var parent = function(){ 948 | if (!this.$caller) throw new Error('The method "parent" cannot be called.'); 949 | var name = this.$caller.$name, 950 | parent = this.$caller.$owner.parent, 951 | previous = (parent) ? parent.prototype[name] : null; 952 | if (!previous) throw new Error('The method "' + name + '" has no parent.'); 953 | return previous.apply(this, arguments); 954 | }; 955 | 956 | var reset = function(object){ 957 | for (var key in object){ 958 | var value = object[key]; 959 | switch (typeOf(value)){ 960 | case 'object': 961 | var F = function(){}; 962 | F.prototype = value; 963 | object[key] = reset(new F); 964 | break; 965 | case 'array': object[key] = value.clone(); break; 966 | } 967 | } 968 | return object; 969 | }; 970 | 971 | // var wrap = function(self, key, method){ 972 | // if (method.$origin) method = method.$origin; 973 | // var wrapper = function(){ 974 | // if (method.$protected && this.$caller == null) throw new Error('The method "' + key + '" cannot be called.'); 975 | // var caller = this.caller, current = this.$caller; 976 | // this.caller = current; this.$caller = wrapper; 977 | // var result = method.apply(this, arguments); 978 | // this.$caller = current; this.caller = caller; 979 | // return result; 980 | // }.extend({$owner: self, $origin: method, $name: key}); 981 | // return wrapper; 982 | // }; 983 | 984 | var wrap = function(self, key, method) { 985 | return method; 986 | }; 987 | 988 | var implement = function(key, value, retain){ 989 | if (Class.Mutators.hasOwnProperty(key)){ 990 | value = Class.Mutators[key].call(this, value); 991 | if (value == null) return this; 992 | } 993 | 994 | if (typeOf(value) == 'function'){ 995 | if (value.$hidden) return this; 996 | this.prototype[key] = (retain) ? value : wrap(this, key, value); 997 | } else { 998 | Object.merge(this.prototype, key, value); 999 | } 1000 | 1001 | return this; 1002 | }; 1003 | 1004 | var getInstance = function(klass){ 1005 | klass.$prototyping = true; 1006 | var proto = new klass; 1007 | delete klass.$prototyping; 1008 | return proto; 1009 | }; 1010 | 1011 | Class.implement('implement', implement.overloadSetter()); 1012 | 1013 | Class.Mutators = { 1014 | 1015 | Extends: function(parent){ 1016 | this.parent = parent; 1017 | this.prototype = getInstance(parent); 1018 | }, 1019 | 1020 | Implements: function(items){ 1021 | Array.from(items).each(function(item){ 1022 | var instance = new item; 1023 | for (var key in instance) implement.call(this, key, instance[key], true); 1024 | }, this); 1025 | } 1026 | }; 1027 | 1028 | })(); 1029 | 1030 | 1031 | /* 1032 | --- 1033 | 1034 | name: Class.Extras 1035 | 1036 | description: Contains Utility Classes that can be implemented into your own Classes to ease the execution of many common tasks. 1037 | 1038 | license: MIT-style license. 1039 | 1040 | requires: Class 1041 | 1042 | provides: [Class.Extras, Chain, Events, Options] 1043 | 1044 | ... 1045 | */ 1046 | 1047 | (function(){ 1048 | 1049 | this.Chain = new Class({ 1050 | 1051 | $chain: [], 1052 | 1053 | chain: function(){ 1054 | this.$chain.append(Array.flatten(arguments)); 1055 | return this; 1056 | }, 1057 | 1058 | callChain: function(){ 1059 | return (this.$chain.length) ? this.$chain.shift().apply(this, arguments) : false; 1060 | }, 1061 | 1062 | clearChain: function(){ 1063 | this.$chain.empty(); 1064 | return this; 1065 | } 1066 | 1067 | }); 1068 | 1069 | var removeOn = function(string){ 1070 | return string.replace(/^on([A-Z])/, function(full, first){ 1071 | return first.toLowerCase(); 1072 | }); 1073 | }; 1074 | 1075 | this.Events = new Class({ 1076 | 1077 | $events: {}, 1078 | 1079 | addEvent: function(type, fn, internal){ 1080 | type = removeOn(type); 1081 | 1082 | 1083 | 1084 | this.$events[type] = (this.$events[type] || []).include(fn); 1085 | if (internal) fn.internal = true; 1086 | return this; 1087 | }, 1088 | 1089 | addEvents: function(events){ 1090 | for (var type in events) this.addEvent(type, events[type]); 1091 | return this; 1092 | }, 1093 | 1094 | fireEvent: function(type, args, delay){ 1095 | type = removeOn(type); 1096 | var events = this.$events[type]; 1097 | if (!events) return this; 1098 | args = Array.from(args); 1099 | events.each(function(fn){ 1100 | if (delay) fn.delay(delay, this, args); 1101 | else fn.apply(this, args); 1102 | }, this); 1103 | return this; 1104 | }, 1105 | 1106 | removeEvent: function(type, fn){ 1107 | type = removeOn(type); 1108 | var events = this.$events[type]; 1109 | if (events && !fn.internal){ 1110 | var index = events.indexOf(fn); 1111 | if (index != -1) delete events[index]; 1112 | } 1113 | return this; 1114 | }, 1115 | 1116 | removeEvents: function(events){ 1117 | var type; 1118 | if (typeOf(events) == 'object'){ 1119 | for (type in events) this.removeEvent(type, events[type]); 1120 | return this; 1121 | } 1122 | if (events) events = removeOn(events); 1123 | for (type in this.$events){ 1124 | if (events && events != type) continue; 1125 | var fns = this.$events[type]; 1126 | for (var i = fns.length; i--;) if (i in fns){ 1127 | this.removeEvent(type, fns[i]); 1128 | } 1129 | } 1130 | return this; 1131 | } 1132 | 1133 | }); 1134 | 1135 | this.Options = new Class({ 1136 | 1137 | setOptions: function(){ 1138 | var options = this.options = Object.merge.apply(null, [{}, this.options].append(arguments)); 1139 | if (this.addEvent) for (var option in options){ 1140 | if (typeOf(options[option]) != 'function' || !(/^on[A-Z]/).test(option)) continue; 1141 | this.addEvent(option, options[option]); 1142 | delete options[option]; 1143 | } 1144 | return this; 1145 | } 1146 | 1147 | }); 1148 | 1149 | })(); 1150 | -------------------------------------------------------------------------------- /canvas.ui/slider.js: -------------------------------------------------------------------------------- 1 | var Slider = new Class({ 2 | Extends: Widget, 3 | 4 | initialize: function(options) { 5 | this._value = 0; 6 | this.handleSize = 20; 7 | this.handlePos = 0; 8 | this.lastEventTime = 0; 9 | this.min = 0; 10 | this.max = 1; 11 | this.label = ""; 12 | this.fgColor = '#0b9eff'; 13 | this.bgColor = '#3a3637'; 14 | 15 | Widget.prototype.initialize.call(this, options); 16 | }, 17 | 18 | drawCanvas: function(context) { 19 | var position = (this.height - this.handleSize) * 20 | ((this._value - this.min) / (this.max - this.min)); 21 | 22 | this.handlePos = this.height - this.handleSize - position; 23 | 24 | context.fillStyle = this.bgColor; 25 | context.fillRect(0, 0, this.width, this.height); 26 | 27 | context.fillStyle = this.fgColor; 28 | context.fillRect(0, this.handlePos, this.width, this.height-this.handlePos);//*-1); 29 | 30 | // context.fillStyle = this.fontColor; 31 | // context.font = "20px Helvetica"; 32 | // context.fillText(this.label, 2, this.height - 40, this.width - 20) 33 | }, 34 | 35 | value: function(value) { 36 | if (value === undefined) { 37 | return this._value; 38 | } 39 | else { 40 | this._value = Math.max(this.min, Math.min(this.max, value)); 41 | } 42 | }, 43 | 44 | setValueTimed: function(value) { 45 | if (this.lastEventTime + 500 < Number(new Date())) { 46 | this.value(value); 47 | } 48 | }, 49 | 50 | handleEvent: function(event) { 51 | var value = this.min + ((this.height - event.localY) / this.height) * (this.max - this.min); 52 | 53 | if (Math.abs(value - this._value) > 0.001) { 54 | this.lastEventTime = Number(new Date()); 55 | this.value(value); 56 | //console.log("slider.js- handle changed"); 57 | this.fireEvent("change", this._value); 58 | }/*else{ 59 | console.log("slider.js- handle changed --else"); 60 | }*/ 61 | }, 62 | 63 | onTouchDown: function(event) { 64 | this.handleEvent(event); 65 | return true; 66 | }, 67 | 68 | onTouchMove: function(event) { 69 | this.handleEvent(event); 70 | return true; 71 | } 72 | }); 73 | -------------------------------------------------------------------------------- /canvas.ui/touch-tracker.js: -------------------------------------------------------------------------------- 1 | var TouchTracker = new Class({ 2 | 3 | initialize: function(root) { 4 | this.root = root; 5 | this.touchModel = null; 6 | this.touches = {}; 7 | 8 | if (navigator.userAgent.match(/iPad|iPhone/i)) { 9 | this.touchModel = "apple"; 10 | this.event = { 11 | down: 'touchstart', 12 | move: 'touchmove', 13 | up: 'touchend' 14 | }; 15 | } 16 | else { 17 | if (window.location.hash == '#touch' && navigator.userAgent.match(/Firefox/i)) { 18 | document.multitouchData = true; 19 | this.touchModel = "mozilla"; 20 | this.event = { 21 | down: 'MozTouchDown', 22 | move: 'MozTouchMove', 23 | up: 'MozTouchUp' 24 | }; 25 | } 26 | else { 27 | this.touchModel = "mouse"; 28 | this.event = { 29 | down: 'mousedown', 30 | move: 'mousemove', 31 | up: 'mouseup' 32 | }; 33 | } 34 | } 35 | 36 | // document.documentElement.style.webkitTapHighlightColor = "rgba(0,0,0,0)"; 37 | // document.documentElement.style.webkitTouchCallout = "none"; 38 | 39 | document.onselectstart = function () { return false; }; 40 | document.onselect = function () { return false; }; 41 | 42 | document.ongesturechange = function(e) { e.preventDefault(); }; 43 | document.ongesturestart = function(e) { e.preventDefault(); }; 44 | 45 | document.addEventListener(this.event.down, this.onTouchDown.bind(this), false); 46 | document.addEventListener(this.event.move, this.onTouchMove.bind(this), false); 47 | document.addEventListener(this.event.up, this.onTouchUp.bind(this), false); 48 | 49 | // this.scrollManager = new ScrollManager(); 50 | }, 51 | 52 | log: function(str) { 53 | this.controller.log(str); 54 | }, 55 | 56 | createEvent :function(widget, event) { 57 | return { 58 | localX: event.pageX - widget.pageX(), 59 | localY: event.pageY - widget.pageY() 60 | }; 61 | }, 62 | 63 | eventInside: function(widget, event) { 64 | var x = event.pageX - widget.pageX(); 65 | var y = event.pageY - widget.pageY(); 66 | 67 | return x >= 0 && x <= widget.width && y >= 0 && y <= widget.height; 68 | }, 69 | 70 | findTarget: function(widget, event) { 71 | if (widget.visible && this.eventInside(widget, event)) { 72 | for (var i = widget.children.length - 1; i >= 0; i--) { 73 | var target = this.findTarget(widget.children[i], event); 74 | if (target) { 75 | return target; 76 | } 77 | } 78 | return widget; 79 | } 80 | 81 | return false; 82 | }, 83 | 84 | bubbleEvent: function(widget, event) { 85 | if (this.handleEvent(widget, "onTouchDown", event)) { 86 | this.touches[event.identifier] = widget; 87 | } 88 | else { 89 | if (widget.parent) { 90 | this.bubbleEvent(widget, event); 91 | } 92 | } 93 | }, 94 | 95 | handleEvent: function(widget, method, event) { 96 | return widget[method].call(widget, this.createEvent(widget, event)); 97 | }, 98 | 99 | findTargetAndBubble: function(event) { 100 | var target = this.findTarget(this.root, event); 101 | 102 | if (target && target.parent) { 103 | this.bubbleEvent(target, event); 104 | } 105 | else { 106 | // this.scrollManager.onTouchDown(event); 107 | } 108 | }, 109 | 110 | foreachTouch: function(event, method) { 111 | for (var i = 0; i < event.changedTouches.length; i++) { 112 | this[method].call(this, event.changedTouches[i]); 113 | } 114 | }, 115 | 116 | prepareEvent: function(event) { 117 | if (event.preventDefault) { 118 | event.preventDefault(); 119 | } 120 | 121 | if (event.streamId !== undefined) { 122 | event.identifier = event.streamId; 123 | } 124 | 125 | if (event.identifier === undefined) { 126 | event.identifier = 1; 127 | } 128 | }, 129 | 130 | onTouchDown: function(event) { 131 | this.prepareEvent(event); 132 | 133 | if (event.touches) { 134 | this.foreachTouch(event, 'onTouchDown'); 135 | } 136 | else { 137 | this.findTargetAndBubble(event); 138 | } 139 | 140 | return false; 141 | }, 142 | 143 | onTouchMove: function(event) { 144 | this.prepareEvent(event); 145 | 146 | if (event.touches) { 147 | this.foreachTouch(event, 'onTouchMove'); 148 | } 149 | else { 150 | var widget = this.touches[event.identifier]; 151 | 152 | if (widget) { 153 | this.handleEvent(widget, "onTouchMove", event); 154 | } 155 | } 156 | return false; 157 | }, 158 | 159 | onTouchUp: function(event) { 160 | this.prepareEvent(event); 161 | 162 | if (event.touches) { 163 | this.foreachTouch(event, 'onTouchUp'); 164 | } 165 | else { 166 | var widget = this.touches[event.identifier]; 167 | 168 | if (widget) { 169 | this.handleEvent(widget, "onTouchUp", event); 170 | } 171 | 172 | delete this.touches[event.identifier]; 173 | } 174 | return false; 175 | } 176 | }); -------------------------------------------------------------------------------- /canvas.ui/widget.js: -------------------------------------------------------------------------------- 1 | var WidgetId = 1; 2 | 3 | var Widget = new Class({ 4 | 5 | Implements: Events, 6 | 7 | initialize: function(options) { 8 | this.children = []; 9 | this.id = WidgetId++; 10 | this.x = 0; 11 | this.y = 0; 12 | this.width = 0; 13 | this.height = 0; 14 | this.visible = true; 15 | 16 | this.marginTop = 0; 17 | this.marginBottom = 0; 18 | this.marginLeft = 0; 19 | this.marginRight = 0; 20 | 21 | this.sizeHint = 1; 22 | this.set(options); 23 | 24 | if (!this._parent) { 25 | this.initCanvas(); 26 | } 27 | }, 28 | 29 | on: function(event, callback) { 30 | if (callback) { 31 | this.addEvent(event, callback); 32 | } 33 | else { 34 | for (var name in event) { 35 | this.addEvent(name, event[name]); 36 | } 37 | } 38 | }, 39 | 40 | onTouchDown: function(event) { 41 | return false; 42 | }, 43 | 44 | onTouchMove: function(event) { 45 | return false; 46 | }, 47 | 48 | onTouchUp: function(event) { 49 | return false; 50 | }, 51 | 52 | doLayout: function() { 53 | switch (this.layout) { 54 | case 'horizontal': 55 | this.doHorizontalLayout(); 56 | break; 57 | case 'vertical': 58 | this.doVerticalLayout(); 59 | break; 60 | } 61 | 62 | this.layoutChildren(); 63 | }, 64 | 65 | layoutChildren: function() { 66 | this.children.each(function(child) { 67 | child.doLayout(); 68 | }); 69 | }, 70 | 71 | sumSizeHints: function() { 72 | var size = 0; 73 | 74 | this.children.each(function(child) { 75 | size += child.sizeHint; 76 | }); 77 | 78 | return size; 79 | }, 80 | 81 | sumVerticalMargins: function() { 82 | var margin = 0; 83 | 84 | this.children.each(function(child) { 85 | margin += child.marginTop + child.marginBottom; 86 | }); 87 | 88 | return margin; 89 | }, 90 | 91 | sumHorizontalMargins: function() { 92 | var margin = 0; 93 | 94 | this.children.each(function(child) { 95 | margin += child.marginLeft + child.marginRight; 96 | }); 97 | 98 | return margin; 99 | }, 100 | 101 | doHorizontalLayout: function() { 102 | var x = 0; 103 | var y = 0; 104 | var width = 0; 105 | var w = (this.width - this.sumHorizontalMargins()) / this.sumSizeHints(); 106 | var h = this.height; 107 | 108 | this.children.each(function(child) { 109 | x += child.marginLeft; 110 | child.extent(x, y, w * child.sizeHint, h); 111 | x += child.width; 112 | x += child.marginRight; 113 | }); 114 | }, 115 | 116 | doVerticalLayout: function() { 117 | var x = 0; 118 | var y = 0; 119 | var w = this.width; 120 | var h = (this.height - this.sumVerticalMargins()) / this.sumSizeHints(); 121 | 122 | this.children.each(function(child) { 123 | y += child.marginTop; 124 | child.extent(x, y, w, h * child.sizeHint); 125 | y += child.height; 126 | y += child.marginBottom; 127 | }); 128 | }, 129 | 130 | initCanvas: function() { 131 | this.canvas = document.createElement('canvas'); 132 | this.canvas.setAttribute("width", window.innerWidth); 133 | this.canvas.setAttribute("height", window.innerHeight); 134 | this.touchtracker = new TouchTracker(this); 135 | 136 | document.body.appendChild(this.canvas); 137 | 138 | setInterval(function() { 139 | this.draw() 140 | }.bind(this), 50); 141 | }, 142 | 143 | drawCanvas: function(context) { 144 | }, 145 | 146 | draw: function(context) { 147 | if (!this.visible) { 148 | return; 149 | } 150 | 151 | if (context) { 152 | context.save(); 153 | context.translate(this.x, this.y); 154 | 155 | this.drawCanvas(context); 156 | this.drawChildren(context); 157 | 158 | context.restore(); 159 | } 160 | else { 161 | this.canvas.setAttribute("width", window.innerWidth); 162 | this.canvas.setAttribute("height", window.innerHeight); 163 | this.width = window.innerWidth; 164 | this.height = window.innerHeight; 165 | this.doLayout(); 166 | 167 | context = this.canvas.getContext("2d"); 168 | context.clearRect(0, 0, this.width, this.height); 169 | 170 | this.drawChildren(context); 171 | } 172 | }, 173 | 174 | drawChildren: function(context) { 175 | this.children.each(function(child) { 176 | child.draw(context); 177 | }); 178 | }, 179 | 180 | redraw: function() { 181 | this.clear(); 182 | this.draw(); 183 | }, 184 | 185 | set: function(options) { 186 | for (var name in options) { 187 | if (name == 'type') { 188 | continue; 189 | } 190 | 191 | if (typeof(this[name]) == "function") { 192 | this[name](options[name]); 193 | } 194 | else { 195 | this[name] = options[name]; 196 | } 197 | } 198 | }, 199 | 200 | listen: function() { 201 | this.controller.addEvent.apply(this.controller, arguments); 202 | }, 203 | 204 | send: function() { 205 | this.controller.send.apply(this.controller, arguments); 206 | }, 207 | 208 | add: function(options) { 209 | var type = options.type || Widget; 210 | 211 | if (!options.controller) { 212 | options.controller = this.controller; 213 | } 214 | 215 | if (!options._parent) { 216 | options._parent = this; 217 | } 218 | 219 | var child = new type.prototype.$constructor(options); 220 | 221 | this.children.push(child); 222 | 223 | return child; 224 | }, 225 | 226 | find: function(id) { 227 | for (var i = 0; i < this.children.length; i++) { 228 | if (this.children[i].id == id) { 229 | return i; 230 | } 231 | } 232 | 233 | return null; 234 | }, 235 | 236 | remove: function(widget) { 237 | var index = this.find(widget.id); 238 | this.children.splice(index, 1); 239 | widget._parent = null; 240 | }, 241 | 242 | child: function(key) { 243 | for (var i = 0; i < this.children.length; i++) { 244 | if (this.children[i].key == key) { 245 | return this.children[i]; 246 | } 247 | } 248 | return null; 249 | }, 250 | 251 | pos: function(x, y) { 252 | if (x === undefined) { 253 | return [this.x, this.y]; 254 | } 255 | else { 256 | this.x = x; 257 | this.y = y; 258 | return this; 259 | } 260 | }, 261 | 262 | extent: function(x, y, w, h) { 263 | if (x === undefined) { 264 | return [this.x, this.y, this.width, this.height]; 265 | } 266 | else { 267 | this.x = x; 268 | this.y = y; 269 | this.width = w; 270 | this.height = h; 271 | return this; 272 | } 273 | }, 274 | 275 | size: function(w, h) { 276 | if (w === undefined) { 277 | return [this.width, this.height]; 278 | } 279 | else { 280 | this.width = w; 281 | this.height = h; 282 | return this; 283 | } 284 | }, 285 | 286 | root: function() { 287 | if (this._parent) { 288 | return this._parent.root(); 289 | } 290 | else { 291 | return this; 292 | } 293 | }, 294 | 295 | pageX: function() { 296 | if (this._parent) { 297 | return this._parent.pageX() + this.x; 298 | } 299 | else { 300 | return this.x; 301 | } 302 | }, 303 | 304 | pageY: function() { 305 | if (this._parent) { 306 | return this._parent.pageY() + this.y; 307 | } 308 | else { 309 | return this.y; 310 | } 311 | } 312 | 313 | }); 314 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /node/bin/cygcrypto-0.9.8.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vnoise/vtouch/a25d881c0096070d90aaab4d16800192865815f4/node/bin/cygcrypto-0.9.8.dll -------------------------------------------------------------------------------- /node/bin/cyggcc_s-1.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vnoise/vtouch/a25d881c0096070d90aaab4d16800192865815f4/node/bin/cyggcc_s-1.dll -------------------------------------------------------------------------------- /node/bin/cygiconv-2.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vnoise/vtouch/a25d881c0096070d90aaab4d16800192865815f4/node/bin/cygiconv-2.dll -------------------------------------------------------------------------------- /node/bin/cygintl-8.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vnoise/vtouch/a25d881c0096070d90aaab4d16800192865815f4/node/bin/cygintl-8.dll -------------------------------------------------------------------------------- /node/bin/cygpath.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vnoise/vtouch/a25d881c0096070d90aaab4d16800192865815f4/node/bin/cygpath.exe -------------------------------------------------------------------------------- /node/bin/cygssl-0.9.8.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vnoise/vtouch/a25d881c0096070d90aaab4d16800192865815f4/node/bin/cygssl-0.9.8.dll -------------------------------------------------------------------------------- /node/bin/cygstdc++-6.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vnoise/vtouch/a25d881c0096070d90aaab4d16800192865815f4/node/bin/cygstdc++-6.dll -------------------------------------------------------------------------------- /node/bin/cygwin1.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vnoise/vtouch/a25d881c0096070d90aaab4d16800192865815f4/node/bin/cygwin1.dll -------------------------------------------------------------------------------- /node/bin/cygz.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vnoise/vtouch/a25d881c0096070d90aaab4d16800192865815f4/node/bin/cygz.dll -------------------------------------------------------------------------------- /node/bin/env.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vnoise/vtouch/a25d881c0096070d90aaab4d16800192865815f4/node/bin/env.exe -------------------------------------------------------------------------------- /node/bin/gzip.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vnoise/vtouch/a25d881c0096070d90aaab4d16800192865815f4/node/bin/gzip.exe -------------------------------------------------------------------------------- /node/bin/man.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vnoise/vtouch/a25d881c0096070d90aaab4d16800192865815f4/node/bin/man.exe -------------------------------------------------------------------------------- /node/bin/node-waf: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os, sys 3 | 4 | 5 | join = os.path.join 6 | bindir = os.path.dirname(os.path.realpath(__file__)) 7 | prefix = join(bindir, "..") 8 | wafdir = join(prefix, "lib", "node") 9 | 10 | w = join(wafdir, 'wafadmin') 11 | t = join(w, 'Tools') 12 | sys.path = [w, t] + sys.path 13 | 14 | import Scripting 15 | VERSION="1.5.16" 16 | Scripting.prepare(t, os.getcwd(), VERSION, wafdir) 17 | sys.exit(0) 18 | -------------------------------------------------------------------------------- /node/bin/node.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vnoise/vtouch/a25d881c0096070d90aaab4d16800192865815f4/node/bin/node.exe -------------------------------------------------------------------------------- /node/bin/npm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vnoise/vtouch/a25d881c0096070d90aaab4d16800192865815f4/node/bin/npm -------------------------------------------------------------------------------- /node/bin/npm-get-uid-gid: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vnoise/vtouch/a25d881c0096070d90aaab4d16800192865815f4/node/bin/npm-get-uid-gid -------------------------------------------------------------------------------- /node/bin/npm-get-uid-gid@0.3.14: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vnoise/vtouch/a25d881c0096070d90aaab4d16800192865815f4/node/bin/npm-get-uid-gid@0.3.14 -------------------------------------------------------------------------------- /node/bin/npm@0.3.14: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vnoise/vtouch/a25d881c0096070d90aaab4d16800192865815f4/node/bin/npm@0.3.14 -------------------------------------------------------------------------------- /node/bin/read-package-json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vnoise/vtouch/a25d881c0096070d90aaab4d16800192865815f4/node/bin/read-package-json -------------------------------------------------------------------------------- /node/bin/read-package-json@0.3.14: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vnoise/vtouch/a25d881c0096070d90aaab4d16800192865815f4/node/bin/read-package-json@0.3.14 -------------------------------------------------------------------------------- /node/bin/runnode.cmd: -------------------------------------------------------------------------------- 1 | rem @echo off 2 | call %~dp0\setenv 3 | echo on 4 | FOR /F "tokens=*" %%i in ('cygpath.exe %1') do SET convArg=%%i 5 | node.exe "%convArg%" 6 | -------------------------------------------------------------------------------- /node/bin/setenv.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | echo Setting environment for using Node.JS 3 | path %PATH%;%~dp0 4 | set TAR_OPTIONS=--no-same-owner 5 | -------------------------------------------------------------------------------- /node/bin/shell.cmd: -------------------------------------------------------------------------------- 1 | @%comspec% /k setenv -------------------------------------------------------------------------------- /node/bin/su.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vnoise/vtouch/a25d881c0096070d90aaab4d16800192865815f4/node/bin/su.exe -------------------------------------------------------------------------------- /node/bin/sudo.cmd: -------------------------------------------------------------------------------- 1 | %1 %2 %3 %4 %5 %6 %7 %8 %9 -------------------------------------------------------------------------------- /node/bin/tar.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vnoise/vtouch/a25d881c0096070d90aaab4d16800192865815f4/node/bin/tar.exe -------------------------------------------------------------------------------- /node/etc/resolv.conf: -------------------------------------------------------------------------------- 1 | nameserver 8.8.8.8 2 | nameserver 8.8.4.4 3 | 4 | -------------------------------------------------------------------------------- /osc/node-jspack/jspack.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vnoise/vtouch/a25d881c0096070d90aaab4d16800192865815f4/osc/node-jspack/jspack.js -------------------------------------------------------------------------------- /osc/osc.js: -------------------------------------------------------------------------------- 1 | require.paths.unshift(__dirname + '/node-jspack'); 2 | var buffer = require('buffer'); 3 | var dgram = require('dgram'); 4 | var sys = require('sys'); 5 | 6 | var jspack = require('jspack').jspack; 7 | 8 | 9 | var Message = function (address) { 10 | this.address = address; 11 | this.typetags = ','; 12 | this.message = []; 13 | } 14 | Message.prototype = { 15 | append: function (arg, typehint) { 16 | if (arg instanceof Array) { 17 | for (var i in arg) { 18 | this.append(arg[i], typehint); 19 | } 20 | return null; 21 | } 22 | if (typeof(arg) == 'object') { 23 | for (var k in arg) { 24 | this.append([k, arg[k]]); 25 | } 26 | return null; 27 | } 28 | 29 | if (typehint == 'b') { 30 | binary = OSCBlob(arg); 31 | tag = 'b'; 32 | } else if (typehint == 't') { 33 | binary = OSCTimeTag(arg); 34 | tag = 't'; 35 | } else { 36 | rv = OSCArgument(arg, typehint); 37 | tag = rv[0]; 38 | binary = rv[1]; 39 | } 40 | 41 | this.typetags += tag; 42 | this.message = this.message.concat(binary); 43 | }, 44 | toBinary: function () { 45 | var binary = OSCString(this.address); 46 | binary = binary.concat(OSCString(this.typetags)); 47 | binary = binary.concat(this.message); 48 | return binary; 49 | }, 50 | } 51 | exports.Message = Message; 52 | 53 | 54 | var Bundle = function (address, time) { 55 | Message.call(this, address); 56 | this.timetag = time || 0; 57 | } 58 | sys.inherits(Bundle, Message); 59 | Bundle.prototype.append = function (arg, typehint) { 60 | var binary; 61 | if (arg instanceof Message) { 62 | binary = OSCBlob(arg.toBinary()); 63 | } else { 64 | var msg = Message(this.address); 65 | if (typeof(arg) == 'Object') { 66 | if (arg.addr) { 67 | msg.address = arg.addr; 68 | } 69 | if (arg.args) { 70 | msg.append(arg.args, typehint); 71 | } 72 | } else { 73 | msg.append(arg, typehint); 74 | } 75 | binary = OSCBlob(msg.toBinary()); 76 | } 77 | this.message += binary; 78 | this.typetags += 'b'; 79 | }; 80 | Bundle.prototype.toBinary = function () { 81 | var binary = OSCString('#bundle'); 82 | binary = binary.concat(OSCTimeTag(this.timetag)); 83 | binary = binary.concat(this.message); 84 | return binary; 85 | }; 86 | exports.Bundle = Bundle; 87 | 88 | 89 | var OSCString = function (next) { 90 | var len = Math.ceil((next.length + 1) / 4.0) * 4; 91 | return jspack.Pack('>' + len + 's', [next]); 92 | } 93 | 94 | 95 | var OSCBlob = function (next) { 96 | var binary; 97 | if (typeof(next) == 'String') { 98 | var len = Math.ceil((next.length) / 4.0) * 4; 99 | binary = jspack.Pack('>i' + len + 's', [len, next]); 100 | } else { 101 | binary = ''; 102 | } 103 | return binary; 104 | } 105 | 106 | 107 | var OSCArgument = function (next, typehint) { 108 | var binary, tag; 109 | if (!typehint) { 110 | if (typeof(next) == 'number') { 111 | if (next.toString().indexOf('.') != -1) { 112 | binary = jspack.Pack('>f', [next]); 113 | tag = 'f'; 114 | } else { 115 | binary = jspack.Pack('>i', [next]); 116 | tag = 'i'; 117 | } 118 | } else { 119 | binary = OSCString(next); 120 | tag = 's'; 121 | } 122 | } else if (typehint == 'f') { 123 | try { 124 | binary = jspack.Pack('>f', [parseFloat(next)]); 125 | tag = 'f'; 126 | } catch (e) { 127 | binary = OSCString(next); 128 | tag = 's'; 129 | } 130 | } else if (typehint == 'i') { 131 | try { 132 | binary = jspack.Pack('>i', [parseInt(next)]); 133 | tag = 'i'; 134 | } catch (e) { 135 | binary = OSCString(next); 136 | tag = 's'; 137 | } 138 | } else { 139 | binary = OSCString(next); 140 | tag = 's'; 141 | } 142 | return [tag, binary]; 143 | } 144 | 145 | var OSCTimeTag = function (time) { 146 | // Not Implemented Yet 147 | return jspack.Pack('>LL', 0, 1); 148 | } 149 | 150 | 151 | var Client = function (port, host) { 152 | this.port = port; 153 | this.host = host // || '127.0.0.1'; 154 | this._sock = dgram.createSocket('udp4'); 155 | } 156 | Client.prototype = { 157 | send: function (msg) { 158 | var binary = msg.toBinary(); 159 | var b = new buffer.Buffer(binary, 'binary'); 160 | this._sock.send(b, 0, b.length, this.port, this.host); 161 | }, 162 | sendSimple: function (address, data) { 163 | var msg = new Message(address); 164 | msg.append(data); 165 | this.send(msg); 166 | }, 167 | } 168 | exports.Client = Client; 169 | -------------------------------------------------------------------------------- /server.bat: -------------------------------------------------------------------------------- 1 | "node\bin\node.exe" "server.js" 2 | pause -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | var sys = require("sys"); 2 | var fs = require('fs'); 3 | var http = require("http"); 4 | var events = require('events'); 5 | var io = require('./Socket.IO-node/lib/socket.io'); 6 | var osc = require('./node-osc/lib/osc'); 7 | var _osc = require('./osc/osc'); 8 | 9 | 10 | Function.prototype.bind = function(object) { 11 | var fn = this; 12 | var args = Array.prototype.slice.call(arguments, 1); 13 | return function() { 14 | return fn.apply(object, args.concat(Array.prototype.slice.call(arguments, 0))); 15 | }; 16 | }; 17 | 18 | 19 | function index(req, res) { 20 | res.writeHead(200, {'Content-Type': 'text/html'}); 21 | fs.readFile("index.html", function(err, file) { 22 | res.end(file); 23 | }); 24 | } 25 | 26 | function file(req, res) { 27 | var type = 'text/plain'; 28 | 29 | if (req.url.match(/\.html$/)) { 30 | type = 'text/html'; 31 | } 32 | 33 | if (req.url.match(/\.js$/)) { 34 | type = 'text/javascript'; 35 | } 36 | 37 | if (req.url.match(/\.css$/)) { 38 | type = 'text/css'; 39 | } 40 | 41 | res.writeHead(200, {'Content-Type': type}); 42 | fs.readFile(req.url.slice(1), function(err, file) { 43 | res.end(file); 44 | }); 45 | } 46 | 47 | 48 | var routes = [ 49 | [/^$/, index], 50 | [/.*/, file] 51 | ]; 52 | 53 | function controller(req, res) { 54 | sys.puts(req.url); 55 | 56 | for (var i = 0; i < routes.length; i++) { 57 | if (req.url.slice(1).match(routes[i][0])) { 58 | routes[i][1](req, res); 59 | return; 60 | } 61 | } 62 | } 63 | 64 | var clients = []; 65 | 66 | var oscServer = new osc.Server(9001, '0.0.0.0'); 67 | var oscClient = new _osc.Client(9000, '127.0.0.1'); 68 | 69 | oscServer.on('message', function(message) { 70 | 71 | if (message[0] == '#bundle'){ 72 | console.log("-bundle---------"); 73 | console.log(message); 74 | 75 | for (i = 2; i < message.length; i++){ 76 | _address = ""; 77 | _args = ""; 78 | _address = message[i][0]; 79 | _args = message[i].slice(1,message[i].length); 80 | //console.log("buffer content---->"+_address+"--->"+_args); 81 | for (var id in clients) { 82 | clients[id].send({ 83 | address: _address, 84 | args: _args 85 | }); 86 | } 87 | } 88 | }else{ 89 | //console.log("-message---------"); 90 | //console.log(message); 91 | 92 | for (var id in clients) { 93 | //console.log("---mesage-slice ->"+message.slice(1)); 94 | clients[id].send({ 95 | address: message[0], 96 | args: message.slice(1) 97 | }); 98 | } 99 | } 100 | }); 101 | var server = http.createServer(controller); 102 | 103 | server.listen(80, "0.0.0.0"); 104 | 105 | var io = io.listen(server); 106 | 107 | io.on('connection', function(client) { 108 | clients[client.sessionId] = client; 109 | 110 | client.send({address: 'connect', args: [] }); 111 | 112 | client.on('message', function(message) { 113 | var types = message.types; 114 | var args = message.args; 115 | var msg = new _osc.Message(message.address); 116 | 117 | if (args.length != types.length) { 118 | console.log("warning: args.length != types.length"); 119 | } 120 | 121 | for (var i in args) { 122 | msg.append(args[i], types[i]); 123 | } 124 | 125 | // console.log(message); 126 | 127 | oscClient.send(msg); 128 | }); 129 | 130 | client.on('disconnect', function() { 131 | delete clients[client.sessionId]; 132 | }); 133 | }); 134 | --------------------------------------------------------------------------------