Class: Glimmer::Web::ElementProxy

Inherits:
Object
  • Object
show all
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

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(keyword, parent, args, block) ⇒ ElementProxy

Returns a new instance of ElementProxy.



124
125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/glimmer/web/element_proxy.rb', line 124

def initialize(keyword, parent, args, block)
  @keyword = keyword
  @parent = parent
  @options = args.last.is_a?(Hash) ? args.last.symbolize_keys : {}
  if parent.nil?
    options[:parent] ||= Component.interpretation_stack.last&.options&.[](:parent)
    options[:render] ||= Component.interpretation_stack.last&.options&.[](:render)
  end
  @args = args
  @block = block
  @children = []
  @parent&.post_initialize_child(self)
  render if !@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



547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
# File 'lib/glimmer/web/element_proxy.rb', line 547

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)
    dom_element.send(method_name, *args, &block)
  elsif !dom_element.prop(property_name).nil? && !dom_element.prop(property_name).is_a?(Proc)
    if method_name.end_with?('=')
      dom_element.prop(property_name, *args)
    else
      dom_element.prop(property_name)
    end
  elsif !dom_element.prop(unnormalized_property_name).nil? && !dom_element.prop(unnormalized_property_name).is_a?(Proc)
    if method_name.end_with?('=')
      dom_element.prop(unnormalized_property_name, *args)
    else
      dom_element.prop(unnormalized_property_name)
    end
  elsif dom_element && dom_element.length > 0
    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
    super(method_name, *args, &block)
  end
end

Instance Attribute Details

#argsObject (readonly)

Returns the value of attribute args.



121
122
123
# File 'lib/glimmer/web/element_proxy.rb', line 121

def args
  @args
end

#backgroundObject

Returns the value of attribute background.



121
122
123
# File 'lib/glimmer/web/element_proxy.rb', line 121

def background
  @background
end

#childrenObject (readonly)

Returns the value of attribute children.



121
122
123
# File 'lib/glimmer/web/element_proxy.rb', line 121

def children
  @children
end

#enabledObject

Returns the value of attribute enabled.



121
122
123
# File 'lib/glimmer/web/element_proxy.rb', line 121

def enabled
  @enabled
end

#foregroundObject

Returns the value of attribute foreground.



121
122
123
# File 'lib/glimmer/web/element_proxy.rb', line 121

def foreground
  @foreground
end

#keywordObject (readonly)

Returns the value of attribute keyword.



121
122
123
# File 'lib/glimmer/web/element_proxy.rb', line 121

def keyword
  @keyword
end

#optionsObject (readonly)

Returns the value of attribute options.



121
122
123
# File 'lib/glimmer/web/element_proxy.rb', line 121

def options
  @options
end

#parentObject (readonly)

Returns the value of attribute parent.



121
122
123
# File 'lib/glimmer/web/element_proxy.rb', line 121

def parent
  @parent
end

#removed?Object (readonly)

Returns the value of attribute removed?.



121
122
123
# File 'lib/glimmer/web/element_proxy.rb', line 121

def removed?
  @removed?
end

#renderedObject (readonly) Also known as: rendered?

Returns the value of attribute rendered.



121
122
123
# File 'lib/glimmer/web/element_proxy.rb', line 121

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

Returns:

  • (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_numbersObject



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 underscored_widget_name(widget_proxy)
  widget_proxy.class.name.split(/::|\./).last.sub(/Proxy$/, '').underscore
end

.widget_handling_listenerObject



70
71
72
# File 'lib/glimmer/web/element_proxy.rb', line 70

def widget_handling_listener
  @@widget_handling_listener
end

Instance Method Details

#add_content_on_render(&content_block) ⇒ Object



318
319
320
321
322
323
324
# File 'lib/glimmer/web/element_proxy.rb', line 318

def add_content_on_render(&content_block)
  if rendered?
    content_block.call
  else
    content_on_render_blocks << content_block
  end
end

#add_css_class(css_class) ⇒ Object



380
381
382
# File 'lib/glimmer/web/element_proxy.rb', line 380

def add_css_class(css_class)
  dom_element.add_class(css_class)
end

#add_css_classes(css_classes_to_add) ⇒ Object



384
385
386
# File 'lib/glimmer/web/element_proxy.rb', line 384

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) ⇒ Object



306
307
308
# File 'lib/glimmer/web/element_proxy.rb', line 306

def add_text_content(text)
  dom_element.append(text.to_s)
end

#attach(the_parent_dom_element) ⇒ Object



298
299
300
# File 'lib/glimmer/web/element_proxy.rb', line 298

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



523
524
525
526
527
528
529
530
531
532
# File 'lib/glimmer/web/element_proxy.rb', line 523

