module Flammarion
module Writeable
attr_reader :front_end
class DeferredValue < Delegator
def initialize
super @value
end
def __setobj__(value)
@value = value
end
def value
@value
end
def __getobj__
@value
end
def inspect
"#R#{@value.inspect}"
end
def checked?
return @value
end
end
class Spectrum
attr_reader :front_end
def initialize(id, target, front_end)
@id = id
@target = target
@front_end = front_end
end
def plot(data, options = {})
@front_end.send_json({action:'plot', id:@id, target:@target, data:data}.merge(options))
end
end
def send_json(hash)
@front_end.send_json({target: @pane_name}.merge(hash))
end
def send(str, options = {})
@front_end.send_json({action:'append', text:str, target:@pane_name}.merge(options))
end
alias_method :print, :send
def puts(str = "", options = {})
send str, options
send "\n"
return nil
end
def replace(str, options = {})
send_json({action:'replace', text:str}.merge(options))
return nil
end
def clear
send_json({action:'clear'})
return nil
end
def close
send_json({action:'closepane'})
end
def plot(values, options = {})
id = @front_end.make_id
send_json({action:'plot', data:values, id:id}.merge(options))
return Spectrum.new(id, @pane_name, @front_end)
end
def highlight(text, options = {})
output = text
output = JSON.pretty_generate(text) if text.is_a? Hash or text.is_a? Array
send_json({action:'highlight', text:output}.merge(options))
nil
end
def button(label, options = {}, &block)
id = @front_end.make_id
send_json({action:'button', label:label, id:id}.merge(options))
@front_end.callbacks[id] = block
id
end
def embedded_button(label, options = {}, &block)
id = @front_end.make_id
@front_end.callbacks[id] = block
%|#{label}|
end
def callback_link(label, options = {}, &block)
id = @front_end.make_id
@front_end.callbacks[id] = block
%|#{label}|
end
def icon(name, additional_classes = [])
%||
end
def input(label, options = {}, &block)
id = @front_end.make_id
send_json({action:'input', label:label, id:id}.merge(options))
if block_given?
@front_end.callbacks[id] = block
else
d = DeferredValue.new
@front_end.callbacks[id] = Proc.new {|v| d.__setobj__ v["text"] }
return d
end
end
def checkbox(label, options = {}, &block)
id = @front_end.make_id
send_json({action:'checkbox', label:label, id:id}.merge(options))
if block_given?
@front_end.callbacks[id] = block
else
d = DeferredValue.new
d.__setobj__(options[:value] || options['value'])
@front_end.callbacks[id] = Proc.new {|v| d.__setobj__(v["checked"])}
return d
end
end
def break(options = {})
send_json({action:'break'}.merge(options))
end
def html(data)
send_json({action:'replace', text:data, raw:true})
end
def script(coffee, options = {})
data = options.fetch(:coffee, true) ? CoffeeScript.compile(coffee) : coffee
send_json({action:'script', data:data}.merge(options))
end
def style(attribute, value)
send_json({action: 'style', attribute: attribute, value: value})
end
def template(file)
data = Slim::Template.new(file).render
send_json({action:'replace', text:data, raw:true})
end
def live_reload_template(file)
FileWatcher.new(file).watch {|file| template(file) }
end
def markdown(text, options = {})
markdown_html = Redcarpet::Markdown.new(Redcarpet::Render::HTML, {
tables: true,
fenced_code_blocks: true,
autolink: true,
strikethrough: true,
superscript: true,
}.merge(options[:markdown_extensions] || {})).render(text)
send_json({action:'markdown', text: markdown_html}.merge(options))
end
def hide
send_json({action:'hidepane'})
end
def show
send_json({action:'showpane'})
end
def subpane(name)
send_json({action:'subpane', name:name})
return Pane.new(@front_end, name)
end
def pane(name)
send_json({action:'addpane', name:name})
return Pane.new(@front_end, name)
end
def orientation=(orientation)
raise ArgumentError.new("Orientation must be :horizontal or :vertical") unless [:horizontal, :vertical].include?(orientation)
send_json({action:'reorient', orientation:orientation})
end
def button_box(name)
send_json({action:'buttonbox', name:name})
return Pane.new(@front_end, name)
end
def status(str, position = :right)
@front_end.send_json({action:'status', text: str, position:position})
end
def table(rows, options = {})
send_json({action:'table', rows: rows}.merge(options))
end
def gets(prompt = "", options = {})
str = nil
input(prompt, {once:true, focus:true}.merge(options)) {|msg| str = msg["text"]}
sleep 0.1 while str.nil?
return str
end
def map(*args)
case (args.size)
when 1
if args[0].respond_to? :keys then
options = args[0]
else
options = {address:args[0].to_s}
end
when 2
if args[1].respond_to? :keys then
options = {address:args[0]}.merge(args[1])
else
options = {latitude:args[0], longitude:args[1]}
end
when 3
options = {latitude:args[0], longitude:args[1]}.merge(args[2])
else
raise ArgumentError.new("Expected 1..3 arguments")
end
send_json({action:'map'}.merge(options))
end
end
end