##
# Renderer for HTML with callbacks
class Borges::HtmlRenderer < Borges::HtmlBuilder
attr_accessor :action_url, :callbacks, :rendering_context
##
# Creates a callback that sends +message+ to +object+ with zero
# arguments.
def action_callback(message, object)
proc do object.send message end
end
##
# Creates a callback that sets +attr+ on +object+.
def setter_callback(attr, object)
proc do |val| object.send "#{attr}=", val end
end
##
# Creates a link with +anchor_text+ that executes a block when
# clicked.
def anchor(anchor_text, &block)
open_anchor(&block)
text(anchor_text)
close
end
##
# Creates a link that calls +sym+ on +obj+ when clicked. +sym+
# is used as the title for the link.
def anchor_on(sym, obj)
element_id(sym)
anchor(label_for(sym)) do obj.send(sym) end
end
##
# Creates a menu of two choices with +labels+ (default
# to Yes and No). If +bool+ is true, the first item is the
# default.
def boolean_menu(bool, labels = [:Yes, :No], &block)
select([true, false].zip(labels), bool, &block)
end
##
# Creates a menu that picks the default value by calling +sym+
# on +obj+.
def boolean_menu_on(sym, obj)
element_id(sym)
boolean_menu(obj.send(sym), &setter_callback(sym, obj))
end
##
# Creates a checkbox. The checkbox will be initially checked
# if +checked+ is set to true.
def checkbox(checked = false, &block)
@attributes[:checked] if checked
update_key = value_input(:checkbox, 'true') do |v|
block.call(v != 'false')
end
input(:hidden, update_key, 'false') if checked
end
##
# Creates a default action that gets called when the form is
# submitted.
def default_action(&block)
input(:hidden, @callbacks.register_action_callback(&block))
end
##
# Creates a default action that sends +message+ to +object+.
# +message+ is sent with zero arguments.
def default_action_on(message, object)
callback = action_callback message, object
input :hidden, @callbacks.register_action_callback(&callback)
end
##
# Creates a link to download +object+ with the text of +text+.
# +mime_type+ will default to text/plain.
#
# #to_s will be sent to +object+, and the resulting string will
# be returned to the user.
#
# WARNING! A download link created by this method will remain
# live until Borges is shut down. The space used will not be
# reclaimed. If you are creating many download links, you may
# wish to serve them from outside Borges.
def download_link(object, text, mime_type = nil)
url_anchor url_for_document(object, mime_type), text
end
##
# Creates a file upload field.
#--
# TODO: Document how the file is passed to the block
def file_upload(&block)
input(:file, @callbacks.register_callback(&block))
end
##
# Create a form.
def form(&block)
open_form
block.call
close
end
##
# Creates a link with image from img_url as content. The link
# executes +block+ when executed. Image has alt text of +alt+.
def image_anchor(img_url, alt='', &block)
open_anchor(&block)
image(img_url, alt)
close
end
def initialize(rendering_context)
@rendering_context = rendering_context
super(rendering_context.document)
@action_url = rendering_context.action_url
@callbacks = rendering_context.callbacks
end
##
# Turn +sym+ into a capitalized label.
def label_for(sym)
label = sym.to_s
label.gsub!('_', ' ')
label.gsub!(/\w+/) do |w|
w.capitalize! unless %w(of in at a or to by).include? w
w
end
return label
end
def open_anchor(&block)
@attributes[:href] = url_for(&block)
open_tag(:a)
end
def open_form
@attributes[:method] = 'POST'
@attributes[:action] = @action_url
open_tag(:form)
end
def open_select
@attributes[:name] = @callbacks.register_dispatch_callback
open_tag(:select)
end
##
# Creates an option for a . +label+ sets the label for
# the select. The option will be selected if +selected+ is
# true.
def option(label, selected = false, &callback)
@attributes[:selected] if selected
@attributes[:value] = @callbacks.register_callback(&callback)
open_tag(:option)
text(label)
close
end
##
# Creates a radio button that is part of radio group +group+.
# The radio button will be checked if +checked+ is true.
#
# +group+ must come from a previous #radio_group:
#
# group = r.radio_group
# r.radio_button(group) do ... end
def radio_button(group, checked = false, &block)
@attributes[:checked] if checked
input(:radio, group, @callbacks.register_callback(&block))
end
##
# Creates a default action for a radio group.
def radio_group
return @callbacks.register_dispatch_callback
end
##
# Creates a from +list+.
#
# Calls #each on +list+ and if +list+ yields a single item,
# then #to_s will be called on the item for use as the label.
# If +list+ yields two items, then the second item will be used
# as a label.
#
# If +selected+ matches an item in +list+, that item will be
# selected.
#
# +callback+ will be passed the user-selected item.
def select(list, selected = nil, &callback)
open_select
list.each do |item, label|
option(label || item, item == selected) do
callback.call item
end
end
close
end
##
# Creates a new document containing +css+, and returns a URL
# for the document.
def style(css)
style_link(url_for_document(css, 'text/css'))
end
##
# Creates a submit button with a label of +label+, or 'Submit'
# if not given.
def submit_button(label = 'Submit', &block)
@attributes[:value] = label
if block then
input :submit, @callbacks.register_action_callback(&block)
else
input :submit
end
end
##
# Creates a submit button with a label of +sym+ that calls
# +sym+ on +obj+ when clicked.
def submit_button_on(sym, obj)
element_id(sym)
submit_button(label_for(sym)) do
obj.send(sym)
end
end
##
# Creates a textarea with +value+ as its contents.
def text_area(value = "", &block)
callback = nil
if value.nil? then
callback = proc do |v|
block.call(v == '' ? nil : v)
end
else
callback = block
end
@attributes[:name] = @callbacks.register_callback(&callback)
open_tag :textarea
encode_text value
close
end
##
# Creates a