def bind_content(*binding_args, &content_block)
  # TODO in the future, consider optimizing code by diffing content if that makes sense
  content_binding_work = proc do |*values|
    children.dup.each { |child| child.remove }
    content(&content_block)
  end
  content_binding_observer = Glimmer::DataBinding::Observer.proc(&content_binding_work)
  content_binding_observer.observe(*binding_args)
  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



326
327
328
329
# File 'lib/glimmer/web/element_proxy.rb', line 326

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

#can_handle_observation_request?(keyword) ⇒ Boolean

Returns:

  • (Boolean)


461
462
463
464
465
466
467
468
469
470
471
472
473
# File 'lib/glimmer/web/element_proxy.rb', line 461

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

#class_name=(value) ⇒ Object



374
375
376
377
378
# File 'lib/glimmer/web/element_proxy.rb', line 374

def class_name=(value)
  value = value.is_a?(Array) ? value.join(' ') : value.to_s
  new_class_name = "#{name} #{element_id} #{value}"
  dom_element.prop('className', new_class_name)
end

#clear_css_classesObject



396
397
398
# File 'lib/glimmer/web/element_proxy.rb', line 396

def clear_css_classes
  css_classes.each {|css_class| remove_css_class(css_class)}
end

#content(&block) ⇒ Object



354
355
356
# File 'lib/glimmer/web/element_proxy.rb', line 354

def content(&block)
  Glimmer::DSL::Engine.add_content(self, Glimmer::DSL::Web::ElementExpression.new, keyword, &block)
end

#content_on_render_blocksObject



310
311
312
# File 'lib/glimmer/web/element_proxy.rb', line 310

def content_on_render_blocks
  @content_on_render_blocks ||= []
end

#css_classesObject



160
161
162
# File 'lib/glimmer/web/element_proxy.rb', line 160

def css_classes
  dom_element.attr('class').to_s.split
end

#data_bind(property, model_binding) ⇒ Object



499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
# File 'lib/glimmer/web/element_proxy.rb', line 499

def data_bind(property, model_binding)
  element_binding_translator = value_converters_for_input_type(type)[:model_to_view]
  element_binding_parameters = [self, property, element_binding_translator]
  element_binding = DataBinding::ElementBinding.new(*element_binding_parameters)
  element_binding.call(model_binding.evaluate_property)
  #TODO make this options observer dependent and all similar observers in element specific data binding handlers
  element_binding.observe(model_binding)
  data_bindings[element_binding] = model_binding
  unless model_binding.binding_options[: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)
        converted_view_property_value = value_converters_for_input_type(type)[:view_to_model].call(view_property_value, model_binding.evaluate_property)
        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_mapObject



605
606
607
608
609
610
611
612
613
614
615
616
617
618
# File 'lib/glimmer/web/element_proxy.rb', line 605

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



597
598
599
# File 'lib/glimmer/web/element_proxy.rb', line 597

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



601
602
603
# File 'lib/glimmer/web/element_proxy.rb', line 601

def data_binding_property_listener_map_for_element(element_keyword)
  data_binding_element_keyword_to_property_listener_map[element_keyword] || {}
end

#data_bindingsObject



495
496
497
# File 'lib/glimmer/web/element_proxy.rb', line 495

def data_bindings
  @data_bindings ||= {}
end

#dialog_ancestorObject



215
216
217
# File 'lib/glimmer/web/element_proxy.rb', line 215

def dialog_ancestor
  parents.detect {|p| p.is_a?(DialogProxy)}
end

#domObject



331
332
333
334
335
336
337
338
# File 'lib/glimmer/web/element_proxy.rb', line 331

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 if args.first.is_a?(String)
    ElementProxy.render_html(keyword, html_options, content)
  end
end

#dom_elementObject



404
405
406
407
# File 'lib/glimmer/web/element_proxy.rb', line 404

def dom_element
  # 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)
end

#elementObject

Root element representing widget. Must be overridden by subclasses if different from div



195
196
197
# File 'lib/glimmer/web/element_proxy.rb', line 195

def element
  keyword
end

#element_idObject

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



370
371
372
# File 'lib/glimmer/web/element_proxy.rb', line 370

def element_id
  @element_id ||= "element-#{ElementProxy.next_id_number_for(name)}"
end

#event_handling_suspended?Boolean

Returns:

  • (Boolean)


449
450
451
# File 'lib/glimmer/web/element_proxy.rb', line 449

def event_handling_suspended?
  @event_handling_suspended
end

#event_listener_proxiesObject



437
438
439
# File 'lib/glimmer/web/element_proxy.rb', line 437

def event_listener_proxies
  @event_listener_proxies ||= []
end

#handle_observation_request(keyword, original_event_listener) ⇒ Object



