## # Borges::HTMLBuilder is a generic class for outputting HTML. class Borges::HtmlBuilder SIMPLE_ELEMENTS = { :bold => :b, :code => :code, :div => :div, :emphasis => :em, :italic => :i, :paragraph => :p, :preformatted => :pre, :strong => :strong, :small => :small, :span => :span, :sub => :sub, :sup => :sup, :table_data => :td, :table_heading => :th, :underline => :u, } HTML_CHARACTERS = {} attr_accessor :document attr_reader :attributes SIMPLE_ELEMENTS.each do |method, element| eval %^ def #{method}(obj = nil, &block) return tag_do(:#{element}, obj) unless obj.nil? return tag_do(:#{element}, block) end ^ end ## # Creates a line break. def break tag(:br) end ## # Close the last open element. def close @document.pop_element end ## # Adds a class attribute that contains +classes+ def css_class(classes) @attributes[:class] = classes end ## # Creates a
that has an id attribute containing +name+. def div_named(name, obj = nil, &block) element_id(name) div(obj, &block) end ## # Creates a
that has an class attribute containing # +classes+. def div_with_class(classes, obj = nil, &block) css_class(classes) div(obj, &block) end ## # Adds an id attribute containing +e_id+ def element_id(e_id) @attributes[:id] = e_id end ## # Turns an +obj+ into an HTML encoded string # # encode "foo > bar" # => "foo > bar" def encode(obj) obj.to_s.gsub(/./) do |char| HTML_CHARACTERS[char[0]] end end ## # Adds +obj+ as an HTML encoded string. def encode_text(obj) text encode(obj) end ## # Creates a
labeled +legend+ def fieldset(legend, &block) tag_do(:fieldset, proc do tag_do(:legend, legend) block.call end) end ## # Creates a tag that goes in the of the document with +name+. def head_tag(name) element = Borges::HtmlElement.new name, @attributes @document.head.add element @attributes = Borges::HtmlAttributes.new return element end ## # Creates a tag that goes in the of the document with # +name+, that contains +child+ in its contents. def head_tag_with(name, child) head_tag(name).add(child) end ## # Creates a level 1 heading # # heading("My Heading") # #

My Heading

def heading(str) heading_level(str, 1) end ## # Creates a heading with level # # heading_level("My Heading", 2) # #

My Heading

def heading_level(obj, level) tag_do("h#{level}", obj) end ## # Adds an
to the document. def horizontal_rule tag(:hr) end ## # Adds an with +src+ as its src attribute. If +alt+ is # specified, it is used as the image's alternate text, # otherwise the alt attribute is left empty. def image(src, alt = "") @attributes[:src] = src @attributes[:alt] = alt tag :img end ## # Add an anchor on an with +src+ as its src attribute. If +alt+ is # specified, it is used as the image's alternate text, # otherwise the alt attribute is left empty. def image_url_anchor(url, src, alt='') url_anchor(url) do image(src, alt) end end ## # Creates a new HtmlBuilder. # # +document+ must have the following interface: # #head # #add_element # #pop_element # #push_element def initialize(document) @attributes = Borges::HtmlAttributes.new @document = document end ## # Creates an of type +input_type+ with an optional # +name+ and +value+. def input(input_type, name = nil, value = nil) @attributes[:name] = name unless name.nil? @attributes[:value] = encode value unless value.nil? @attributes[:type] = input_type tag :input end ## # Builds an unordered list of items from an Enumerable. #text # will be called on each item. # # list ["x", 5, { :x => :y }] # #
  • x
  • 5
  • xy
