Class: Aurita::GUI::Element
- Inherits:
-
Array
- Object
- Array
- Aurita::GUI::Element
- Extended by:
- Marshal_Helper_Class_Methods
- Includes:
- Marshal_Helper
- Defined in:
- lib/aurita-gui/element.rb,
lib/aurita-gui/marshal.rb,
lib/aurita-gui/element_fixed.rb
Overview
GUI::Element is the base class for any rendering implementation. It consists of the following members:
* @tag: The HTML tag to render.
* @attrib: A hash storing tag attributes, like
{ :href => '/link/to/somewhere' }
* @content: Content this element is wrapping.
Content can be set in the constructor
via parameter :content or using a
block or by #content and #content=.
Usage as container
Element implements all features expected from a container class. It delegates access to @content to class Array, so an element can be used as Array instance, too:
e = Element.new { Element.new { 'first' } + Element.new { 'second' } }
puts e.join(' -- ')
-->
'first -- second'
You can also push elements into an element:
e1 = HTML.div { 'Foo' }
e2 = HTML.div { 'Bar' }
assert_equal(e[0], 'Foo')
assert_equal(e[1], e2)
It also keeps track of parent classes:
assert_equal(e1[1].parent, e1)
Random access operators are redefined, so you can either access elements by array index, as usual, as well as by their DOM id:
e = Element.new { Element.new(:tag => :p, :id => :foo) { 'nested element' } }
puts e[:foo].to_s
-->
'<p id="foo">nested element</p>'
Builder
Most methods invoked on an Element instance are redirected to return or set a tag attribute. Example:
link = Element.new(:tag => :a) { 'click me' }
link.href = '/link/to/somewhere'
Same as
link = Element(:tag => :a,
:content => 'click me',
:href => '/link/to/somewhere')
An Element instance can wrap one or more other elements:
image_link = Element.new(:tag => :a, :href => '/link/') {
Element.new(:tag => :img, :src => '/an_image.png')
}
In case an element has no content, it will render a self-closing tag, like <img … />.
In most cases you won’t use class Element directly, but by using a factory like Aurita::GUI::HTML or by any derived class like Aurita::GUI::Form or Aurita::GUI::Table.
Markaby style
A syntax similar to markaby is also provided:
HTML.build {
div.outer {
p.inner 'click me'
} +
div.footer 'text at the bottom'
}.to_s
–>
<div class="outer">
<p class="inner">paragraph</p>
</div>
<div class="footer">text at the bottom</div>
Javascript convenience
When including the Javascript helper (aurita-gui/javascript), class HTML is extended by method .js, which provides building Javascript snippets in ruby:
e = HTML.build {
div.outer(:onclick => js.Wombat.alert('message')) {
p.inner 'click me'
}
}
e.to_s
–>
<div class="outer" onclick="Wombat.alert(\'message\'); ">
<p class="inner">click me</p>
</div>
But watch out for operator precedence! This won’t work, as .js() catches the block first:
HTML.build {
div :header, :onclick => js.funcall { 'i will not be passed to div' }
}
–>
<div class="header" onclick="funcall();"></div>
So be explicit, use parentheses:
HTML.build {
div(:header, :onclick => js.funcall) { 'aaah, much better' }
}
–>
<div class="header" onclick="funcall();">aaah, much better</div>
Notes
Double-quotes in tag parameters will be escaped when rendering to string.
e = Element.new(:onclick => 'alert("message");')
The value of parameter :onclick does not change, but will be escaped when rendering:
e.onclick == 'alert("message");'
e.to_s == '<div onclick="alert(\"message\");"></div>'
Direct Known Subclasses
Buffered_Element, Button, Fieldset, Form, Form_Content_Wrapper, Form_Field, Form_Field_Wrapper, PseudoElement, Table, Table_Cell, Table_Row, Widget
Constant Summary collapse
- @@element_count =
0
- @@render_count =
0
Instance Attribute Summary collapse
-
#attrib ⇒ Object
Returns the value of attribute attrib.
-
#force_closing_tag ⇒ Object
Returns the value of attribute force_closing_tag.
-
#gui_element_id ⇒ Object
Returns the value of attribute gui_element_id.
-
#parent ⇒ Object
Returns the value of attribute parent.
-
#tag ⇒ Object
Returns the value of attribute tag.
Instance Method Summary collapse
-
#+(other) ⇒ Object
Return [ self, other ] so concatenation of Element instances works as expected; .
-
#<<(other) ⇒ Object
(also: #add_child, #add_content)
Append object to array of nested elements.
-
#[](index) ⇒ Object
Do not redirect random access operators.
-
#[]=(index, element) ⇒ Object
Do not redirect random access operators.
-
#add_class(*css_class_names) ⇒ Object
(also: #add_css_class, #add_css_classes, #add_classes)
Add CSS class to this Element instance.
-
#aurita_gui_element ⇒ Object
To definitely tell if a class is anyhow derived from Element, use .
-
#clear_floating ⇒ Object
Static helper definition for clearing CSS floats.
-
#css_classes ⇒ Object
(also: #css_class)
Return CSS classes as array.
-
#find_by_dom_id(dom_id) ⇒ Object
Retreive an element from object tree by its dom_id.
-
#get_content ⇒ Object
Returns nested content as array.
- #has_content? ⇒ Boolean
-
#id ⇒ Object
(also: #dom_id)
Alias definition for #dom_id().
-
#id=(value) ⇒ Object
(also: #dom_id=)
Alias definition for #dom_id=(value) Define explicitly so built-in method #id is not invoked instead.
-
#initialize(*args, &block) ⇒ Element
constructor
A new instance of Element.
- #inspect ⇒ Object
- #js_init_code ⇒ Object
- #length ⇒ Object
-
#method_missing(meth, value = nil, &block) ⇒ Object
Redirect methods to setting or retreiving tag attributes.
-
#recurse(&block) ⇒ Object
Iterates over all Elements in this instances object tree (depth first).
-
#remove_class(css_class_name) ⇒ Object
(also: #remove_css_class)
Remove CSS class from this Element instance.
-
#set_content(obj) ⇒ Object
(also: #content=)
Set enclosed content of this element.
-
#string ⇒ Object
(also: #to_s, #to_str, #to_string)
Render this element to a string.
-
#swap(other) ⇒ Object
(also: #copy)
Copy constructor.
-
#to_ary ⇒ Object
(also: #to_a)
Returns [ self ], so concatenation with Arrays and other Element instances works as expected (see #<<(other)..
-
#touch ⇒ Object
(also: #touch!)
Tell object tree instance to rebuild object tree on next call of #string as this element instance has been changed.
- #touched? ⇒ Boolean
-
#type=(type) ⇒ Object
Define explicitly so built-in method #type is not invoked instead.
- #untouch ⇒ Object
Methods included from Marshal_Helper_Class_Methods
Methods included from Marshal_Helper
Constructor Details
#initialize(*args, &block) ⇒ Element
Returns a new instance of Element.
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 |
# File 'lib/aurita-gui/element.rb', line 175 def initialize(*args, &block) case args[0] when Hash params = args[0] else params = args[1] params ||= {} params[:content] = args[0] end @touched = false @@element_count += 1 @gui_element_id = @@element_count @id = @@element_count @parent ||= params[:parent] @force_closing_tag = params[:force_closing_tag] params[:tag] = :div if params[:tag].nil? @tag = params[:tag] params.delete(:parent) params.delete(:force_closing_tag) if block_given? then @content = yield else @content = params[:content] unless @content end # DON'T EVER USE @content.string UNLESS FOR RENDERING!! # @content = nil if @content.to_s.length == 0 # <--- NOOOOooo! # instead, do: @content = nil if !@content.is_a?(Element) && ((@content.respond_to?(:length) && @content.length == 0)) @content = [ @content ] unless (@content.kind_of? Array or @content.nil?) @content ||= [] @content.each { |c| if c.is_a?(Element) then c.parent = self end } params.delete(:content) params.delete(:tag) @attrib = params super(@content) end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(meth, value = nil, &block) ⇒ Object
Redirect methods to setting or retreiving tag attributes. There are several possible routings for method_missing:
-
Setting an attribute (no block, method ends in ‘=’) Example:
my_div = HTML.div 'content'
my_div.onlick = "alert('foo');"
puts my_div.to_s
–>
<div onclick="alert('foo');">content</div>
-
Retreiving an attribute (no block, method does not end in ‘=’). Example:
puts my_div.onlick
–>
'alert(\'foo\');'
-
Setting the css class (block or value passed, method does not end in ‘=’). Example:
my_div.highlighted { 'content' }
or
my_div.highlighted 'content'
–>
<div class="highlighted">content</div>
349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 |
# File 'lib/aurita-gui/element.rb', line 349 def method_missing(meth, value=nil, &block) touch() if block_given? then @attrib[:class] = meth @attrib.update(value) if value.is_a? Hash c = yield c = [ c ] unless c.is_a?(Array) __setobj__(c) return self elsif !value.nil? && !meth.to_s.include?('=') then @attrib[:class] = meth case value when Hash then @attrib.update(value) c = value[:content] c = [ c ] if (c && !c.is_a?(Array)) __setobj__(c) if c when String then __setobj__([value]) end return self else return @attrib[meth] unless value or meth.to_s.include? '=' @attrib[meth.to_s.gsub('=','').intern] = value end end |
Instance Attribute Details
#attrib ⇒ Object
Returns the value of attribute attrib.
153 154 155 |
# File 'lib/aurita-gui/element.rb', line 153 def attrib @attrib end |
#force_closing_tag ⇒ Object
Returns the value of attribute force_closing_tag.
153 154 155 |
# File 'lib/aurita-gui/element.rb', line 153 def force_closing_tag @force_closing_tag end |
#gui_element_id ⇒ Object
Returns the value of attribute gui_element_id.
153 154 155 |
# File 'lib/aurita-gui/element.rb', line 153 def gui_element_id @gui_element_id end |
#parent ⇒ Object
Returns the value of attribute parent.
153 154 155 |
# File 'lib/aurita-gui/element.rb', line 153 def parent @parent end |
#tag ⇒ Object
Returns the value of attribute tag.
153 154 155 |
# File 'lib/aurita-gui/element.rb', line 153 def tag @tag end |
Instance Method Details
#+(other) ⇒ Object
Return [ self, other ] so concatenation of Element instances works as expected;
HTML.build {
div { 'first' } + div { 'second' }
}
--> <Element [ <Element 'first'>, <Element 'second'> ] >
284 285 286 287 288 289 290 291 292 293 |
# File 'lib/aurita-gui/element.rb', line 284 def +(other) other = [other] unless other.is_a?(Array) other.each { |o| if o.is_a?(Element) then o.parent = @parent if @parent end } touch # ADDED return [ self ] + other end |
#<<(other) ⇒ Object Also known as: add_child, add_content
Append object to array of nested elements. Object to append (other) does not have to be an Element instance. If so, however, other#parent will be set to this instance.
300 301 302 303 304 305 306 |
# File 'lib/aurita-gui/element.rb', line 300 def <<(other) if other.is_a?(Element) then other.parent = self end touch # ADDED __getobj__().push(other) end |
#[](index) ⇒ Object
Do not redirect random access operators.
397 398 399 400 |
# File 'lib/aurita-gui/element.rb', line 397 def [](index) return super(index) if (index.is_a?(Fixnum)) return find_by_dom_id(index) end |
#[]=(index, element) ⇒ Object
Do not redirect random access operators.
417 418 419 420 421 422 |
# File 'lib/aurita-gui/element.rb', line 417 def []=(index,element) touch() super(index,element) if (index.is_a? Numeric) e = find_by_dom_id(index) e.swap(element) end |
#add_class(*css_class_names) ⇒ Object Also known as: add_css_class, add_css_classes, add_classes
Add CSS class to this Element instance.
e = Element.new(:class => :first)
e.add_class(:second
e.to_s
–>
<div class="first second"></div>
520 521 522 523 524 525 526 527 |
# File 'lib/aurita-gui/element.rb', line 520 def add_class(*css_class_names) touch() if css_class_names.first.is_a?(Array) then css_class_names = css_class_names.first end css_class_names.map! { |c| c.to_sym } @attrib[:class] = (css_classes + css_class_names) end |
#aurita_gui_element ⇒ Object
To definitely tell if a class is anyhow derived from Element, use
something.respond_to? :aurita_gui_element
234 235 |
# File 'lib/aurita-gui/element.rb', line 234 def aurita_gui_element end |
#clear_floating ⇒ Object
Static helper definition for clearing CSS floats.
438 439 440 |
# File 'lib/aurita-gui/element.rb', line 438 def clear_floating '<div style="clear: both;" />' end |
#css_classes ⇒ Object Also known as: css_class
Return CSS classes as array. Note that Element#class is not redefined to return attribute :class, for obvious reasons.
500 501 502 503 504 505 506 507 508 509 510 511 |
# File 'lib/aurita-gui/element.rb', line 500 def css_classes css_classes = @attrib[:class] if css_classes.kind_of? Array css_classes.flatten! elsif css_classes.kind_of? String css_classes = css_classes.split(' ') else # e.g. Symbol css_classes = [ css_classes ] end css_classes.map! { |c| c.to_sym if c } return css_classes end |
#find_by_dom_id(dom_id) ⇒ Object
Retreive an element from object tree by its dom_id
404 405 406 407 408 409 410 411 412 413 414 |
# File 'lib/aurita-gui/element.rb', line 404 def find_by_dom_id(dom_id) dom_id = dom_id.to_sym each { |c| if c.is_a? Element then return c if (c.dom_id == dom_id) sub = c.find_by_dom_id(dom_id) return sub if sub end } return nil end |
#get_content ⇒ Object
Returns nested content as array.
319 320 321 |
# File 'lib/aurita-gui/element.rb', line 319 def get_content __getobj__() end |
#has_content? ⇒ Boolean
259 260 261 |
# File 'lib/aurita-gui/element.rb', line 259 def has_content? (length > 0) end |
#id ⇒ Object Also known as: dom_id
Alias definition for #dom_id()
271 272 273 |
# File 'lib/aurita-gui/element.rb', line 271 def id @attrib[:id] if @attrib end |
#id=(value) ⇒ Object Also known as: dom_id=
Alias definition for #dom_id=(value) Define explicitly so built-in method #id is not invoked instead
266 267 268 |
# File 'lib/aurita-gui/element.rb', line 266 def id=(value) @attrib[:id] = value if @attrib end |
#inspect ⇒ Object
225 226 227 |
# File 'lib/aurita-gui/element.rb', line 225 def inspect "#{self.class.to_s}: <#{@tag}>, #{@attrib.inspect} { #{@content.inspect} }" end |
#js_init_code ⇒ Object
587 588 589 590 591 592 593 594 |
# File 'lib/aurita-gui/element.rb', line 587 def js_init_code() code = js_initialize() if self.respond_to?(:js_initialize) code ||= '' recurse { |e| code << e.js_initialize if e.respond_to?(:js_initialize) } code end |
#length ⇒ Object
237 238 239 |
# File 'lib/aurita-gui/element.rb', line 237 def length __getobj__.length end |
#recurse(&block) ⇒ Object
Iterates over all Elements in this instances object tree (depth first).
x = HTML.build {
div.main {
h2.header { 'Title' } +
div.lead { 'Intro here' } +
div.body {
p.section { 'First' } +
p.section { 'Second' }
}
}
}
x.recurse { |element|
p element.css_class
}
–>
:main
:header
:lead
:body
:section
:section
578 579 580 581 582 583 584 585 |
# File 'lib/aurita-gui/element.rb', line 578 def recurse(&block) each { |c| if c.is_a?(Element) then yield(c) c.recurse(&block) end } end |
#remove_class(css_class_name) ⇒ Object Also known as: remove_css_class
543 544 545 546 547 548 |
# File 'lib/aurita-gui/element.rb', line 543 def remove_class(css_class_name) touch() classes = css_classes classes.delete(css_class_name.to_sym) @attrib[:class] = classes end |
#set_content(obj) ⇒ Object Also known as: content=
Set enclosed content of this element. Will be automatically wrapped in an array.
378 379 380 381 382 383 384 385 386 387 |
# File 'lib/aurita-gui/element.rb', line 378 def set_content(obj) touch() if obj.is_a?(Array) then obj.each { |o| o.parent = self if o.is_a?(Element) } __setobj__(obj) else obj.parent = self if obj.is_a?(Element) return __setobj__([ obj ]) end end |
#string ⇒ Object Also known as: to_s, to_str, to_string
Render this element to a string.
443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 |
# File 'lib/aurita-gui/element.rb', line 443 def string return @string if @string if @tag == :pseudo then @string = get_content if @string.is_a?(Array) then @string = @string.map { |e| e.to_s; e }.join('') else @string = @string.to_s end return @string end @@render_count += 1 attrib_string = '' @attrib.each_pair { |name,value| if value.instance_of?(Array) then value = value.reject { |e| e.to_s == '' }.join(' ') elsif value.instance_of?(TrueClass) then value = name end if !value.nil? then value = value.to_s.gsub('"','\"') attrib_string << " #{name}=\"#{value}\"" end } if (!(@force_closing_tag.instance_of?(FalseClass)) && ![ :hr, :br, :input ].include?(@tag)) then @force_closing_tag = true end if @force_closing_tag || has_content? then # Compatible to ruby 1.9 but SLOW: tmp = __getobj__ tmp = tmp.map { |e| e.to_s; e }.join('') if tmp.is_a?(Array) # return "<#{@tag}#{attrib_string}>#{tmp}</#{@tag}>" # # Ruby 1.8 only: # inner = __getobj__.to_s @string = "<#{@tag}#{attrib_string}>#{tmp}</#{@tag}>" untouch() return @string else untouch() @string = "<#{@tag}#{attrib_string} />" return @string end end |
#swap(other) ⇒ Object Also known as: copy
Copy constructor. Replace self with other element.
426 427 428 429 430 431 432 433 |
# File 'lib/aurita-gui/element.rb', line 426 def swap(other) touch() save_own_id = dom_id() @tag = other.tag @attrib = other.attrib @attrib[:id] = save_own_id __setobj__(other.get_content) end |
#to_ary ⇒ Object Also known as: to_a
Returns [ self ], so concatenation with Arrays and other Element instances works as expected (see #<<(other).
313 314 315 |
# File 'lib/aurita-gui/element.rb', line 313 def to_ary [ self ] end |
#touch ⇒ Object Also known as: touch!
Tell object tree instance to rebuild object tree on next call of #string as this element instance has been changed.
245 246 247 248 249 250 |
# File 'lib/aurita-gui/element.rb', line 245 def touch @touched = true @string = nil # Don't re-touch! Parent could have been caller! @parent.touch() if (@parent && !@parent.touched?) end |
#touched? ⇒ Boolean
252 253 254 |
# File 'lib/aurita-gui/element.rb', line 252 def touched? (@touched == true) end |
#type=(type) ⇒ Object
Define explicitly so built-in method #type is not invoked instead
392 393 394 |
# File 'lib/aurita-gui/element.rb', line 392 def type=(type) @attrib[:type] = type end |
#untouch ⇒ Object
255 256 257 |
# File 'lib/aurita-gui/element.rb', line 255 def untouch @touched = false end |