475
476
477
478
479
480
481
482
483
484
485
486
# File 'lib/glimmer/web/element_proxy.rb', line 475

def handle_observation_request(keyword, original_event_listener)
  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
end

#has_style?(symbol) ⇒ Boolean

Returns:

  • (Boolean)


400
401
402
# File 'lib/glimmer/web/element_proxy.rb', line 400

def has_style?(symbol)
  @args.include?(symbol) # not a very solid implementation. Bring SWT constants eventually
end

#html_optionsObject



340
341
342
343
344
345
346
347
348
349
350
351
352
# File 'lib/glimmer/web/element_proxy.rb', line 340

def html_options
  body_class = ([name, element_id] + css_classes.to_a).join(' ')
  html_options = options.dup
  GLIMMER_ATTRIBUTES.each do |attribute|
    next unless html_options.include?(attribute)
    data_normalized_attribute = attribute.split('_').join('-')
    html_options["data-#{data_normalized_attribute}"] = html_options.delete(attribute)
  end
  html_options[:class] ||= ''
  html_options[:class] = "#{html_options[:class]} #{body_class}".strip
  html_options['data-turbo'] = 'false' if parent.nil?
  html_options
end

#input_value_convertersObject



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
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
# File 'lib/glimmer/web/element_proxy.rb', line 624

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

#listener_dom_elementObject



429
430
431
# File 'lib/glimmer/web/element_proxy.rb', line 429

def listener_dom_element
  Document.find(listener_selector)
end

#listener_selectorObject



425
426
427
# File 'lib/glimmer/web/element_proxy.rb', line 425

def listener_selector
  selector
end

#listenersObject



453
454
455
# File 'lib/glimmer/web/element_proxy.rb', line 453

def listeners
  @listeners ||= {}
end

#listeners_for(listener_event) ⇒ Object



457
458
459
# File 'lib/glimmer/web/element_proxy.rb', line 457

def listeners_for(listener_event)
  listeners[listener_event.to_s] ||= []
end

#nameObject



363
364
365
# File 'lib/glimmer/web/element_proxy.rb', line 363

def name
  self.class.name.split('::').last.underscore.sub(/_proxy$/, '').gsub('_', '-')
end

#observation_request_to_event_mappingObject

Subclasses must override with their own mappings



359
360
361
# File 'lib/glimmer/web/element_proxy.rb', line 359

def observation_request_to_event_mapping
  {}
end

#observation_requestsObject



433
434
435
# File 'lib/glimmer/web/element_proxy.rb', line 433

def observation_requests
  @observation_requests ||= {}
end

#parent_dom_elementObject



245
246
247
248
249
250
251
252
253
254
255
256
257
# File 'lib/glimmer/web/element_proxy.rb', line 245

def parent_dom_element
  if parent_selector
    Document.find(parent_selector)
  else
    options[:parent] ||= 'body'
    the_element = Document.find(options[:parent])
    if the_element.length == 0
      options[:parent] = 'body'
      the_element = Document.find('body')
    end
    the_element
  end
end

#parent_selectorObject



241
242
243
# File 'lib/glimmer/web/element_proxy.rb', line 241

def parent_selector
  @parent&.selector
end

#parentsObject



205
206
207
208
209
210
211
212
213
# File 'lib/glimmer/web/element_proxy.rb', line 205

def parents
  parents_array = []
  current_widget = self
  until current_widget.parent.nil?
    current_widget = current_widget.parent
    parents_array << current_widget
  end
  parents_array
end

#post_add_contentObject

Executes at the closing of a parent widget curly braces after all children/properties have been added/set



155
156
157
158
# File 'lib/glimmer/web/element_proxy.rb', line 155

def post_add_content
  # TODO double check every place we should call this method
  # No Op
end

#post_initialize_child(child) ⇒ Object

Executes for the parent of a child that just got added



144
145
146
147
# File 'lib/glimmer/web/element_proxy.rb', line 144

def post_initialize_child(child)
  @children << child
  child.render if !render_after_create?
end

#post_remove_child(child) ⇒ Object

Executes for the parent of a child that just got removed



150
151
152
# File 'lib/glimmer/web/element_proxy.rb', line 150

def post_remove_child(child)
  @children.delete(child)
end


219
220
221
222
# File 'lib/glimmer/web/element_proxy.rb', line 219

def print
  `window.print()`
  true
end

#property_name_for(method_name) ⇒ Object



583
584
585
586
# File 'lib/glimmer/web/element_proxy.rb', line 583

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



302
303
304
# File 'lib/glimmer/web/element_proxy.rb', line 302

def reattach(old_element)
  old_element.replace_with(@dom)
end

#removeObject



164
165
166
167
168
169
170
171
172
173
174
175
176
# File 'lib/glimmer/web/element_proxy.rb', line 164

