#!/usr/bin/env ruby #-- # Copyright 2004, 2005 by Jim Weirich (jim@weirichhouse.org). # Copyright 2005 by Scott Barron (scott@elitists.net). # All rights reserved. # # Permission is granted for use, copying, modification, distribution, # and distribution of modified versions of this work as long as the # above copyright notice is included. # # Much of this is taken from Jim's work in xmlbase.rb and xmlmarkup.rb. # Documentation has also been copied and pasted and modified to reflect # that we're building CSS here instead of XML. Jim is conducting the # orchestra here and I'm just off in the corner playing a flute. #++ # Provide a flexible and easy to use Builder for creating Cascading # Style Sheets (CSS). require 'builder/blankslate' module Builder # Create a Cascading Style Sheet (CSS) using Ruby. # # Example usage: # # css = Builder::CSS.new # # text_color = '#7F7F7F' # preferred_fonts = 'Helvetica, Arial, sans_serif' # # css.comment! 'This is our stylesheet' # css.body { # background_color '#FAFAFA' # font_size 'small' # font_family preferred_fonts # color text_color # } # # css.id!('navbar') { # width '500px' # } # # css.class!('navitem') { # color 'red' # } # # css.a :hover { # text_decoration 'underline' # } # # css.div(:id => 'menu') { # background 'green' # } # # css.div(:class => 'foo') { # background 'red' # } # # This will yield the following stylesheet: # # /* This is our stylesheet */ # body { # background_color: #FAFAFA; # font_size: small; # font_family: Helvetica, Arial, sans_serif; # color: #7F7F7F; # } # # #navbar { # width: 500px; # } # # .navitem { # color: red; # } # # a:hover { # text_decoration: underline; # } # # div#menu { # background: green; # } # # div.foo { # background: red; # } # class CSS < BlankSlate # Create a CSS builder. # # out:: Object receiving the markup.1 +out+ must respond to # <<. # indent:: Number of spaces used for indentation (0 implies no # indentation and no line breaks). # def initialize(indent=2) @indent = indent @target = [] @parts = [] @library = {} end def +(part) _join_with_op! '+' self end def >>(part) _join_with_op! '' self end def >(part) _join_with_op! '>' self end def |(part) _join_with_op! ',' self end # Return the target of the builder def target! @target * '' end # Create a comment string in the output. def comment!(comment_text) @target << "/* #{comment_text} */\n" end def id!(arg, &block) _start_container('#'+arg.to_s, nil, block_given?) _css_block(block) if block _unify_block self end def class!(arg, &block) _start_container('.'+arg.to_s, nil, block_given?) _css_block(block) if block _unify_block self end def store!(sym, &block) @library[sym] = block.to_proc end def group!(*args, &block) args.each do |arg| if arg.is_a?(Symbol) instance_eval(&@library[arg]) else instance_eval(&arg) end _text ', ' unless arg == args.last end if block _css_block(block) _unify_block end end def method_missing(sym, *args, &block) sym = "#{sym}:#{args.shift}" if args.first.kind_of?(Symbol) if block _start_container(sym, args.first) _css_block(block) _unify_block elsif @in_block _indent _css_line(sym, *args) _newline return self else _start_container(sym, args.first, false) _unify_block end self end # "Cargo culted" from Jim who also "cargo culted" it. See xmlbase.rb. def nil? false end private def _unify_block @target << @parts * '' @parts = [] end def _join_with_op!(op) rhs, lhs = @target.pop, @target.pop @target << "#{lhs} #{op} #{rhs}" end def _text(text) @parts << text end def _css_block(block) _newline _nested_structures(block) _end_container _end_block end def _end_block _newline _newline end def _newline _text "\n" end def _indent _text ' ' * @indent end def _nested_structures(block) @in_block = true self.instance_eval(&block) @in_block = false end def _start_container(sym, atts = {}, with_bracket = true) selector = sym.to_s selector << ".#{atts[:class]}" if atts && atts[:class] selector << '#' + "#{atts[:id]}" if atts && atts[:id] @parts << "#{selector}#{with_bracket ? ' {' : ''}" end def _end_container @parts << "}" end def _css_line(sym, *args) _text("#{sym.to_s.gsub('_','-')}: #{args * ' '};") end end end