def list(items) list_do(items) do |x| text(x) end end ## # Builds an unordered list of items from an Enumerable. The # given block will be called on each item. # # See also #list def list_do(items, &block) tag_do(:ul, proc do items.each do |item| tag_do(:li, proc do block.call(item) end) end end) end ## # Adds a new element +name+ to the document with all the # attributes specified for it. def open_tag(name) @document.push_element Borges::HtmlElement.new(name, @attributes) @attributes = Borges::HtmlAttributes.new end ## # Renders +obj+. #-- # FIXME: better comment def render(obj) raise "Rendering nil object" if obj.nil? # XXX catch possible bugs obj.render_on(self) end def set_attribute(attr, val) @attributes[attr] = val end ## # Adds a non-breakin space ( ). def space text(' ') end ## # Adds a link to an external stylesheet with +url+. def style_link(url) @attributes[:rel] = 'stylesheet' @attributes[:type] = 'text/css' @attributes[:href] = url @attributes[:media] = 'all' head_tag(:link) end ## # Creates a element. def table(&block) tag_do(:table, block) end ## # Creates a row of table headings from +headings+ def table_headings(*headings) table_row do headings.each do |heading| table_heading(heading) end end end ## # Creates a table row. def table_row(&block) tag_do(:tr, block) end ## # Creates a table row with a table cell that spans +span+ # columns. #-- # FIXME: This has a funky name def table_row_span(span, &block) table_row do @attributes[:colspan] = span table_data(&block) end end ## # Creates a table row containing a single data cell. def table_row_with(obj = nil, &block) table_row do table_data(obj || block) end end ## # Creates a table row with +label+ in the first cell. def table_row_labeled(label, obj = nil, &block) table_row do css_class(:label) table_data(label) table_data(obj, &block) end end ## # Creates an empty table row. def table_spacer_row table_row do space end end ## # Creates an empty element with +name+ def tag(name) open_tag(name) close end ## # Creates a +name+ element containing +obj+ def tag_do(name, obj) open_tag(name) render(obj) close end ## # Turn an object into a string. It is _not_ recommened to use this method # to dump raw HTML. # # text("hello world") # # hello world def text(obj) @document.add_element(obj.to_s) end ## # Sets the of the document to +str+ def title(str) head_tag_with(:title, str) end ## # Creates an anchor that links to +url+. def url_anchor(url, obj = nil, &block) @attributes[:href] = url return tag_do(:a, obj) unless obj.nil? return tag_do(:a, block) end =begin def anchorWithUrl_title_do(urlString, titleString, &block) attributes.title(titleString) anchorWithUrl_do(urlString, &block) end ## # XXX Same as Hash#update def attributes=(attrs) @attributes ||= Borges::HtmlAttributes.new attrs.each do |key, val| @attributes[key] = val end end def buttonForUrl_withText(urlString, labelString) buttonForUrl_withText_data(urlString, labelString, []) end def buttonForUrl_withText_data(urlString, labelString, assocCollection) formWithMethod_action_do('GET', urlString, proc do assocCollection.each do |each| inputWithType_named_value('hidden', each.key, each.value) end submitButtonWithText(labelString) end) end def doesNotUnderstand(aMessage) argCount = aMessage.arguments.size if argCount == 0 then return tag(aMessage.selector) end if argCount == 1 then return tag_do(aMessage.selector.allButLast, aMessage.argument) end return super.doesNotUnderstand(aMessage) end def formWithAction_do(actionUrl, &block) formWithMethod_action_do('POST', actionUrl, &block) end def formWithMethod_action_do(methodString, actionUrl, &block) attributes.method(methodString) attributes.action(actionUrl) openTag('form') block.call close end ## # HTML4 image must have alt attribute def image_width_height(urlString, width, height) attributeAt_put('width', width) attributeAt_put('height', height) image(urlString) end def image_width_height_altText(urlString, width, height, altString) attributeAt_put('alt', altString) image_width_height(urlString, width, height) end def inputWithType(input_type) inputWithType_named(input_type, nil) end def inputWithType_named(input_type, name) inputWithType_named_value(input_type, name, nil) end def layoutTable(&block) attributes.border(0) attributes.cellspacing(0) attributes.cellpadding(0) table(&block) end def layoutTableOfWidth_do(width, &block) attributes.width(width) layoutTable(&block) end def metaTagNamed_content(nameString, contentString) attributes.name(nameString) attributes.content(contentString) headTag('meta') end def self.on(aDocument) inst = self.new inst.document = aDocument return inst end def scriptWithUrl(urlString) attributes.language('javascript') attributes.src(urlString) headTag('script') end def spanClass_with(aString, anObject) cssClass(aString) span(anObject) end def spanNamed_with(aString, anObject) cssId(aString) span(anObject) end def submitButton inputWithType('submit') end def submitButtonWithText(aString) attributes.value(aString) inputWithType('submit') end def tableRowWith(&block) tableRow do tableData(&block) end end def tableRowWith_with(aBlock, anotherBlock) tableRow do tableData(aBlock) tableData(anotherBlock) end end def tableRowWith_with_with(x, y, z) tableRow do tableData(x) tableData(y) tableData(z) end end def tableRowWithLabel_column_column(anObject, aBlock, anotherBlock) tableRow do cssClass('label') tableData(anObject) tableData(aBlock) tableData(anotherBlock) end end =end ## # HtmlBuilder initialize (0..8).each do |i| HTML_CHARACTERS[i] = "&##{i};" end (9..13).each do |i| HTML_CHARACTERS[i] = i.chr end (14..31).each do |i| HTML_CHARACTERS[i] = "&##{i};" end (32..126).each do |i| HTML_CHARACTERS[i] = i.chr end (127..159).each do |i| HTML_CHARACTERS[i] = "&##{i};" end {'quot' => '"', 'lt' => '<', 'amp' => '&', 'gt' => '>'}.each do |s, c| HTML_CHARACTERS[c[0]] = "&#{s};" end %w(nbsp iexcl cent pound curren yen brvbar sect uml copy ordf laquo not shy reg macr deg plusmn sup2 sup3 acute micro para middot cedil sup1 ordm raquo frac14 frac12 frac34 iquest Agrave Aacute Acirc Atilde Auml Aring AElig Ccedil Egrave Eacute Ecirc Euml Igrave Iacute Icirc Iuml ETH Ntilde Ograve Oacute Ocirc Otilde Ouml times Oslash Ugrave Uacute Ucirc Uuml Yacute THORN szlig agrave aacute acirc atilde auml aring aelig ccedil egrave eacute ecirc euml igrave iacute icirc iuml eth ntilde ograve oacute ocirc otilde ouml divide oslash ugrave uacute ucirc uuml yacute thorn yuml).each_with_index do |s, i| HTML_CHARACTERS[(i + 160)] = "&#{s};" end end