def remove
  @children.dup.each do |child|
    child.remove
  end
  on_remove_listeners = listeners_for('on_remove').dup
  remove_all_listeners
  dom_element.remove
  parent&.post_remove_child(self)
  @removed = true
  on_remove_listeners.each do |listener|
    listener.original_event_listener.call(EventProxy.new(listener: listener))
  end
end

#remove_all_listenersObject



178
179
180
181
182
183
184
185
186
187
# File 'lib/glimmer/web/element_proxy.rb', line 178

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



388
389
390
# File 'lib/glimmer/web/element_proxy.rb', line 388

def remove_css_class(css_class)
  dom_element.remove_class(css_class)
end

#remove_css_classes(css_classes_to_remove) ⇒ Object



392
393
394
# File 'lib/glimmer/web/element_proxy.rb', line 392

def remove_css_classes(css_classes_to_remove)
  css_classes_to_remove.each {|css_class| remove_css_class(css_class)}
end

#remove_event_listener_proxiesObject



488
489
490
491
492
493
# File 'lib/glimmer/web/element_proxy.rb', line 488

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



259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
# File 'lib/glimmer/web/element_proxy.rb', line 259

def render(parent: nil, custom_parent_dom_element: nil, brand_new: false)
  parent_selector = parent
  options[:parent] = parent_selector if !parent_selector.to_s.empty?
  if !options[: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? || !options[: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
  observation_requests&.each do |keyword, event_listener_set|
    event_listener_set.each do |event_listener|
      handle_observation_request(keyword, event_listener)
    end
  end
  unless render_after_create?
    children.each do |child|
      child.render
    end
  end
  @rendered = true
  unless skip_content_on_render_blocks?
    content_on_render_blocks.each do |content_block|
      content(&content_block)
    end
  end
  # TODO replace following line with a method call like (`notify_listeners('on_render')`)
  listeners_for('on_render').each do |listener|
    listener.original_event_listener.call(EventProxy.new(listener: listener))
  end
end

#render_after_create?Boolean

Returns:

  • (Boolean)


139
140
141
# File 'lib/glimmer/web/element_proxy.rb', line 139

def render_after_create?
  options[:render] != false && (@parent.nil? || @parent.render_after_create?)
end

#respond_to_missing?(method_name, include_private = false) ⇒ Boolean

Returns:

  • (Boolean)


534
535
536
537
538
539
540
541
542
543
544
545
# File 'lib/glimmer/web/element_proxy.rb', line 534

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_handlingObject



445
446
447
# File 'lib/glimmer/web/element_proxy.rb', line 445

def resume_event_handling
  @event_handling_suspended = false
end

#selectorObject

Subclasses can override with their own selector



190
191
192
# File 'lib/glimmer/web/element_proxy.rb', line 190

def selector
  ".#{element_id}"
end

#shellObject



199
200
201
202
203
# File 'lib/glimmer/web/element_proxy.rb', line 199

def shell
  current_widget = self
  current_widget = current_widget.parent until current_widget.parent.nil?
  current_widget
end

#skip_content_on_render_blocks?Boolean

Returns:

  • (Boolean)


314
315
316
# File 'lib/glimmer/web/element_proxy.rb', line 314

def skip_content_on_render_blocks?
  false
end

#style_elementObject

TODO consider adding a default #dom method implementation for the common case, automatically relying on #element and other methods to build the dom html



411
412
413
414
415
416
417
418
419
420
421
422
423
# File 'lib/glimmer/web/element_proxy.rb', line 411

def style_element
  style_element_id = "#{id}-style"
  style_element_selector = "style##{style_element_id}"
  element = dom_element.find(style_element_selector)
  if element.empty?
    new_element = Element.new(:style)
    new_element.attr('id', style_element_id)
    new_element.attr('class', "#{name.gsub('_', '-')}-instance-style widget-instance-style")
    dom_element.prepend(new_element)
    element = dom_element.find(style_element_selector)
  end
  element
end

#suspend_event_handlingObject



441
442
443
# File 'lib/glimmer/web/element_proxy.rb', line 441

def suspend_event_handling
  @event_handling_suspended = true
end

#swt_widgetObject



592
593
594
595
# File 'lib/glimmer/web/element_proxy.rb', line 592

def swt_widget
  # only added for compatibility/adaptibility with Glimmer DSL for SWT
  self
end

#unnormalized_property_name_for(method_name) ⇒ Object



588
589
590
# File 'lib/glimmer/web/element_proxy.rb', line 588

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



620
621
622
# File 'lib/glimmer/web/element_proxy.rb', line 620

def value_converters_for_input_type(input_type)
  input_value_converters[input_type] || {model_to_view: ->(value, old_value) {value}, view_to_model: ->(value, old_value) {value}}
end