flammarion 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +19 -0
- data/Readme.md +74 -0
- data/lib/flammarion.rb +155 -0
- data/lib/flammarion/pane.rb +12 -0
- data/lib/flammarion/server.rb +103 -0
- data/lib/flammarion/version.rb +3 -0
- data/lib/flammarion/writeable.rb +202 -0
- data/lib/html/Gemfile +19 -0
- data/lib/html/Gemfile.lock +151 -0
- data/lib/html/build/images/layers-2x.png +0 -0
- data/lib/html/build/images/layers.png +0 -0
- data/lib/html/build/images/marker-icon-2x.png +0 -0
- data/lib/html/build/images/marker-icon.png +0 -0
- data/lib/html/build/images/marker-shadow.png +0 -0
- data/lib/html/build/index.html +1 -0
- data/lib/html/build/javascripts/actions.js +415 -0
- data/lib/html/build/javascripts/all.js +7401 -0
- data/lib/html/build/javascripts/map.js +222 -0
- data/lib/html/build/javascripts/plot.js +288 -0
- data/lib/html/build/javascripts/querystring.js +39 -0
- data/lib/html/build/javascripts/status.js +37 -0
- data/lib/html/build/javascripts/vendor/ansi_up.js +327 -0
- data/lib/html/build/javascripts/vendor/highlight.pack.js +1 -0
- data/lib/html/build/javascripts/vendor/jquery.js +5 -0
- data/lib/html/build/javascripts/vendor/jquery.transit.min.js +1 -0
- data/lib/html/build/javascripts/vendor/l.control.geosearch.js +253 -0
- data/lib/html/build/javascripts/vendor/l.geosearch.provider.openstreetmap.js +297 -0
- data/lib/html/build/javascripts/vendor/leaflet.js +10 -0
- data/lib/html/build/javascripts/vendor/term.js +5974 -0
- data/lib/html/build/javascripts/websocket.js +166 -0
- data/lib/html/build/spectrum_test.html +12 -0
- data/lib/html/build/stylesheets/all.css +992 -0
- data/lib/html/build/stylesheets/buttons.css +179 -0
- data/lib/html/build/stylesheets/colors.css +0 -0
- data/lib/html/build/stylesheets/frontend.css +112 -0
- data/lib/html/build/stylesheets/leaflet.css +479 -0
- data/lib/html/build/stylesheets/map.css +0 -0
- data/lib/html/build/stylesheets/normalize.bak +375 -0
- data/lib/html/build/stylesheets/railscasts.css +185 -0
- data/lib/html/build/stylesheets/scrollbar.css +14 -0
- data/lib/html/build/stylesheets/table.css +21 -0
- data/lib/html/build/term_test.html +28 -0
- data/lib/html/config.rb +72 -0
- data/lib/html/source/images/layers-2x.png +0 -0
- data/lib/html/source/images/layers.png +0 -0
- data/lib/html/source/images/marker-icon-2x.png +0 -0
- data/lib/html/source/images/marker-icon.png +0 -0
- data/lib/html/source/images/marker-shadow.png +0 -0
- data/lib/html/source/index.html.slim +31 -0
- data/lib/html/source/javascripts/actions.coffee +192 -0
- data/lib/html/source/javascripts/all.js +3 -0
- data/lib/html/source/javascripts/map.coffee +43 -0
- data/lib/html/source/javascripts/plot.coffee +233 -0
- data/lib/html/source/javascripts/querystring.coffee +12 -0
- data/lib/html/source/javascripts/status.coffee +17 -0
- data/lib/html/source/javascripts/vendor/ansi_up.js +348 -0
- data/lib/html/source/javascripts/vendor/highlight.pack.js +1 -0
- data/lib/html/source/javascripts/vendor/jquery.js +4 -0
- data/lib/html/source/javascripts/vendor/jquery.transit.min.js +1 -0
- data/lib/html/source/javascripts/vendor/l.control.geosearch.js +242 -0
- data/lib/html/source/javascripts/vendor/l.geosearch.provider.openstreetmap.js +43 -0
- data/lib/html/source/javascripts/vendor/leaflet.js +9 -0
- data/lib/html/source/javascripts/vendor/term.js +5973 -0
- data/lib/html/source/javascripts/websocket.coffee +83 -0
- data/lib/html/source/layouts/layout.erb +0 -0
- data/lib/html/source/spectrum_test.html.slim +39 -0
- data/lib/html/source/stylesheets/all.css +2 -0
- data/lib/html/source/stylesheets/buttons.styl +115 -0
- data/lib/html/source/stylesheets/colors.styl +18 -0
- data/lib/html/source/stylesheets/frontend.styl +109 -0
- data/lib/html/source/stylesheets/leaflet.css +478 -0
- data/lib/html/source/stylesheets/map.styl +0 -0
- data/lib/html/source/stylesheets/normalize.bak +375 -0
- data/lib/html/source/stylesheets/railscasts.css +184 -0
- data/lib/html/source/stylesheets/scrollbar.styl +18 -0
- data/lib/html/source/stylesheets/table.styl +28 -0
- data/lib/html/source/term_test.html.slim +50 -0
- metadata +250 -0
data/LICENSE
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2015 Zachary Capalbo
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
THE SOFTWARE.
|
data/Readme.md
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
# Flammarion GUI Toolkit
|
2
|
+
|
3
|
+
## Overview
|
4
|
+
|
5
|
+
Flammarion is an easy-to-use library for displaying information that you might
|
6
|
+
normally display to the command line in a slightly easier-to-access way.
|
7
|
+
|
8
|
+
It is not intended to be a full fledged application development toolkit. It is
|
9
|
+
intended instead for small scripts where you just want to show some information
|
10
|
+
without going through too much trouble.
|
11
|
+
|
12
|
+
## Documentation
|
13
|
+
|
14
|
+
The easiest way to use Flammarion, is similar to how you might use STDOUT:
|
15
|
+
|
16
|
+
```ruby
|
17
|
+
require 'flammarion'
|
18
|
+
f = Flammarion::Engraving.new
|
19
|
+
f.puts "Hello World!"
|
20
|
+
```
|
21
|
+
|
22
|
+
It can even support standard console color codes: (Thanks to [ansi_up](http://github.com/drudru/ansi_up)!)
|
23
|
+
|
24
|
+
```ruby
|
25
|
+
require 'colorized'
|
26
|
+
f.puts "This line will be red!".red
|
27
|
+
f.puts "This #{"word".colorize(:green)} will not be blue."
|
28
|
+
```
|
29
|
+
|
30
|
+
However, you can also do more advanced things. Say you want to show a table. Easy!
|
31
|
+
|
32
|
+
```ruby
|
33
|
+
f.table(
|
34
|
+
[%w[Number Squared Sqrt].map{|h| h.light_magenta}] + # Make the header a different color
|
35
|
+
10.times.collect{|x| [x, x * x, Math.sqrt(x)]})
|
36
|
+
```
|
37
|
+
|
38
|
+
Or maybe you want to know where something is:
|
39
|
+
|
40
|
+
```ruby
|
41
|
+
f.map("Boston, MA")
|
42
|
+
```
|
43
|
+
|
44
|
+
Maybe you even want to see both of those things *at the same time*!
|
45
|
+
|
46
|
+
```ruby
|
47
|
+
f.pane("numberstuff").table([%w[Number Squared Sqrt].map{|h| h.light_magenta}] +
|
48
|
+
10.times.collect{|x| [x, x * x, Math.sqrt(x)]})
|
49
|
+
f.pane("mapstuff").map("Big Ben")
|
50
|
+
```
|
51
|
+
|
52
|
+
If you need feedback, there's a simple callback mechanism for buttons and text
|
53
|
+
boxes:
|
54
|
+
|
55
|
+
```ruby
|
56
|
+
f.button("Click Here!!!") {f.puts "You clicked the button!"}
|
57
|
+
f.input("Placeholder > ") {|msg| f.puts "You wrote: #{msg['text'].light_magenta}"}
|
58
|
+
```
|
59
|
+
|
60
|
+
There's lots more, too. I'll write more documentation eventually.
|
61
|
+
|
62
|
+
## Installation
|
63
|
+
|
64
|
+
First you need to install [Google Chrome](https://www.google.com/chrome/browser/desktop/index.html)
|
65
|
+
and make sure it's in your path. Then you can install the gem:
|
66
|
+
|
67
|
+
`gem install flammarion`
|
68
|
+
|
69
|
+
or add it to your Gemfile.
|
70
|
+
|
71
|
+
## Behind the scenes
|
72
|
+
|
73
|
+
Flammarion uses Chrome to display a simple html page, and WebSockets to communicate
|
74
|
+
between the Javascript and ruby.
|
data/lib/flammarion.rb
ADDED
@@ -0,0 +1,155 @@
|
|
1
|
+
require 'monitor'
|
2
|
+
require 'open3'
|
3
|
+
require 'ostruct'
|
4
|
+
require 'em-websocket'
|
5
|
+
require 'json'
|
6
|
+
require 'slim'
|
7
|
+
require 'coffee-script'
|
8
|
+
require 'sass'
|
9
|
+
require 'colorize'
|
10
|
+
require 'filewatcher'
|
11
|
+
|
12
|
+
require_relative 'flammarion/writeable.rb'
|
13
|
+
require_relative 'flammarion/pane.rb'
|
14
|
+
require_relative 'flammarion/server.rb'
|
15
|
+
|
16
|
+
module Flammarion
|
17
|
+
class Engraving
|
18
|
+
attr_reader :chrome
|
19
|
+
attr_accessor :callbacks, :sockets, :on_disconnect, :on_connect, :actions
|
20
|
+
include Writeable
|
21
|
+
def initialize(options = {})
|
22
|
+
@chrome = OpenStruct.new
|
23
|
+
@sockets = []
|
24
|
+
@actions = {}
|
25
|
+
@front_end = self
|
26
|
+
@pane_name = "default"
|
27
|
+
start_server
|
28
|
+
@window_id = @@server.register_window(self)
|
29
|
+
open_a_window unless options[:no_chrome]
|
30
|
+
@callbacks = {}
|
31
|
+
@exit_on_disconnect = options.fetch(:exit_on_disconnect, false)
|
32
|
+
wait_for_a_connection unless options[:no_wait]
|
33
|
+
@on_disconnect = options[:on_disconnect]
|
34
|
+
@ignore_old = options.fetch(:ignore_old, false)
|
35
|
+
@on_connect = options[:on_connect]
|
36
|
+
end
|
37
|
+
|
38
|
+
def disconnect(ws)
|
39
|
+
@sockets.delete ws
|
40
|
+
exit 0 if @exit_on_disconnect
|
41
|
+
@on_disconnect.call if @on_disconnect
|
42
|
+
end
|
43
|
+
|
44
|
+
def process_message(msg)
|
45
|
+
@last_msg = msg
|
46
|
+
m = {}
|
47
|
+
begin
|
48
|
+
m = JSON.parse(msg)
|
49
|
+
rescue JSON::ParserError
|
50
|
+
log "Invalid JSON String"
|
51
|
+
return
|
52
|
+
end
|
53
|
+
|
54
|
+
case m["action"]
|
55
|
+
when 'callback'
|
56
|
+
callback = @callbacks[m['id']]
|
57
|
+
unless callback.nil?
|
58
|
+
if callback.arity == 1
|
59
|
+
callback.call(m)
|
60
|
+
else
|
61
|
+
callback.call
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
@actions[m["action"]].call(m) if @actions.include?(m["action"])
|
66
|
+
end
|
67
|
+
|
68
|
+
def make_id
|
69
|
+
@id ||= 0
|
70
|
+
@id += 1
|
71
|
+
"i#{@id}"
|
72
|
+
end
|
73
|
+
|
74
|
+
def start_server
|
75
|
+
@@server ||= Server.new
|
76
|
+
end
|
77
|
+
|
78
|
+
def wait_for_a_connection
|
79
|
+
sleep 0.5 while @sockets.empty?
|
80
|
+
end
|
81
|
+
|
82
|
+
CHROME_PATH = 'C:\Program Files (x86)\Google\Chrome\Application\chrome.exe'
|
83
|
+
def open_a_window_on_windows
|
84
|
+
resource = %[file\://#{File.absolute_path(File.dirname(__FILE__))}/html/build/index.html]
|
85
|
+
spawn(CHROME_PATH, %[--app=#{resource}?path=#{@window_id}&port=#{@@server.port}])
|
86
|
+
end
|
87
|
+
|
88
|
+
def open_a_window
|
89
|
+
return open_a_window_on_windows if Gem.win_platform?
|
90
|
+
developmentMode = system("lsof -i:#{4567}", out: '/dev/null')
|
91
|
+
host = "file://#{File.dirname(File.absolute_path(__FILE__))}/html/build/index.html"
|
92
|
+
host = "http://localhost:4567/" if developmentMode
|
93
|
+
|
94
|
+
# data_dir = Dir.mktmpdir("flammarion")
|
95
|
+
# File.open("#{data_dir}/First\ Run", "w") {}
|
96
|
+
|
97
|
+
@expect_title = "Flammarion-#{rand.to_s[2..-1]}"
|
98
|
+
|
99
|
+
%w[google-chrome google-chrome-stable chromium chromium-browser chrome C:\Program\ Files\ (x86)\Google\Chrome\Application\chrome.exe].each do |executable|
|
100
|
+
@chrome.in, @chrome.out, @chrome.err, @chrome.thread = Open3.popen3("#{executable} --app='#{host}?path=#{@window_id}&port=#{@@server.port}&title=#{@expect_title}'")
|
101
|
+
break if @chrome.in
|
102
|
+
end
|
103
|
+
|
104
|
+
raise StandardError.new("Cannot launch any browser") unless @chrome.in
|
105
|
+
end
|
106
|
+
|
107
|
+
def window_open?
|
108
|
+
not @sockets.empty?
|
109
|
+
end
|
110
|
+
|
111
|
+
def send_json(val)
|
112
|
+
if @sockets.empty? then
|
113
|
+
open_a_window
|
114
|
+
wait_for_a_connection
|
115
|
+
end
|
116
|
+
@sockets.each{|ws| ws.send val.to_json}
|
117
|
+
nil
|
118
|
+
end
|
119
|
+
|
120
|
+
def pane(name)
|
121
|
+
return Pane.new(self, name)
|
122
|
+
end
|
123
|
+
|
124
|
+
def alert(text)
|
125
|
+
send_json(action:'alert', text:text)
|
126
|
+
end
|
127
|
+
|
128
|
+
def orientation=(orientation)
|
129
|
+
raise ArgumentError.new("Orientation must be :horizontal or :vertical") unless [:horizontal, :vertical].include?(orientation)
|
130
|
+
send_json({action:'reorient', orientation:orientation})
|
131
|
+
end
|
132
|
+
|
133
|
+
def title(str)
|
134
|
+
send_json({action:'title', title:str})
|
135
|
+
end
|
136
|
+
|
137
|
+
def layout(file)
|
138
|
+
data = Slim::Template.new(file).render
|
139
|
+
send_json({action:'layout', data:data})
|
140
|
+
end
|
141
|
+
|
142
|
+
def live_reload_layout(file)
|
143
|
+
layout(file); yield if block_given?
|
144
|
+
FileWatcher.new(file).watch {|file| layout(file); yield if block_given? }
|
145
|
+
end
|
146
|
+
|
147
|
+
def get_save_path
|
148
|
+
if Gem.win_platform?
|
149
|
+
`powershell "Add-Type -AssemblyName System.windows.forms|Out-Null;$f=New-Object System.Windows.Forms.SaveFileDialog;$f.InitialDirectory='%cd%';$f.Filter='All Files (*.*)|*.*';$f.showHelp=$true;$f.ShowDialog()|Out-Null;$f.FileName"`.strip
|
150
|
+
else
|
151
|
+
`zenity --file-selection --save --confirm-overwrite`.strip
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
module Flammarion
|
2
|
+
class Server
|
3
|
+
attr_reader :port
|
4
|
+
def initialize
|
5
|
+
@windows = {}
|
6
|
+
@socket_paths = {}
|
7
|
+
@started = false
|
8
|
+
Thread.new do
|
9
|
+
begin
|
10
|
+
start_server_internal
|
11
|
+
rescue SystemExit
|
12
|
+
raise
|
13
|
+
rescue Exception => e
|
14
|
+
if binding.respond_to? :pry
|
15
|
+
binding.pry
|
16
|
+
else
|
17
|
+
raise
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
sleep 0.5 while not @started
|
22
|
+
end
|
23
|
+
def start_server_internal
|
24
|
+
EM.run {
|
25
|
+
@port = 7870
|
26
|
+
@port = rand(65000 - 1024) + 1024 if Gem.win_platform?
|
27
|
+
begin
|
28
|
+
EM::WebSocket.run(:host => "0.0.0.0", :port => @port) do |ws|
|
29
|
+
ws.onopen { |handshake|
|
30
|
+
log "WebSocket connection open"
|
31
|
+
if @windows.include?(handshake.path)
|
32
|
+
@windows[handshake.path].sockets << ws
|
33
|
+
@windows[handshake.path].on_connect.call() if @windows[handshake.path].on_connect
|
34
|
+
@socket_paths[ws] = handshake.path
|
35
|
+
else
|
36
|
+
log "No such window: #{handshake.path}"
|
37
|
+
end
|
38
|
+
}
|
39
|
+
|
40
|
+
ws.onclose do
|
41
|
+
log "Connection closed";
|
42
|
+
@windows[@socket_paths[ws]].disconnect(ws) if @windows[@socket_paths[ws]]
|
43
|
+
end
|
44
|
+
|
45
|
+
ws.onmessage { |msg|
|
46
|
+
Thread.new do
|
47
|
+
begin
|
48
|
+
@windows[@socket_paths[ws]].process_message(msg)
|
49
|
+
rescue Exception => e
|
50
|
+
Kernel.puts "#{e.message.to_s.red}\n#{e.backtrace.join("\n").light_red}"
|
51
|
+
if binding.respond_to? :pry
|
52
|
+
binding.pry
|
53
|
+
else
|
54
|
+
raise
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
}
|
59
|
+
|
60
|
+
ws.onerror { |err|
|
61
|
+
if binding.respond_to? :pry
|
62
|
+
binding.pry
|
63
|
+
else
|
64
|
+
raise
|
65
|
+
end
|
66
|
+
}
|
67
|
+
end
|
68
|
+
rescue RuntimeError => e
|
69
|
+
if e.message == "no acceptor (port is in use or requires root privileges)"
|
70
|
+
@port = rand(65000 - 1024) + 1024
|
71
|
+
Kernel.puts "New port: #{port}"
|
72
|
+
retry
|
73
|
+
else
|
74
|
+
raise
|
75
|
+
end
|
76
|
+
end
|
77
|
+
@started = true
|
78
|
+
}
|
79
|
+
rescue Exception => e
|
80
|
+
unless e.is_a? SystemExit
|
81
|
+
Kernel.puts "Error in server:"
|
82
|
+
binding.pry if binding.respond_to? :pry
|
83
|
+
Kernel.puts e.message
|
84
|
+
Kernel.puts e.backtrace.inspect
|
85
|
+
end
|
86
|
+
raise
|
87
|
+
end
|
88
|
+
|
89
|
+
def stop
|
90
|
+
end
|
91
|
+
|
92
|
+
def log(str)
|
93
|
+
# Kernel.puts str
|
94
|
+
end
|
95
|
+
|
96
|
+
def register_window(window)
|
97
|
+
@new_path ||= 0
|
98
|
+
@new_path += 1
|
99
|
+
@windows["/w#{@new_path}"] = window
|
100
|
+
return "w#{@new_path}"
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,202 @@
|
|
1
|
+
module Flammarion
|
2
|
+
module Writeable
|
3
|
+
attr_reader :front_end
|
4
|
+
class DeferredValue < Delegator
|
5
|
+
def initialize
|
6
|
+
super @value
|
7
|
+
end
|
8
|
+
def __setobj__(value)
|
9
|
+
@value = value
|
10
|
+
end
|
11
|
+
|
12
|
+
def value
|
13
|
+
@value
|
14
|
+
end
|
15
|
+
|
16
|
+
def __getobj__
|
17
|
+
@value
|
18
|
+
end
|
19
|
+
|
20
|
+
def inspect
|
21
|
+
"#R#{@value.inspect}"
|
22
|
+
end
|
23
|
+
|
24
|
+
def checked?
|
25
|
+
return @value
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class Spectrum
|
30
|
+
attr_reader :front_end
|
31
|
+
def initialize(id, target, front_end)
|
32
|
+
@id = id
|
33
|
+
@target = target
|
34
|
+
@front_end = front_end
|
35
|
+
end
|
36
|
+
|
37
|
+
def plot(data, options = {})
|
38
|
+
@front_end.send_json({action:'plot', id:@id, target:@target, data:data}.merge(options))
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def send_json(hash)
|
43
|
+
@front_end.send_json({target: @pane_name}.merge(hash))
|
44
|
+
end
|
45
|
+
|
46
|
+
def send(str, options = {})
|
47
|
+
@front_end.send_json({action:'append', text:str, target:@pane_name}.merge(options))
|
48
|
+
end
|
49
|
+
alias_method :print, :send
|
50
|
+
|
51
|
+
def puts(str = "", options = {})
|
52
|
+
send str, options
|
53
|
+
send "\n"
|
54
|
+
return nil
|
55
|
+
end
|
56
|
+
|
57
|
+
def replace(str, options = {})
|
58
|
+
send_json({action:'replace', text:str}.merge(options))
|
59
|
+
return nil
|
60
|
+
end
|
61
|
+
|
62
|
+
def clear
|
63
|
+
send_json({action:'clear'})
|
64
|
+
return nil
|
65
|
+
end
|
66
|
+
|
67
|
+
def close
|
68
|
+
send_json({action:'closepane'})
|
69
|
+
end
|
70
|
+
|
71
|
+
def plot(values, options = {})
|
72
|
+
id = @front_end.make_id
|
73
|
+
send_json({action:'plot', data:values, id:id}.merge(options))
|
74
|
+
return Spectrum.new(id, @pane_name, @front_end)
|
75
|
+
end
|
76
|
+
|
77
|
+
def highlight(text, options = {})
|
78
|
+
output = text
|
79
|
+
output = JSON.pretty_generate(text) if text.is_a? Hash or text.is_a? Array
|
80
|
+
send_json({action:'highlight', text:output}.merge(options))
|
81
|
+
nil
|
82
|
+
end
|
83
|
+
|
84
|
+
def button(label, options = {}, &block)
|
85
|
+
id = @front_end.make_id
|
86
|
+
send_json({action:'button', label:label, id:id}.merge(options))
|
87
|
+
@front_end.callbacks[id] = block
|
88
|
+
end
|
89
|
+
|
90
|
+
def input(label, options = {}, &block)
|
91
|
+
id = @front_end.make_id
|
92
|
+
send_json({action:'input', label:label, id:id}.merge(options))
|
93
|
+
if block_given?
|
94
|
+
@front_end.callbacks[id] = block
|
95
|
+
else
|
96
|
+
d = DeferredValue.new
|
97
|
+
@front_end.callbacks[id] = Proc.new {|v| d.__setobj__ v["text"] }
|
98
|
+
return d
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def checkbox(label, options = {}, &block)
|
103
|
+
id = @front_end.make_id
|
104
|
+
send_json({action:'checkbox', label:label, id:id}.merge(options))
|
105
|
+
if block_given?
|
106
|
+
@front_end.callbacks[id] = block
|
107
|
+
else
|
108
|
+
d = DeferredValue.new
|
109
|
+
d.__setobj__(options[:value] || options['value'])
|
110
|
+
@front_end.callbacks[id] = Proc.new {|v| d.__setobj__(v["checked"])}
|
111
|
+
return d
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def break(options = {})
|
116
|
+
send_json({action:'break'}.merge(options))
|
117
|
+
end
|
118
|
+
|
119
|
+
def html(data)
|
120
|
+
send_json({action:'replace', text:data, raw:true})
|
121
|
+
end
|
122
|
+
|
123
|
+
def script(coffee)
|
124
|
+
data = CoffeeScript.compile(coffee)
|
125
|
+
send_json({action:'script', data:data})
|
126
|
+
end
|
127
|
+
|
128
|
+
def template(file)
|
129
|
+
data = Slim::Template.new(file).render
|
130
|
+
send_json({action:'replace', text:data, raw:true})
|
131
|
+
end
|
132
|
+
|
133
|
+
def live_reload_template(file)
|
134
|
+
FileWatcher.new(file).watch {|file| template(file) }
|
135
|
+
end
|
136
|
+
|
137
|
+
def hide
|
138
|
+
send_json({action:'hidepane'})
|
139
|
+
end
|
140
|
+
|
141
|
+
def show
|
142
|
+
send_json({action:'showpane'})
|
143
|
+
end
|
144
|
+
|
145
|
+
def subpane(name)
|
146
|
+
send_json({action:'subpane', name:name})
|
147
|
+
return Pane.new(@front_end, name)
|
148
|
+
end
|
149
|
+
|
150
|
+
def pane(name)
|
151
|
+
send_json({action:'addpane', name:name})
|
152
|
+
return Pane.new(@front_end, name)
|
153
|
+
end
|
154
|
+
|
155
|
+
def orientation=(orientation)
|
156
|
+
raise ArgumentError.new("Orientation must be :horizontal or :vertical") unless [:horizontal, :vertical].include?(orientation)
|
157
|
+
send_json({action:'reorient', orientation:orientation})
|
158
|
+
end
|
159
|
+
|
160
|
+
def button_box(name)
|
161
|
+
send_json({action:'buttonbox', name:name})
|
162
|
+
return Pane.new(@front_end, name)
|
163
|
+
end
|
164
|
+
|
165
|
+
def status(str, position = :right)
|
166
|
+
@front_end.send_json({action:'status', text: str, position:position})
|
167
|
+
end
|
168
|
+
|
169
|
+
def table(rows, options = {})
|
170
|
+
send_json({action:'table', rows: rows}.merge(options))
|
171
|
+
end
|
172
|
+
|
173
|
+
def gets(prompt = "", options = {})
|
174
|
+
str = nil
|
175
|
+
input(prompt, {once:true, focus:true}.merge(options)) {|msg| str = msg["text"]}
|
176
|
+
sleep 0.1 while str.nil?
|
177
|
+
return str
|
178
|
+
end
|
179
|
+
|
180
|
+
def map(*args)
|
181
|
+
case (args.size)
|
182
|
+
when 1
|
183
|
+
if args[0].respond_to? :keys then
|
184
|
+
options = args[0]
|
185
|
+
else
|
186
|
+
options = {address:args[0].to_s}
|
187
|
+
end
|
188
|
+
when 2
|
189
|
+
if args[1].respond_to? :keys then
|
190
|
+
options = {address:args[0]}.merge(args[1])
|
191
|
+
else
|
192
|
+
options = {latitude:args[0], longitude:args[1]}
|
193
|
+
end
|
194
|
+
when 3
|
195
|
+
options = {latitude:args[0], longitude:args[1]}.merge(args[2])
|
196
|
+
else
|
197
|
+
raise ArgumentError.new("Expected 1..3 arguments")
|
198
|
+
end
|
199
|
+
send_json({action:'map'}.merge(options))
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|