patchmaster 0.0.6 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,99 @@
1
+ /*
2
+ * jQuery Hotkeys Plugin
3
+ * Copyright 2010, John Resig
4
+ * Dual licensed under the MIT or GPL Version 2 licenses.
5
+ *
6
+ * Based upon the plugin by Tzury Bar Yochay:
7
+ * http://github.com/tzuryby/hotkeys
8
+ *
9
+ * Original idea by:
10
+ * Binny V A, http://www.openjs.com/scripts/events/keyboard_shortcuts/
11
+ */
12
+
13
+ (function(jQuery){
14
+
15
+ jQuery.hotkeys = {
16
+ version: "0.8",
17
+
18
+ specialKeys: {
19
+ 8: "backspace", 9: "tab", 13: "return", 16: "shift", 17: "ctrl", 18: "alt", 19: "pause",
20
+ 20: "capslock", 27: "esc", 32: "space", 33: "pageup", 34: "pagedown", 35: "end", 36: "home",
21
+ 37: "left", 38: "up", 39: "right", 40: "down", 45: "insert", 46: "del",
22
+ 96: "0", 97: "1", 98: "2", 99: "3", 100: "4", 101: "5", 102: "6", 103: "7",
23
+ 104: "8", 105: "9", 106: "*", 107: "+", 109: "-", 110: ".", 111 : "/",
24
+ 112: "f1", 113: "f2", 114: "f3", 115: "f4", 116: "f5", 117: "f6", 118: "f7", 119: "f8",
25
+ 120: "f9", 121: "f10", 122: "f11", 123: "f12", 144: "numlock", 145: "scroll", 191: "/", 224: "meta"
26
+ },
27
+
28
+ shiftNums: {
29
+ "`": "~", "1": "!", "2": "@", "3": "#", "4": "$", "5": "%", "6": "^", "7": "&",
30
+ "8": "*", "9": "(", "0": ")", "-": "_", "=": "+", ";": ": ", "'": "\"", ",": "<",
31
+ ".": ">", "/": "?", "\\": "|"
32
+ }
33
+ };
34
+
35
+ function keyHandler( handleObj ) {
36
+ // Only care when a possible input has been specified
37
+ if ( typeof handleObj.data !== "string" ) {
38
+ return;
39
+ }
40
+
41
+ var origHandler = handleObj.handler,
42
+ keys = handleObj.data.toLowerCase().split(" ");
43
+
44
+ handleObj.handler = function( event ) {
45
+ // Don't fire in text-accepting inputs that we didn't directly bind to
46
+ if ( this !== event.target && (/textarea|select/i.test( event.target.nodeName ) ||
47
+ event.target.type === "text") ) {
48
+ return;
49
+ }
50
+
51
+ // Keypress represents characters, not special keys
52
+ var special = event.type !== "keypress" && jQuery.hotkeys.specialKeys[ event.which ],
53
+ character = String.fromCharCode( event.which ).toLowerCase(),
54
+ key, modif = "", possible = {};
55
+
56
+ // check combinations (alt|ctrl|shift+anything)
57
+ if ( event.altKey && special !== "alt" ) {
58
+ modif += "alt+";
59
+ }
60
+
61
+ if ( event.ctrlKey && special !== "ctrl" ) {
62
+ modif += "ctrl+";
63
+ }
64
+
65
+ // TODO: Need to make sure this works consistently across platforms
66
+ if ( event.metaKey && !event.ctrlKey && special !== "meta" ) {
67
+ modif += "meta+";
68
+ }
69
+
70
+ if ( event.shiftKey && special !== "shift" ) {
71
+ modif += "shift+";
72
+ }
73
+
74
+ if ( special ) {
75
+ possible[ modif + special ] = true;
76
+
77
+ } else {
78
+ possible[ modif + character ] = true;
79
+ possible[ modif + jQuery.hotkeys.shiftNums[ character ] ] = true;
80
+
81
+ // "$" can be triggered as "Shift+4" or "Shift+$" or just "$"
82
+ if ( modif === "shift+" ) {
83
+ possible[ jQuery.hotkeys.shiftNums[ character ] ] = true;
84
+ }
85
+ }
86
+
87
+ for ( var i = 0, l = keys.length; i < l; i++ ) {
88
+ if ( possible[ keys[i] ] ) {
89
+ return origHandler.apply( this, arguments );
90
+ }
91
+ }
92
+ };
93
+ }
94
+
95
+ jQuery.each([ "keydown", "keyup", "keypress" ], function() {
96
+ jQuery.event.special[ this ] = { add: keyHandler };
97
+ });
98
+
99
+ })( jQuery );
@@ -0,0 +1,81 @@
1
+ $ = jQuery
2
+ CONN_HEADERS = """
3
+ <tr>
4
+ <th>Input</th>
5
+ <th>Chan</th>
6
+ <th>Output</th>
7
+ <th>Chan</th>
8
+ <th>Prog</th>
9
+ <th>Zone</th>
10
+ <th>Xpose</th>
11
+ <th>Filter</th>
12
+ </tr>
13
+ """
14
+ COLOR_SCHEMES = ['default', 'green', 'amber', 'blue'];
15
+ color_scheme_index = 0;
16
+
17
+ list_item = (val, highlighted_value) ->
18
+ classes = if val == highlighted_value then "selected reverse-#{COLOR_SCHEMES[color_scheme_index]}" else ''
19
+ "<li class=\"#{classes}\">#{val}</li>"
20
+
21
+ list = (id, vals, highlighted_value) ->
22
+ lis = (list_item(val, highlighted_value) for val in vals)
23
+ $('#' + id).html(lis.join("\n"))
24
+
25
+ connection_row = (conn) ->
26
+ vals = (conn[key] for key in ['input', 'input_chan', 'output', 'output_chan', 'pc', 'zone', 'xpose', 'filter'])
27
+ "<tr><td>#{vals.join('</td><td>')}</td></tr>"
28
+
29
+ connection_rows = (connections) ->
30
+ rows = (connection_row(conn) for conn in connections)
31
+ $('#patch').html(CONN_HEADERS + "\n" + rows.join("\n"))
32
+
33
+ maybe_name = (data, key) -> if data[key] then data[key]['name'] else ''
34
+
35
+ message = (str) -> $('#message').html(str)
36
+
37
+ kp = (action) ->
38
+ $.getJSON(action, (data) ->
39
+ list('song-lists', data['lists'], data['list'])
40
+ list('songs', data['songs'], maybe_name(data, 'song'))
41
+ list('triggers', data['triggers'])
42
+
43
+ if data['song']?
44
+ list('song', data['song']['patches'], maybe_name(data, 'patch'))
45
+ if data['patch']?
46
+ connection_rows(data['patch']['connections'])
47
+
48
+ message(data['message']) if data['message']?
49
+ )
50
+
51
+ cycle_colors = () ->
52
+ base_class = COLOR_SCHEMES[color_scheme_index]
53
+ if color_scheme_index >= 0
54
+ $('body').removeClass(base_class)
55
+ $('.selected, th, td#appname').removeClass("reverse-#{base_class}")
56
+ $('tr, td, th').removeClass("#{base_class}-border")
57
+
58
+ color_scheme_index = (color_scheme_index + 1) % COLOR_SCHEMES.length
59
+
60
+ base_class = COLOR_SCHEMES[color_scheme_index]
61
+ $('body').addClass(base_class)
62
+ $('.selected, th, td#appname').addClass("reverse-#{base_class}")
63
+ $('tr, td, th').addClass("#{base_class}-border")
64
+
65
+ color_scheme = base_class
66
+
67
+ bindings =
68
+ 'j': 'next_patch'
69
+ 'down': 'next_patch'
70
+ 'k': 'prev_patch'
71
+ 'up': 'prev_patch'
72
+ 'n': 'next_song'
73
+ 'left': 'next_song'
74
+ 'p': 'prev_song'
75
+ 'right': 'prev_song'
76
+ 'esc': 'panic'
77
+ f = (key, val) -> $(document).bind('keydown', key, () -> kp(val))
78
+ f(key, val) for key, val of bindings
79
+ $(document).bind('keydown', 'c', () -> cycle_colors())
80
+
81
+ kp('status')
@@ -0,0 +1,136 @@
1
+ // Generated by CoffeeScript 1.3.3
2
+ (function() {
3
+ var $, COLOR_SCHEMES, CONN_HEADERS, bindings, color_scheme_index, connection_row, connection_rows, cycle_colors, f, key, kp, list, list_item, maybe_name, message, val;
4
+
5
+ $ = jQuery;
6
+
7
+ CONN_HEADERS = "<tr>\n <th>Input</th>\n <th>Chan</th>\n <th>Output</th>\n <th>Chan</th>\n <th>Prog</th>\n <th>Zone</th>\n <th>Xpose</th>\n <th>Filter</th>\n</tr>";
8
+
9
+ COLOR_SCHEMES = ['default', 'green', 'amber', 'blue'];
10
+
11
+ color_scheme_index = 0;
12
+
13
+ list_item = function(val, highlighted_value) {
14
+ var classes;
15
+ classes = val === highlighted_value ? "selected reverse-" + COLOR_SCHEMES[color_scheme_index] : '';
16
+ return "<li class=\"" + classes + "\">" + val + "</li>";
17
+ };
18
+
19
+ list = function(id, vals, highlighted_value) {
20
+ var lis, val;
21
+ lis = (function() {
22
+ var _i, _len, _results;
23
+ _results = [];
24
+ for (_i = 0, _len = vals.length; _i < _len; _i++) {
25
+ val = vals[_i];
26
+ _results.push(list_item(val, highlighted_value));
27
+ }
28
+ return _results;
29
+ })();
30
+ return $('#' + id).html(lis.join("\n"));
31
+ };
32
+
33
+ connection_row = function(conn) {
34
+ var key, vals;
35
+ vals = (function() {
36
+ var _i, _len, _ref, _results;
37
+ _ref = ['input', 'input_chan', 'output', 'output_chan', 'pc', 'zone', 'xpose', 'filter'];
38
+ _results = [];
39
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
40
+ key = _ref[_i];
41
+ _results.push(conn[key]);
42
+ }
43
+ return _results;
44
+ })();
45
+ return "<tr><td>" + (vals.join('</td><td>')) + "</td></tr>";
46
+ };
47
+
48
+ connection_rows = function(connections) {
49
+ var conn, rows;
50
+ rows = (function() {
51
+ var _i, _len, _results;
52
+ _results = [];
53
+ for (_i = 0, _len = connections.length; _i < _len; _i++) {
54
+ conn = connections[_i];
55
+ _results.push(connection_row(conn));
56
+ }
57
+ return _results;
58
+ })();
59
+ return $('#patch').html(CONN_HEADERS + "\n" + rows.join("\n"));
60
+ };
61
+
62
+ maybe_name = function(data, key) {
63
+ if (data[key]) {
64
+ return data[key]['name'];
65
+ } else {
66
+ return '';
67
+ }
68
+ };
69
+
70
+ message = function(str) {
71
+ return $('#message').html(str);
72
+ };
73
+
74
+ kp = function(action) {
75
+ return $.getJSON(action, function(data) {
76
+ list('song-lists', data['lists'], data['list']);
77
+ list('songs', data['songs'], maybe_name(data, 'song'));
78
+ list('triggers', data['triggers']);
79
+ if (data['song'] != null) {
80
+ list('song', data['song']['patches'], maybe_name(data, 'patch'));
81
+ if (data['patch'] != null) {
82
+ connection_rows(data['patch']['connections']);
83
+ }
84
+ }
85
+ if (data['message'] != null) {
86
+ return message(data['message']);
87
+ }
88
+ });
89
+ };
90
+
91
+ cycle_colors = function() {
92
+ var base_class, color_scheme;
93
+ base_class = COLOR_SCHEMES[color_scheme_index];
94
+ if (color_scheme_index >= 0) {
95
+ $('body').removeClass(base_class);
96
+ $('.selected, th, td#appname').removeClass("reverse-" + base_class);
97
+ $('tr, td, th').removeClass("" + base_class + "-border");
98
+ }
99
+ color_scheme_index = (color_scheme_index + 1) % COLOR_SCHEMES.length;
100
+ base_class = COLOR_SCHEMES[color_scheme_index];
101
+ $('body').addClass(base_class);
102
+ $('.selected, th, td#appname').addClass("reverse-" + base_class);
103
+ $('tr, td, th').addClass("" + base_class + "-border");
104
+ return color_scheme = base_class;
105
+ };
106
+
107
+ bindings = {
108
+ 'j': 'next_patch',
109
+ 'down': 'next_patch',
110
+ 'k': 'prev_patch',
111
+ 'up': 'prev_patch',
112
+ 'n': 'next_song',
113
+ 'left': 'next_song',
114
+ 'p': 'prev_song',
115
+ 'right': 'prev_song',
116
+ 'esc': 'panic'
117
+ };
118
+
119
+ f = function(key, val) {
120
+ return $(document).bind('keydown', key, function() {
121
+ return kp(val);
122
+ });
123
+ };
124
+
125
+ for (key in bindings) {
126
+ val = bindings[key];
127
+ f(key, val);
128
+ }
129
+
130
+ $(document).bind('keydown', 'c', function() {
131
+ return cycle_colors();
132
+ });
133
+
134
+ kp('status');
135
+
136
+ }).call(this);
@@ -0,0 +1,79 @@
1
+ h1 {
2
+ margin-bottom: 0;
3
+ }
4
+ ul {
5
+ list-style: none;
6
+ padding-left: 0;
7
+ }
8
+ table {
9
+ border-collapse: collapse;
10
+ width: 100%;
11
+ }
12
+ tr, td, th {
13
+ border: 1px solid black;
14
+ vertical-align: top;
15
+ }
16
+ th {
17
+ background-color: black;
18
+ color: white;
19
+ border-right: 1px solid #888;
20
+ }
21
+ td#appname {
22
+ text-align: center;
23
+ width: 100%;
24
+ }
25
+ table h1, table li, table pre {
26
+ padding: 0 0.5em 0 0.5em;
27
+ }
28
+
29
+ /* ================ black on white ================ */
30
+ .default {
31
+ background-color: white;
32
+ color: black;
33
+ }
34
+ .reverse-default {
35
+ background-color: black;
36
+ color: white;
37
+ }
38
+ .default-border {
39
+ border: 1px solid black;
40
+ }
41
+
42
+ /* ================ green on black ================ */
43
+ .green {
44
+ background-color: black;
45
+ color: #0f0;
46
+ }
47
+ .reverse-green {
48
+ background-color: #0f0;
49
+ color: black;
50
+ }
51
+ .green-border {
52
+ border: 1px solid #0f0;
53
+ }
54
+
55
+ /* ================ amber on black ================ */
56
+ .amber {
57
+ background-color: black;
58
+ color: #f80;
59
+ }
60
+ .reverse-amber {
61
+ background-color: #f80;
62
+ color: black;
63
+ }
64
+ .amber-border {
65
+ border: 1px solid #f80;
66
+ }
67
+
68
+ /* ================ white on blue ================ */
69
+ .blue {
70
+ background-color: blue;
71
+ color: white;
72
+ }
73
+ .reverse-blue {
74
+ background-color: white;
75
+ color: blue;
76
+ }
77
+ .blue-border {
78
+ border: 1px solid white;
79
+ }
@@ -0,0 +1,130 @@
1
+ require 'sinatra'
2
+ require 'sinatra/json'
3
+ require 'singleton'
4
+
5
+ # ================================================================
6
+ # Settings
7
+ # ================================================================
8
+
9
+ set :run, true
10
+ set :root, File.dirname(__FILE__)
11
+
12
+ # ================================================================
13
+ # Helper methods
14
+ # ================================================================
15
+
16
+ def pm
17
+ @pm ||= PM::SinatraApp.instance.pm
18
+ end
19
+
20
+ def return_status(opts = nil)
21
+ pm = pm()
22
+ status = {
23
+ :lists => pm.song_lists.map(&:name),
24
+ :list => pm.song_list.name,
25
+ :songs => pm.song_list.songs.map(&:name),
26
+ :triggers => pm.inputs.collect do |instrument|
27
+ instrument.triggers.collect { |trigger| ":#{instrument.sym} #{trigger.to_s}" }
28
+ end.flatten
29
+ }
30
+ if pm.song
31
+ status[:song] = {
32
+ :name => pm.song.name,
33
+ :patches => pm.song.patches.map(&:name)
34
+ }
35
+ if pm.patch
36
+ status[:patch] = {
37
+ :name => pm.patch.name,
38
+ :connections => pm.patch.connections.collect do |conn|
39
+ {
40
+ :input => conn.input.name,
41
+ :input_chan => conn.input_chan ? conn.input_chan + 1 : 'all',
42
+ :output => conn.output.name,
43
+ :output_chan => conn.output_chan + 1,
44
+ :pc => conn.pc_prog.to_s,
45
+ :zone => conn.zone ? [conn.note_num_to_name(conn.zone.begin),
46
+ conn.note_num_to_name(conn.zone.end)] : '',
47
+ :xpose => conn.xpose.to_s,
48
+ :filter => conn.filter.to_s
49
+ }
50
+ end
51
+ }
52
+ end
53
+ end
54
+ status.merge(opts) if opts
55
+
56
+ json status
57
+ end
58
+
59
+ # ================================================================
60
+ # URL handlers
61
+ # ================================================================
62
+
63
+ not_found do
64
+ path = request.env['REQUEST_PATH']
65
+ unless path == '/favicon.ico'
66
+ $stderr.puts "error: not_found called, request = #{request.inspect}" # DEBUG
67
+ return_status(:message => "No such URL: #{path}")
68
+ end
69
+ end
70
+
71
+ get '/' do
72
+ redirect '/index.html'
73
+ end
74
+
75
+ get '/status' do
76
+ return_status
77
+ end
78
+
79
+ get '/next_patch' do
80
+ pm.next_patch
81
+ return_status
82
+ end
83
+
84
+ get '/prev_patch' do
85
+ pm.prev_patch
86
+ return_status
87
+ end
88
+
89
+ get '/next_song' do
90
+ pm.next_song
91
+ return_status
92
+ end
93
+
94
+ get '/prev_song' do
95
+ pm.prev_song
96
+ return_status
97
+ end
98
+
99
+ get '/panic' do
100
+ # TODO when panic called twice in a row, call panic(true)
101
+ pm.panic
102
+ return_status
103
+ end
104
+
105
+ # ================================================================
106
+ # GUI class: run method
107
+ # ================================================================
108
+
109
+ module PM
110
+
111
+ class SinatraApp
112
+
113
+ include Singleton
114
+
115
+ attr_accessor :port
116
+ attr_reader :pm
117
+
118
+ def initialize
119
+ @pm = PM::PatchMaster.instance
120
+ end
121
+
122
+ def run
123
+ set(:port, @port) if @port
124
+ @pm.start
125
+ ensure
126
+ @pm.stop
127
+ @pm.close_debug_file
128
+ end
129
+ end
130
+ end