patchmaster 0.0.6 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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