flammarion 0.0.1
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.
- 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
|