Class: Glimmer::Web::ElementProxy
- Inherits:
-
Object
- Object
- Glimmer::Web::ElementProxy
- Includes:
- Glimmer
- Defined in:
- lib/glimmer/web/element_proxy.rb
Defined Under Namespace
Classes: Event
Constant Summary collapse
- ELEMENT_KEYWORDS =
[ "a", "abbr", "acronym", "address", "applet", "area", "article", "aside", "audio", "base", "basefont", "bdi", "bdo", "bgsound", "big", "blink", "blockquote", "body", "button", "canvas", "caption", "center", "cite", "code", "col", "colgroup", "data", "datalist", "dd", "decorator", "details", "dfn", "dir", "div", "dl", "dt", "element", "embed", "fieldset", "figcaption", "figure", "font", "footer", "form", "frame", "frameset", "h1", "h2", "h3", "h4", "h5", "h6", "head", "header", "hgroup", "hr", "html", "iframe", "img", "input", "isindex", "kbd", "keygen", "label", "legend", "li", "link", "listing", "main", "map", "marquee", "menu", "menuitem", "meta", "meter", "nav", "nobr", "noframes", "noscript", "object", "ol", "optgroup", "option", "output", "p", "param", "plaintext", "pre", "progress", "q", "rp", "rt", "ruby", "s", "samp", "script", "section", "select", "shadow", "source", "spacer", "span", "strike", "style", "summary", "table", "tbody", "td", "template", "textarea", "tfoot", "th", "thead", "time", "title", "tr", "track", "tt", "u", "ul", "var", "video", "wbr", "xmp", ]
- GLIMMER_ATTRIBUTES =
[:parent]
- PROPERTY_ALIASES =
{ 'inner_html' => 'innerHTML', 'outer_html' => 'outerHTML', }
- FORMAT_DATETIME =
'%Y-%m-%dT%H:%M'- FORMAT_DATE =
'%Y-%m-%d'- FORMAT_TIME =
'%H:%M'- REGEX_FORMAT_DATETIME =
/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}$/- REGEX_FORMAT_DATE =
/^\d{4}-\d{2}-\d{2}$/- REGEX_FORMAT_TIME =
/^\d{2}:\d{2}$/
Instance Attribute Summary collapse
-
#args ⇒ Object
readonly
Returns the value of attribute args.
-
#background ⇒ Object
Returns the value of attribute background.
-
#children ⇒ Object
readonly
Returns the value of attribute children.
-
#component ⇒ Object
readonly
Returns the value of attribute component.
-
#enabled ⇒ Object
Returns the value of attribute enabled.
-
#foreground ⇒ Object
Returns the value of attribute foreground.
-
#keyword ⇒ Object
readonly
Returns the value of attribute keyword.
-
#options ⇒ Object
readonly
Returns the value of attribute options.
-
#parent ⇒ Object
readonly
Returns the value of attribute parent.
-
#parent_component ⇒ Object
readonly
Returns the value of attribute parent_component.
-
#removed ⇒ Object
(also: #removed?)
readonly
Returns the value of attribute removed.
-
#rendered ⇒ Object
(also: #rendered?)
readonly
Returns the value of attribute rendered.
Class Method Summary collapse
-
.element_type(keyword) ⇒ Object
NOTE: Avoid using this method for now as it has slow performance returns Ruby proxy class (type) that would handle this keyword.
-
.for(keyword, parent, args, block) ⇒ Object
NOTE: Avoid using this method until we start supporting ElementProxy subclasses in which case, we must cache them to avoid the slow performance of element_type Factory Method that translates a Glimmer DSL keyword into a ElementProxy object.
- .keyword_supported?(keyword) ⇒ Boolean
- .max_id_number_for(name) ⇒ Object
- .max_id_numbers ⇒ Object
- .next_id_number_for(name) ⇒ Object
- .render_html(element, attributes, content = nil) ⇒ Object
- .reset_max_id_numbers! ⇒ Object
- .underscored_widget_name(widget_proxy) ⇒ Object
- .unrendered_dom_element(keyword) ⇒ Object
- .widget_handling_listener ⇒ Object
Instance Method Summary collapse
- #add_content_on_render(&content_block) ⇒ Object
- #add_contents_for_render_blocks ⇒ Object
- #add_css_class(css_class) ⇒ Object
- #add_css_classes(css_classes_to_add) ⇒ Object
- #add_text_content(text, on_empty: false) ⇒ Object
- #attach(the_parent_dom_element) ⇒ Object
-
#bind_content(*binding_args, &content_block) ⇒ Object
Data-binds the generation of nested content to a model/property (in binding args) consider providing an option to avoid initial rendering without any changes happening.
- #build_dom(layout: true) ⇒ Object
- #bulk_render? ⇒ Boolean
- #can_handle_observation_request?(keyword) ⇒ Boolean
- #children_dom_content ⇒ Object
- #class_name=(value) ⇒ Object
- #clear_css_classes ⇒ Object
- #content(bulk_render: false, &block) ⇒ Object
- #content_on_render_blocks ⇒ Object
- #css_classes ⇒ Object
- #data_bind(property, model_binding) ⇒ Object
- #data_binding_element_keyword_to_property_listener_map ⇒ Object
- #data_binding_listener_for_element_and_property(element_keyword, property) ⇒ Object
- #data_binding_property_listener_map_for_element(element_keyword) ⇒ Object
- #data_bindings ⇒ Object
- #dialog_ancestor ⇒ Object
- #dom ⇒ Object
- #dom_element ⇒ Object
-
#element ⇒ Object
Root element representing widget.
-
#element_id ⇒ Object
element ID is used as a css class to identify the element.
- #enqueue_post_render_method_call(method_name, *args, &block) ⇒ Object
- #event_handling_suspended? ⇒ Boolean
- #event_listener_proxies ⇒ Object
- #handle_observation_request(keyword, original_event_listener) ⇒ Object
- #handle_observation_requests ⇒ Object
- #html_options ⇒ Object
-
#initialize(keyword, parent, args, block) ⇒ ElementProxy
constructor
A new instance of ElementProxy.
- #input_value_converters ⇒ Object
- #invoke_post_render_method_calls ⇒ Object
- #listeners ⇒ Object
- #listeners_for(listener_event) ⇒ Object
- #mark_rendered ⇒ Object
- #method_missing(method_name, *args, &block) ⇒ Object
- #name ⇒ Object
- #notify_listeners(event) ⇒ Object
- #notify_on_render_listeners ⇒ Object
-
#observation_request_to_event_mapping ⇒ Object
Subclasses must override with their own mappings.
-
#observation_requests ⇒ Object
TODO consider adding a default #dom method implementation for the common case, automatically relying on #element and other methods to build the dom html.
- #parent_dom_element ⇒ Object
- #parent_selector ⇒ Object
- #parents ⇒ Object
-
#post_add_content ⇒ Object
Executes at the closing of a parent widget curly braces after all children/properties have been added/set.
-
#post_initialize_child(child) ⇒ Object
Executes for the parent of a child that just got added.
-
#post_remove_child(child) ⇒ Object
Executes for the parent of a child that just got removed.
- #post_render_method_calls ⇒ Object
- #print ⇒ Object
- #property_name_for(method_name) ⇒ Object
- #reattach(old_element) ⇒ Object
- #remove ⇒ Object
- #remove_all_listeners ⇒ Object
- #remove_css_class(css_class) ⇒ Object
- #remove_css_classes(css_classes_to_remove) ⇒ Object
- #remove_event_listener_proxies ⇒ Object
- #render(parent: nil, custom_parent_dom_element: nil, brand_new: false) ⇒ Object (also: #rerender)
- #render_after_create? ⇒ Boolean
- #respond_to_missing?(method_name, include_private = false) ⇒ Boolean
- #resume_event_handling ⇒ Object
-
#selector ⇒ Object
Subclasses can override with their own selector.
- #shell ⇒ Object
- #skip_content_on_render_blocks? ⇒ Boolean
- #suspend_event_handling ⇒ Object
- #swt_widget ⇒ Object
- #type ⇒ Object
- #unnormalized_property_name_for(method_name) ⇒ Object
- #value_converters_for_input_type(input_type) ⇒ Object
Constructor Details
#initialize(keyword, parent, args, block) ⇒ ElementProxy
Returns a new instance of ElementProxy.
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 |
# File 'lib/glimmer/web/element_proxy.rb', line 130 def initialize(keyword, parent, args, block) @keyword = keyword @parent = parent.is_a?(Glimmer::Web::Component) ? parent.markup_root : parent @parent_component = parent if parent.is_a?(Glimmer::Web::Component) if Component.interpretation_stack.last&.markup_root.nil? @component = Component.interpretation_stack.last @component&.instance_variable_set("@markup_root", self) end = args.last.is_a?(Hash) ? args.last.symbolize_keys : {} if parent.nil? [:parent] ||= Component.interpretation_stack.last&.&.[](:parent) [:render] ||= Component.interpretation_stack.last&.&.[](:render) [:bulk_render] ||= Component.interpretation_stack.last&.&.[](:bulk_render) end @args = args @block = block @children = [] @parent&.post_initialize_child(self) render if !bulk_render? && !@rendered && render_after_create? end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(method_name, *args, &block) ⇒ Object
611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 |
# File 'lib/glimmer/web/element_proxy.rb', line 611 def method_missing(method_name, *args, &block) # TODO consider doing more correct checking of availability of properties/methods using native ticks property_name = property_name_for(method_name) unnormalized_property_name = unnormalized_property_name_for(method_name) if method_name.to_s.start_with?('on_') handle_observation_request(method_name, block) elsif dom_element.respond_to?(method_name) if rendered? dom_element.send(method_name, *args, &block) else enqueue_post_render_method_call(method_name, *args, &block) end elsif !dom_element.prop(property_name).nil? && !dom_element.prop(property_name).is_a?(Proc) if rendered? if method_name.end_with?('=') dom_element.prop(property_name, *args) else dom_element.prop(property_name) end else enqueue_post_render_method_call(method_name, *args, &block) end elsif !dom_element.prop(unnormalized_property_name).nil? && !dom_element.prop(unnormalized_property_name).is_a?(Proc) if rendered? if method_name.end_with?('=') dom_element.prop(unnormalized_property_name, *args) else dom_element.prop(unnormalized_property_name) end else enqueue_post_render_method_call(method_name, *args, &block) end elsif dom_element && dom_element.length > 0 if rendered? js_args = block.nil? ? args : (args + [block]) begin Native.call(dom_element, '0').method_missing(method_name.to_s.camelcase, *js_args) rescue Exception => e begin Native.call(dom_element, '0').method_missing(method_name.to_s, *js_args) rescue Exception => e super(method_name, *args, &block) end end else enqueue_post_render_method_call(method_name, *args, &block) end else super(method_name, *args, &block) end end |
Instance Attribute Details
#args ⇒ Object (readonly)
Returns the value of attribute args.
126 127 128 |
# File 'lib/glimmer/web/element_proxy.rb', line 126 def args @args end |
#background ⇒ Object
Returns the value of attribute background.
126 127 128 |
# File 'lib/glimmer/web/element_proxy.rb', line 126 def background @background end |
#children ⇒ Object (readonly)
Returns the value of attribute children.
126 127 128 |
# File 'lib/glimmer/web/element_proxy.rb', line 126 def children @children end |
#component ⇒ Object (readonly)
Returns the value of attribute component.
126 127 128 |
# File 'lib/glimmer/web/element_proxy.rb', line 126 def component @component end |
#enabled ⇒ Object
Returns the value of attribute enabled.
126 127 128 |
# File 'lib/glimmer/web/element_proxy.rb', line 126 def enabled @enabled end |
#foreground ⇒ Object
Returns the value of attribute foreground.
126 127 128 |
# File 'lib/glimmer/web/element_proxy.rb', line 126 def foreground @foreground end |
#keyword ⇒ Object (readonly)
Returns the value of attribute keyword.
126 127 128 |
# File 'lib/glimmer/web/element_proxy.rb', line 126 def keyword @keyword end |
#options ⇒ Object (readonly)
Returns the value of attribute options.
126 127 128 |
# File 'lib/glimmer/web/element_proxy.rb', line 126 def end |
#parent ⇒ Object (readonly)
Returns the value of attribute parent.
126 127 128 |
# File 'lib/glimmer/web/element_proxy.rb', line 126 def parent @parent end |
#parent_component ⇒ Object (readonly)
Returns the value of attribute parent_component.
126 127 128 |
# File 'lib/glimmer/web/element_proxy.rb', line 126 def parent_component @parent_component end |
#removed ⇒ Object (readonly) Also known as: removed?
Returns the value of attribute removed.
126 127 128 |
# File 'lib/glimmer/web/element_proxy.rb', line 126 def removed @removed end |
#rendered ⇒ Object (readonly) Also known as: rendered?
Returns the value of attribute rendered.
126 127 128 |
# File 'lib/glimmer/web/element_proxy.rb', line 126 def rendered @rendered end |
Class Method Details
.element_type(keyword) ⇒ Object
NOTE: Avoid using this method for now as it has slow performance returns Ruby proxy class (type) that would handle this keyword
43 44 45 46 47 48 |
# File 'lib/glimmer/web/element_proxy.rb', line 43 def element_type(keyword) class_name_main = "#{keyword.camelcase(:upper)}Proxy" Glimmer::Web::ElementProxy.const_get(class_name_main.to_sym) rescue NameError => e Glimmer::Web::ElementProxy end |
.for(keyword, parent, args, block) ⇒ Object
NOTE: Avoid using this method until we start supporting ElementProxy subclasses in which case, we must cache them to avoid the slow performance of element_type Factory Method that translates a Glimmer DSL keyword into a ElementProxy object
37 38 39 |
# File 'lib/glimmer/web/element_proxy.rb', line 37 def for(keyword, parent, args, block) element_type(keyword).new(keyword, parent, args, block) end |
.keyword_supported?(keyword) ⇒ Boolean
30 31 32 |
# File 'lib/glimmer/web/element_proxy.rb', line 30 def keyword_supported?(keyword) ELEMENT_KEYWORDS.include?(keyword.to_s) end |
.max_id_number_for(name) ⇒ Object
54 55 56 |
# File 'lib/glimmer/web/element_proxy.rb', line 54 def max_id_number_for(name) @max_id_numbers[name] = max_id_numbers[name] || 0 end |
.max_id_numbers ⇒ Object
58 59 60 |
# File 'lib/glimmer/web/element_proxy.rb', line 58 def max_id_numbers @max_id_numbers ||= reset_max_id_numbers! end |
.next_id_number_for(name) ⇒ Object
50 51 52 |
# File 'lib/glimmer/web/element_proxy.rb', line 50 def next_id_number_for(name) @max_id_numbers[name] = max_id_number_for(name) + 1 end |
.render_html(element, attributes, content = nil) ⇒ Object
74 75 76 77 78 79 80 81 82 83 84 85 |
# File 'lib/glimmer/web/element_proxy.rb', line 74 def render_html(element, attributes, content = nil) attributes = attributes.reduce('') do |output, option_pair| attribute, value = option_pair value = value.to_s.sub('"', '"').sub("'", ''') output += " #{attribute}=\"#{value}\"" end if content.nil? "<#{element}#{attributes} />" else "<#{element}#{attributes}>#{content}</#{element}>" end end |
.reset_max_id_numbers! ⇒ Object
62 63 64 |
# File 'lib/glimmer/web/element_proxy.rb', line 62 def reset_max_id_numbers! @max_id_numbers = {} end |
.underscored_widget_name(widget_proxy) ⇒ Object
66 67 68 |
# File 'lib/glimmer/web/element_proxy.rb', line 66 def () .class.name.split(/::|\./).last.sub(/Proxy$/, '').underscore end |
.unrendered_dom_element(keyword) ⇒ Object
87 88 89 90 |
# File 'lib/glimmer/web/element_proxy.rb', line 87 def unrendered_dom_element(keyword) @unrendered_dom_elements ||= {} @unrendered_dom_elements[keyword] ||= Element["<#{keyword} />"] end |
.widget_handling_listener ⇒ Object
70 71 72 |
# File 'lib/glimmer/web/element_proxy.rb', line 70 def end |
Instance Method Details
#add_content_on_render(&content_block) ⇒ Object
347 348 349 350 351 352 353 |
# File 'lib/glimmer/web/element_proxy.rb', line 347 def add_content_on_render(&content_block) if rendered? content_block.call else content_on_render_blocks << content_block end end |
#add_contents_for_render_blocks ⇒ Object
689 690 691 692 693 694 695 696 |
# File 'lib/glimmer/web/element_proxy.rb', line 689 def add_contents_for_render_blocks unless skip_content_on_render_blocks? content_on_render_blocks.each do |content_block| content(&content_block) end end children.each(&:add_contents_for_render_blocks) if bulk_render? end |
#add_css_class(css_class) ⇒ Object
422 423 424 425 426 427 428 |
# File 'lib/glimmer/web/element_proxy.rb', line 422 def add_css_class(css_class) if rendered? dom_element.add_class(css_class) else enqueue_post_render_method_call('add_css_class', css_class) end end |
#add_css_classes(css_classes_to_add) ⇒ Object
430 431 432 |
# File 'lib/glimmer/web/element_proxy.rb', line 430 def add_css_classes(css_classes_to_add) css_classes_to_add.each {|css_class| add_css_class(css_class)} end |
#add_text_content(text, on_empty: false) ⇒ Object
331 332 333 334 335 336 337 |
# File 'lib/glimmer/web/element_proxy.rb', line 331 def add_text_content(text, on_empty: false) if rendered? dom_element.append(text.to_s) if !on_empty || dom_element.text.to_s.empty? else enqueue_post_render_method_call('add_text_content', text, on_empty:) end end |
#attach(the_parent_dom_element) ⇒ Object
318 319 320 |
# File 'lib/glimmer/web/element_proxy.rb', line 318 def attach(the_parent_dom_element) the_parent_dom_element.append(@dom) end |
#bind_content(*binding_args, &content_block) ⇒ Object
Data-binds the generation of nested content to a model/property (in binding args) consider providing an option to avoid initial rendering without any changes happening
576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 |
# File 'lib/glimmer/web/element_proxy.rb', line 576 def bind_content(*binding_args, &content_block) content_binding_work = proc do |*values| # TODO in the future, consider optimizing code by diffing content if that makes sense (e.g. using opal-virtual-dom) # To do so, we must avoid generating new content with new unique IDs/Classes and only append the new IDs classes after mounting # TODO consider optimizing remove performance by doing clear instead and removing listeners separately children.dup.each { |child| child.remove } content(bulk_render: true, &content_block) if bulk_render? && rendered? self.inner_html = children_dom_content children.each(&:mark_rendered) children.each(&:invoke_post_render_method_calls) children.each(&:handle_observation_requests) children.each(&:add_contents_for_render_blocks) children.each(&:notify_on_render_listeners) end end model_binding_observer = Glimmer::DataBinding::ModelBinding.new(*binding_args) content_binding_observer = Glimmer::DataBinding::Observer.proc(&content_binding_work) content_binding_observer.observe(model_binding_observer) content_binding_work.call # TODO inspect if we need to pass args here (from observed attributes) [but it's simpler not to pass anything at first] end |
#build_dom(layout: true) ⇒ Object
355 356 357 358 |
# File 'lib/glimmer/web/element_proxy.rb', line 355 def build_dom(layout: true) # TODO consider passing parent element instead and having table item include a table cell widget only for opal @dom = dom # TODO unify how to build dom for most widgets based on element, id, and name (class) end |
#bulk_render? ⇒ Boolean
151 152 153 |
# File 'lib/glimmer/web/element_proxy.rb', line 151 def bulk_render? [:bulk_render] != false && (@parent.nil? || @parent.bulk_render?) end |
#can_handle_observation_request?(keyword) ⇒ Boolean
490 491 492 493 494 495 496 497 498 499 500 501 502 |
# File 'lib/glimmer/web/element_proxy.rb', line 490 def can_handle_observation_request?(keyword) # TODO sort this out for Opal keyword = keyword.to_s keyword.start_with?('on') # if keyword.start_with?('on_swt_') # constant_name = keyword.sub(/^on_swt_/, '') # SWTProxy.has_constant?(constant_name) # elsif keyword.start_with?('on_') # # event = keyword.sub(/^on_/, '') # # can_add_listener?(event) || can_handle_drag_observation_request?(keyword) || can_handle_drop_observation_request?(keyword) # true # TODO filter by valid listeners only in the future # end end |
#children_dom_content ⇒ Object
370 371 372 |
# File 'lib/glimmer/web/element_proxy.rb', line 370 def children_dom_content children.map(&:dom).join end |
#class_name=(value) ⇒ Object
412 413 414 415 416 417 418 419 420 |
# File 'lib/glimmer/web/element_proxy.rb', line 412 def class_name=(value) if rendered? values = value.is_a?(Array) ? value : [value.to_s] new_class_name = (base_css_classes + values).uniq.compact.join(' ') dom_element.prop('className', new_class_name) else enqueue_post_render_method_call('class_name=', value) end end |
#clear_css_classes ⇒ Object
446 447 448 |
# File 'lib/glimmer/web/element_proxy.rb', line 446 def clear_css_classes css_classes.each {|css_class| remove_css_class(css_class)} end |
#content(bulk_render: false, &block) ⇒ Object
388 389 390 391 392 393 394 |
# File 'lib/glimmer/web/element_proxy.rb', line 388 def content(bulk_render: false, &block) original_bulk_render = [:bulk_render] [:bulk_render] = bulk_render if rendered? return_value = Glimmer::DSL::Engine.add_content(self, Glimmer::DSL::Web::ElementExpression.new, keyword, &block) [:bulk_render] = original_bulk_render if rendered? return_value end |
#content_on_render_blocks ⇒ Object
339 340 341 |
# File 'lib/glimmer/web/element_proxy.rb', line 339 def content_on_render_blocks @content_on_render_blocks ||= [] end |
#css_classes ⇒ Object
175 176 177 |
# File 'lib/glimmer/web/element_proxy.rb', line 175 def css_classes dom_element.attr('class').to_s.split if rendered? end |
#data_bind(property, model_binding) ⇒ Object
551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 |
# File 'lib/glimmer/web/element_proxy.rb', line 551 def data_bind(property, model_binding) element_binding_read_translator = value_converters_for_input_type(type)&.[](:model_to_view) element_binding_parameters = [self, property, element_binding_read_translator] element_binding = DataBinding::ElementBinding.new(*element_binding_parameters) #TODO make this options observer dependent and all similar observers in element specific data binding handlers element_binding.observe(model_binding) element_binding.call(model_binding.evaluate_property) data_bindings[element_binding] = model_binding unless model_binding.[:read_only] # TODO add guards against nil cases for hash below listener_keyword = data_binding_listener_for_element_and_property(keyword, property) if listener_keyword data_binding_read_listener = lambda do |event| view_property_value = send(property) element_binding_write_translator = value_converters_for_input_type(type)&.[](:view_to_model) converted_view_property_value = element_binding_write_translator&.call(view_property_value, model_binding.evaluate_property) || view_property_value model_binding.call(converted_view_property_value) end handle_observation_request(listener_keyword, data_binding_read_listener) end end end |
#data_binding_element_keyword_to_property_listener_map ⇒ Object
720 721 722 723 724 725 726 727 728 729 730 731 732 733 |
# File 'lib/glimmer/web/element_proxy.rb', line 720 def data_binding_element_keyword_to_property_listener_map @data_binding_element_keyword_to_property_listener_map ||= { 'input' => { 'value' => 'oninput', 'checked' => 'oninput', }, 'select' => { 'value' => 'onchange', }, 'textarea' => { 'value' => 'oninput', }, } end |
#data_binding_listener_for_element_and_property(element_keyword, property) ⇒ Object
712 713 714 |
# File 'lib/glimmer/web/element_proxy.rb', line 712 def data_binding_listener_for_element_and_property(element_keyword, property) data_binding_property_listener_map_for_element(element_keyword)[property] end |
#data_binding_property_listener_map_for_element(element_keyword) ⇒ Object
716 717 718 |
# File 'lib/glimmer/web/element_proxy.rb', line 716 def data_binding_property_listener_map_for_element(element_keyword) data_binding_element_keyword_to_property_listener_map[element_keyword] || {} end |
#data_bindings ⇒ Object
539 540 541 |
# File 'lib/glimmer/web/element_proxy.rb', line 539 def data_bindings @data_bindings ||= {} end |
#dialog_ancestor ⇒ Object
237 238 239 |
# File 'lib/glimmer/web/element_proxy.rb', line 237 def dialog_ancestor parents.detect {|p| p.is_a?(DialogProxy)} end |
#dom ⇒ Object
360 361 362 363 364 365 366 367 368 |
# File 'lib/glimmer/web/element_proxy.rb', line 360 def dom # TODO auto-convert known glimmer attributes like parent to data attributes like data-parent # TODO check if we need to avoid rendering content block if no content is available @dom ||= begin content = args.first.is_a?(String) ? args.first : '' content += children_dom_content if bulk_render? ElementProxy.render_html(keyword, , content) end end |
#dom_element ⇒ Object
450 451 452 453 454 455 456 457 458 |
# File 'lib/glimmer/web/element_proxy.rb', line 450 def dom_element if rendered? # TODO consider making this pick an element in relation to its parent, allowing unhooked dom elements to be built if needed (unhooked to the visible page dom) Document.find(selector) else # Using a fill-in dom element until self is rendered ElementProxy.unrendered_dom_element(keyword) end end |
#element ⇒ Object
Root element representing widget. Must be overridden by subclasses if different from div
217 218 219 |
# File 'lib/glimmer/web/element_proxy.rb', line 217 def element keyword end |
#element_id ⇒ Object
element ID is used as a css class to identify the element. It is intentionally not set as the actual HTML element ID to let software engineers specify their own IDs if they wanted
408 409 410 |
# File 'lib/glimmer/web/element_proxy.rb', line 408 def element_id @element_id ||= "element-#{ElementProxy.next_id_number_for(name)}" end |
#enqueue_post_render_method_call(method_name, *args, &block) ⇒ Object
667 668 669 670 |
# File 'lib/glimmer/web/element_proxy.rb', line 667 def enqueue_post_render_method_call(method_name, *args, &block) post_render_method_calls << [method_name, args, block] nil end |
#event_handling_suspended? ⇒ Boolean
478 479 480 |
# File 'lib/glimmer/web/element_proxy.rb', line 478 def event_handling_suspended? @event_handling_suspended end |
#event_listener_proxies ⇒ Object
466 467 468 |
# File 'lib/glimmer/web/element_proxy.rb', line 466 def event_listener_proxies @event_listener_proxies ||= [] end |
#handle_observation_request(keyword, original_event_listener) ⇒ Object
504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 |
# File 'lib/glimmer/web/element_proxy.rb', line 504 def handle_observation_request(keyword, original_event_listener) if rendered? listener = ListenerProxy.new( element: self, selector: selector, dom_element: dom_element, event_attribute: keyword, original_event_listener: original_event_listener, ) listener.register listeners_for(keyword) << listener listener else enqueue_post_render_method_call('handle_observation_request', keyword, original_event_listener) end end |
#handle_observation_requests ⇒ Object
680 681 682 683 684 685 686 687 |
# File 'lib/glimmer/web/element_proxy.rb', line 680 def handle_observation_requests observation_requests&.each do |keyword, event_listener_set| event_listener_set.each do |event_listener| handle_observation_request(keyword, event_listener) end end children.each(&:handle_observation_requests) if bulk_render? end |
#html_options ⇒ Object
374 375 376 377 378 379 380 381 382 383 384 385 386 |
# File 'lib/glimmer/web/element_proxy.rb', line 374 def body_class = (base_css_classes + css_classes.to_a).uniq.compact.join(' ') = .dup GLIMMER_ATTRIBUTES.each do |attribute| next unless .include?(attribute) data_normalized_attribute = attribute.split('_').join('-') ["data-#{data_normalized_attribute}"] = .delete(attribute) end [:class] ||= '' [:class] = "#{html_options[:class]} #{body_class}".strip ['data-turbo'] = 'false' if parent.nil? end |
#input_value_converters ⇒ Object
739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 |
# File 'lib/glimmer/web/element_proxy.rb', line 739 def input_value_converters @input_value_converters ||= { 'number' => { model_to_view: -> (value, old_value) { value.to_s }, view_to_model: -> (value, old_value) { value.include?('.') ? value.to_f : value.to_i }, }, 'range' => { model_to_view: -> (value, old_value) { value.to_s }, view_to_model: -> (value, old_value) { value.include?('.') ? value.to_f : value.to_i }, }, 'datetime-local' => { model_to_view: -> (value, old_value) { if value.respond_to?(:strftime) value.strftime(FORMAT_DATETIME) elsif value.is_a?(String) && valid_js_date_string?(value) value else old_value end }, view_to_model: -> (value, old_value) { if value.to_s.empty? nil else date = Native(`new Date(Date.parse(#{value}))`) year = Native.call(date, 'getFullYear') month = Native.call(date, 'getMonth') + 1 day = Native.call(date, 'getDate') hour = Native.call(date, 'getHours') minute = Native.call(date, 'getMinutes') Time.new(year, month, day, hour, minute) end }, }, 'date' => { model_to_view: -> (value, old_value) { if value.respond_to?(:strftime) value.strftime(FORMAT_DATE) elsif value.is_a?(String) && valid_js_date_string?(value) value else old_value end }, view_to_model: -> (value, old_value) { if value.to_s.empty? nil else year, month, day = value.split('-') if old_value Time.new(year, month, day, old_value.hour, old_value.min) else Time.new(year, month, day) end end }, }, 'time' => { model_to_view: -> (value, old_value) { if value.respond_to?(:strftime) value.strftime(FORMAT_TIME) elsif value.is_a?(String) && valid_js_date_string?(value) value else old_value end }, view_to_model: -> (value, old_value) { if value.to_s.empty? nil else hour, minute = value.split(':') if old_value Time.new(old_value.year, old_value.month, old_value.day, hour, minute) else now = Time.now Time.new(now.year, now.month, now.day, hour, minute) end end }, }, } end |
#invoke_post_render_method_calls ⇒ Object
672 673 674 675 676 677 678 |
# File 'lib/glimmer/web/element_proxy.rb', line 672 def invoke_post_render_method_calls return unless rendered? post_render_method_calls.each do |method_name, args, block| send(method_name, *args, &block) end children.each(&:invoke_post_render_method_calls) if bulk_render? end |
#listeners ⇒ Object
482 483 484 |
# File 'lib/glimmer/web/element_proxy.rb', line 482 def listeners @listeners ||= {} end |
#listeners_for(listener_event) ⇒ Object
486 487 488 |
# File 'lib/glimmer/web/element_proxy.rb', line 486 def listeners_for(listener_event) listeners[listener_event.to_s] ||= [] end |
#mark_rendered ⇒ Object
326 327 328 329 |
# File 'lib/glimmer/web/element_proxy.rb', line 326 def mark_rendered @rendered = true children.each(&:mark_rendered) if bulk_render? end |
#name ⇒ Object
401 402 403 |
# File 'lib/glimmer/web/element_proxy.rb', line 401 def name self.class.name.split('::').last.underscore.sub(/_proxy$/, '').gsub('_', '-') end |
#notify_listeners(event) ⇒ Object
528 529 530 531 532 |
# File 'lib/glimmer/web/element_proxy.rb', line 528 def notify_listeners(event) listeners_for(event).each do |listener| listener.original_event_listener.call(EventProxy.new(listener: listener)) end end |
#notify_on_render_listeners ⇒ Object
534 535 536 537 |
# File 'lib/glimmer/web/element_proxy.rb', line 534 def notify_on_render_listeners notify_listeners('on_render') children.each(&:notify_on_render_listeners) if bulk_render? end |
#observation_request_to_event_mapping ⇒ Object
Subclasses must override with their own mappings
397 398 399 |
# File 'lib/glimmer/web/element_proxy.rb', line 397 def observation_request_to_event_mapping {} end |
#observation_requests ⇒ Object
TODO consider adding a default #dom method implementation for the common case, automatically relying on #element and other methods to build the dom html
462 463 464 |
# File 'lib/glimmer/web/element_proxy.rb', line 462 def observation_requests @observation_requests ||= {} end |
#parent_dom_element ⇒ Object
279 280 281 282 283 284 285 286 287 288 289 290 291 |
# File 'lib/glimmer/web/element_proxy.rb', line 279 def parent_dom_element if parent parent.dom_element else [:parent] ||= 'body' the_element = Document.find([:parent]) if the_element.length == 0 [:parent] = 'body' the_element = Document.find('body') end the_element end end |
#parent_selector ⇒ Object
275 276 277 |
# File 'lib/glimmer/web/element_proxy.rb', line 275 def parent_selector @parent&.selector end |
#parents ⇒ Object
227 228 229 230 231 232 233 234 235 |
# File 'lib/glimmer/web/element_proxy.rb', line 227 def parents parents_array = [] = self until .parent.nil? = .parent parents_array << end parents_array end |
#post_add_content ⇒ Object
Executes at the closing of a parent widget curly braces after all children/properties have been added/set
171 172 173 |
# File 'lib/glimmer/web/element_proxy.rb', line 171 def post_add_content render if bulk_render? && @parent.nil? && !rendered? end |
#post_initialize_child(child) ⇒ Object
Executes for the parent of a child that just got added
160 161 162 163 |
# File 'lib/glimmer/web/element_proxy.rb', line 160 def post_initialize_child(child) @children << child child.render if !bulk_render? && !render_after_create? end |
#post_remove_child(child) ⇒ Object
Executes for the parent of a child that just got removed
166 167 168 |
# File 'lib/glimmer/web/element_proxy.rb', line 166 def post_remove_child(child) @children.delete(child) end |
#post_render_method_calls ⇒ Object
663 664 665 |
# File 'lib/glimmer/web/element_proxy.rb', line 663 def post_render_method_calls @post_render_method_calls ||= [] end |
#print ⇒ Object
241 242 243 244 |
# File 'lib/glimmer/web/element_proxy.rb', line 241 def print `window.print()` true end |
#property_name_for(method_name) ⇒ Object
698 699 700 701 |
# File 'lib/glimmer/web/element_proxy.rb', line 698 def property_name_for(method_name) attribute_name = method_name.end_with?('=') ? method_name.to_s[0...-1] : method_name.to_s PROPERTY_ALIASES[attribute_name] || attribute_name.camelcase end |
#reattach(old_element) ⇒ Object
322 323 324 |
# File 'lib/glimmer/web/element_proxy.rb', line 322 def reattach(old_element) old_element.replace_with(@dom) end |
#remove ⇒ Object
179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 |
# File 'lib/glimmer/web/element_proxy.rb', line 179 def remove return if @removed on_remove_listeners = listeners_for('on_remove').dup if rendered? @children.dup.each do |child| child.remove end remove_all_listeners dom_element.remove end parent&.post_remove_child(self) if component Glimmer::Web::Component.remove_component(component) component.remove_style_block end @removed = true on_remove_listeners.each do |listener| listener.original_event_listener.call(EventProxy.new(listener: listener)) end end |
#remove_all_listeners ⇒ Object
200 201 202 203 204 205 206 207 208 209 |
# File 'lib/glimmer/web/element_proxy.rb', line 200 def remove_all_listeners listeners.each do |event, event_listeners| event_listeners.dup.each(&:unregister) end listeners.clear data_bindings.each do |element_binding, model_binding| element_binding.unregister_all_observables end data_bindings.clear end |
#remove_css_class(css_class) ⇒ Object
434 435 436 437 438 439 440 |
# File 'lib/glimmer/web/element_proxy.rb', line 434 def remove_css_class(css_class) if rendered? dom_element.remove_class(css_class) else enqueue_post_render_method_call('remove_css_class', css_class) end end |
#remove_css_classes(css_classes_to_remove) ⇒ Object
442 443 444 |
# File 'lib/glimmer/web/element_proxy.rb', line 442 def remove_css_classes(css_classes_to_remove) css_classes_to_remove.each {|css_class| remove_css_class(css_class)} end |
#remove_event_listener_proxies ⇒ Object
521 522 523 524 525 526 |
# File 'lib/glimmer/web/element_proxy.rb', line 521 def remove_event_listener_proxies event_listener_proxies.each do |event_listener_proxy| event_listener_proxy.unregister end event_listener_proxies.clear end |
#render(parent: nil, custom_parent_dom_element: nil, brand_new: false) ⇒ Object Also known as: rerender
293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 |
# File 'lib/glimmer/web/element_proxy.rb', line 293 def render(parent: nil, custom_parent_dom_element: nil, brand_new: false) parent_selector = parent [:parent] = parent_selector if !parent_selector.to_s.empty? if ![:parent].to_s.empty? # ensure element is orphaned as it is becoming a top-level root element @parent&.post_remove_child(self) @parent = nil end the_parent_dom_element = custom_parent_dom_element || parent_dom_element brand_new ||= @dom.nil? || ![:parent].to_s.empty? || (old_element = dom_element).empty? build_dom(layout: !custom_parent_dom_element) # TODO handle custom parent layout by passing parent instead of parent dom element if brand_new attach(the_parent_dom_element) else reattach(old_element) end mark_rendered invoke_post_render_method_calls if bulk_render? handle_observation_requests children.each(&:render) if !bulk_render? && !render_after_create? add_contents_for_render_blocks notify_on_render_listeners end |
#render_after_create? ⇒ Boolean
155 156 157 |
# File 'lib/glimmer/web/element_proxy.rb', line 155 def render_after_create? [:render] != false && (@parent.nil? || @parent.render_after_create?) end |
#respond_to_missing?(method_name, include_private = false) ⇒ Boolean
598 599 600 601 602 603 604 605 606 607 608 609 |
# File 'lib/glimmer/web/element_proxy.rb', line 598 def respond_to_missing?(method_name, include_private = false) # TODO consider doing more correct checking of availability of properties/methods using native ticks property_name = property_name_for(method_name) unnormalized_property_name = unnormalized_property_name_for(method_name) super(method_name, include_private) || (dom_element && dom_element.length > 0 && Native.call(dom_element, '0').respond_to?(method_name.to_s.camelcase, include_private)) || (dom_element && dom_element.length > 0 && Native.call(dom_element, '0').respond_to?(method_name.to_s, include_private)) || dom_element.respond_to?(method_name, include_private) || (!dom_element.prop(property_name).nil? && !dom_element.prop(property_name).is_a?(Proc)) || (!dom_element.prop(unnormalized_property_name).nil? && !dom_element.prop(unnormalized_property_name).is_a?(Proc)) || method_name.to_s.start_with?('on_') end |
#resume_event_handling ⇒ Object
474 475 476 |
# File 'lib/glimmer/web/element_proxy.rb', line 474 def resume_event_handling @event_handling_suspended = false end |
#selector ⇒ Object
Subclasses can override with their own selector
212 213 214 |
# File 'lib/glimmer/web/element_proxy.rb', line 212 def selector ".#{element_id}" end |
#shell ⇒ Object
221 222 223 224 225 |
# File 'lib/glimmer/web/element_proxy.rb', line 221 def shell = self = .parent until .parent.nil? end |
#skip_content_on_render_blocks? ⇒ Boolean
343 344 345 |
# File 'lib/glimmer/web/element_proxy.rb', line 343 def skip_content_on_render_blocks? false end |
#suspend_event_handling ⇒ Object
470 471 472 |
# File 'lib/glimmer/web/element_proxy.rb', line 470 def suspend_event_handling @event_handling_suspended = true end |
#swt_widget ⇒ Object
707 708 709 710 |
# File 'lib/glimmer/web/element_proxy.rb', line 707 def # only added for compatibility/adaptibility with Glimmer DSL for SWT self end |
#type ⇒ Object
543 544 545 546 547 548 549 |
# File 'lib/glimmer/web/element_proxy.rb', line 543 def type if rendered? super else [:type] || 'text' end end |
#unnormalized_property_name_for(method_name) ⇒ Object
703 704 705 |
# File 'lib/glimmer/web/element_proxy.rb', line 703 def unnormalized_property_name_for(method_name) method_name.end_with?('=') ? method_name.to_s[0...-1] : method_name.to_s end |
#value_converters_for_input_type(input_type) ⇒ Object
735 736 737 |
# File 'lib/glimmer/web/element_proxy.rb', line 735 def value_converters_for_input_type(input_type) input_value_converters[input_type] end |