glimmer-dsl-web 0.0.2 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +9 -1
- data/README.md +370 -23
- data/VERSION +1 -1
- data/glimmer-dsl-web.gemspec +6 -3
- data/lib/glimmer/dsl/web/dsl.rb +3 -1
- data/lib/glimmer/dsl/web/element_expression.rb +2 -2
- data/lib/glimmer/dsl/web/listener_expression.rb +19 -0
- data/lib/glimmer/util/proc_tracker.rb +1 -1
- data/lib/glimmer/web/element_proxy.rb +98 -57
- data/lib/glimmer/web/listener_proxy.rb +37 -0
- data/lib/glimmer/web.rb +1 -1
- data/lib/glimmer-dsl-web/samples/hello/hello_button.rb +99 -0
- data/lib/glimmer-dsl-web/samples/hello/hello_world.rb +1 -1
- data/lib/glimmer-dsl-web.rb +1 -1
- metadata +5 -2
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 10f466c47fb515cb9cd28f7eb49920c1cb52ba68126ccd85a94c321f807b43a6
         | 
| 4 | 
            +
              data.tar.gz: ac50a3b1d5f3d96b4b6770d5a725f900840aca77da0ccef7c7773a31fa43c4df
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: e6fc2635c32e91c06843bb230b26e600eefda14c9e2b4b9ca22f4dcb26ebaf5e341dee90074fcedd356a506b2c9f169a5ff51104b3afba37bad6b80b9c570cad
         | 
| 7 | 
            +
              data.tar.gz: cccb3243bd7351b40564aacabf1c866271b5d6a53ddd0ebda4441b8b90274370469bfa3ba3f38b68d16827850df94fa57dfe7408e2e5091097a4120c4577cc6d
         | 
    
        data/CHANGELOG.md
    CHANGED
    
    | @@ -1,11 +1,19 @@ | |
| 1 1 | 
             
            # Change Log
         | 
| 2 2 |  | 
| 3 | 
            +
            ## 0.0.3
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            - Set Glimmer specific element attributes (e.g. `parent`) as data attributes on generated HTML elements
         | 
| 6 | 
            +
            - Support setting text content by passing as first argument to an element as an alternative to block return value
         | 
| 7 | 
            +
            - Proxy method/attribute invocations on an element to its HTML element (e.g. `input_element.check_validity` proxies to `checkValidity` JS function)
         | 
| 8 | 
            +
            - Support JS listeners like `onclick` by nesting an `on_someeventname` block under an element (e.g. `on_click { ... }`)
         | 
| 9 | 
            +
            - New Hello, Button! Sample: `require 'glimmer-dsl-web/samples/hello/hello_button'`
         | 
| 10 | 
            +
             | 
| 3 11 | 
             
            ## 0.0.2
         | 
| 4 12 |  | 
| 5 13 | 
             
            - Rename element `:root` option to `:parent` option
         | 
| 6 14 | 
             
            - Set `body` as parent by default if `:parent` option is not specified for a root element
         | 
| 7 | 
            -
            - `glimmer-dsl-web/samples/hello/hello_world.rb`
         | 
| 8 15 | 
             
            - Set `class` instead of `id` on generated HTML elements to identify them (e.g. `element-2`).
         | 
| 16 | 
            +
            - New Hello, World! Sample: `require 'glimmer-dsl-web/samples/hello/hello_world'`
         | 
| 9 17 |  | 
| 10 18 | 
             
            ## 0.0.1
         | 
| 11 19 |  | 
    
        data/README.md
    CHANGED
    
    | @@ -1,4 +1,4 @@ | |
| 1 | 
            -
            # [<img src="https://raw.githubusercontent.com/AndyObtiva/glimmer/master/images/glimmer-logo-hi-res.png" height=85 />](https://github.com/AndyObtiva/glimmer) Glimmer DSL for Web 0.0. | 
| 1 | 
            +
            # [<img src="https://raw.githubusercontent.com/AndyObtiva/glimmer/master/images/glimmer-logo-hi-res.png" height=85 />](https://github.com/AndyObtiva/glimmer) Glimmer DSL for Web 0.0.3 (Early Alpha)
         | 
| 2 2 | 
             
            ## Ruby in the Browser Web GUI Library
         | 
| 3 3 | 
             
            [](http://badge.fury.io/rb/glimmer-dsl-web)
         | 
| 4 4 | 
             
            [](https://gitter.im/AndyObtiva/glimmer?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
         | 
| @@ -29,12 +29,14 @@ require 'glimmer-dsl-web' | |
| 29 29 |  | 
| 30 30 | 
             
            include Glimmer
         | 
| 31 31 |  | 
| 32 | 
            -
             | 
| 33 | 
            -
             | 
| 34 | 
            -
               | 
| 35 | 
            -
                ' | 
| 36 | 
            -
             | 
| 37 | 
            -
            }
         | 
| 32 | 
            +
            Document.ready? do
         | 
| 33 | 
            +
              # This will hook into element #app-container and then build HTML inside it using Ruby DSL code
         | 
| 34 | 
            +
              div(parent: '#app-container') {
         | 
| 35 | 
            +
                label(class: 'greeting') {
         | 
| 36 | 
            +
                  'Hello, World!'
         | 
| 37 | 
            +
                }
         | 
| 38 | 
            +
              }.render
         | 
| 39 | 
            +
            end
         | 
| 38 40 | 
             
            ```
         | 
| 39 41 |  | 
| 40 42 | 
             
            That produces:
         | 
| @@ -51,6 +53,8 @@ That produces: | |
| 51 53 | 
             
            ...
         | 
| 52 54 | 
             
            ```
         | 
| 53 55 |  | 
| 56 | 
            +
            
         | 
| 57 | 
            +
             | 
| 54 58 | 
             
            **Hello, World! Sample**
         | 
| 55 59 |  | 
| 56 60 | 
             
            Glimmer GUI code:
         | 
| @@ -75,8 +79,178 @@ That produces the following under `<body></body>`: | |
| 75 79 | 
             
            </div>
         | 
| 76 80 | 
             
            ```
         | 
| 77 81 |  | 
| 82 | 
            +
            
         | 
| 83 | 
            +
             | 
| 78 84 | 
             
            **Hello, Button! Sample**
         | 
| 79 85 |  | 
| 86 | 
            +
            Glimmer GUI code:
         | 
| 87 | 
            +
             | 
| 88 | 
            +
            ```ruby
         | 
| 89 | 
            +
            require 'glimmer-dsl-web'
         | 
| 90 | 
            +
             | 
| 91 | 
            +
            include Glimmer
         | 
| 92 | 
            +
             | 
| 93 | 
            +
            Document.ready? do
         | 
| 94 | 
            +
              div {
         | 
| 95 | 
            +
                h1('Contact Form')
         | 
| 96 | 
            +
                form {
         | 
| 97 | 
            +
                  div(class: 'field-row') {
         | 
| 98 | 
            +
                    label('Name: ', for: 'name-field')
         | 
| 99 | 
            +
                    @name_input = input(id: 'name-field', class: 'field', type: 'text', required: true)
         | 
| 100 | 
            +
                  }
         | 
| 101 | 
            +
                  div(class: 'field-row') {
         | 
| 102 | 
            +
                    label('Email: ', for: 'email-field')
         | 
| 103 | 
            +
                    @email_input = input(id: 'email-field', class: 'field', type: 'email', required: true)
         | 
| 104 | 
            +
                  }
         | 
| 105 | 
            +
                  @add_button = button('Add Contact', class: 'submit-button') {
         | 
| 106 | 
            +
                    on_click do
         | 
| 107 | 
            +
                      if ([@name_input, @email_input].all? {|input| input.check_validity })
         | 
| 108 | 
            +
                        @table.content {
         | 
| 109 | 
            +
                          tr {
         | 
| 110 | 
            +
                            td { @name_input.value }
         | 
| 111 | 
            +
                            td { @email_input.value }
         | 
| 112 | 
            +
                          }
         | 
| 113 | 
            +
                        }
         | 
| 114 | 
            +
                        @email_input.value = @name_input.value = ''
         | 
| 115 | 
            +
                      else
         | 
| 116 | 
            +
                        error_messages = []
         | 
| 117 | 
            +
                        error_messages << "Name is not valid! Make sure it is filled." if !@name_input.check_validity
         | 
| 118 | 
            +
                        error_messages << "Email is not valid! Make sure it is filled and has a valid format." if !@email_input.check_validity
         | 
| 119 | 
            +
                        $$.alert(error_messages.join("\n"))
         | 
| 120 | 
            +
                      end
         | 
| 121 | 
            +
                    end
         | 
| 122 | 
            +
                  }
         | 
| 123 | 
            +
                }
         | 
| 124 | 
            +
                h1('Contacts Table')
         | 
| 125 | 
            +
                @table = table {
         | 
| 126 | 
            +
                  tr {
         | 
| 127 | 
            +
                    th('Name')
         | 
| 128 | 
            +
                    th('Email')
         | 
| 129 | 
            +
                  }
         | 
| 130 | 
            +
                  tr {
         | 
| 131 | 
            +
                    td('John Doe')
         | 
| 132 | 
            +
                    td('johndoe@example.com')
         | 
| 133 | 
            +
                  }
         | 
| 134 | 
            +
                  tr {
         | 
| 135 | 
            +
                    td('Jane Doe')
         | 
| 136 | 
            +
                    td('janedoe@example.com')
         | 
| 137 | 
            +
                  }
         | 
| 138 | 
            +
                }
         | 
| 139 | 
            +
                
         | 
| 140 | 
            +
                # CSS Styles
         | 
| 141 | 
            +
                style {
         | 
| 142 | 
            +
                  <<~CSS
         | 
| 143 | 
            +
                    .field-row {
         | 
| 144 | 
            +
                      margin: 10px 5px;
         | 
| 145 | 
            +
                    }
         | 
| 146 | 
            +
                    .field {
         | 
| 147 | 
            +
                      margin-left: 5px;
         | 
| 148 | 
            +
                    }
         | 
| 149 | 
            +
                    .submit-button {
         | 
| 150 | 
            +
                      display: block;
         | 
| 151 | 
            +
                      margin: 10px 5px;
         | 
| 152 | 
            +
                    }
         | 
| 153 | 
            +
                    table {
         | 
| 154 | 
            +
                      border:1px solid grey;
         | 
| 155 | 
            +
                      border-spacing: 0;
         | 
| 156 | 
            +
                    }
         | 
| 157 | 
            +
                    table tr td, table tr th {
         | 
| 158 | 
            +
                      padding: 5px;
         | 
| 159 | 
            +
                    }
         | 
| 160 | 
            +
                    table tr:nth-child(even) {
         | 
| 161 | 
            +
                      background: #ccc;
         | 
| 162 | 
            +
                    }
         | 
| 163 | 
            +
                  CSS
         | 
| 164 | 
            +
                }
         | 
| 165 | 
            +
              }.render
         | 
| 166 | 
            +
            end
         | 
| 167 | 
            +
            ```
         | 
| 168 | 
            +
             | 
| 169 | 
            +
            That produces the following under `<body></body>`:
         | 
| 170 | 
            +
             | 
| 171 | 
            +
            ```html
         | 
| 172 | 
            +
            <div data-parent="body" class="element element-1">
         | 
| 173 | 
            +
              <h1 class="element element-2">Contact Form</h1>
         | 
| 174 | 
            +
              <form class="element element-3">
         | 
| 175 | 
            +
                <div class="field-row element element-4">
         | 
| 176 | 
            +
                  <label for="name-field" class="element element-5">Name: </label>
         | 
| 177 | 
            +
                  <input id="name-field" class="field element element-6" type="text" required="true">
         | 
| 178 | 
            +
                </div>
         | 
| 179 | 
            +
                <div class="field-row element element-7">
         | 
| 180 | 
            +
                  <label for="email-field" class="element element-8">Email: </label>
         | 
| 181 | 
            +
                  <input id="email-field" class="field element element-9" type="email" required="true">
         | 
| 182 | 
            +
                </div>
         | 
| 183 | 
            +
                <button class="submit-button element element-10">Add Contact</button>
         | 
| 184 | 
            +
              </form>
         | 
| 185 | 
            +
              <h1 class="element element-11">Contacts Table</h1>
         | 
| 186 | 
            +
              <table class="element element-12">
         | 
| 187 | 
            +
                <tr class="element element-13">
         | 
| 188 | 
            +
                  <th class="element element-14">Name</th>
         | 
| 189 | 
            +
                  <th class="element element-15">Email</th>
         | 
| 190 | 
            +
                </tr>
         | 
| 191 | 
            +
                <tr class="element element-16">
         | 
| 192 | 
            +
                  <td class="element element-17">John Doe</td>
         | 
| 193 | 
            +
                  <td class="element element-18">johndoe@example.com</td>
         | 
| 194 | 
            +
                </tr>
         | 
| 195 | 
            +
                <tr class="element element-19">
         | 
| 196 | 
            +
                  <td class="element element-20">Jane Doe</td>
         | 
| 197 | 
            +
                  <td class="element element-21">janedoe@example.com</td>
         | 
| 198 | 
            +
                </tr>
         | 
| 199 | 
            +
              </table>
         | 
| 200 | 
            +
              <style class="element element-22">.field-row {
         | 
| 201 | 
            +
                margin: 10px 5px;
         | 
| 202 | 
            +
              }
         | 
| 203 | 
            +
              .field {
         | 
| 204 | 
            +
                margin-left: 5px;
         | 
| 205 | 
            +
              }
         | 
| 206 | 
            +
              .submit-button {
         | 
| 207 | 
            +
                display: block;
         | 
| 208 | 
            +
                margin: 10px 5px;
         | 
| 209 | 
            +
              }
         | 
| 210 | 
            +
              table {
         | 
| 211 | 
            +
                border:1px solid grey;
         | 
| 212 | 
            +
                border-spacing: 0;
         | 
| 213 | 
            +
              }
         | 
| 214 | 
            +
              table tr td, table tr th {
         | 
| 215 | 
            +
                padding: 5px;
         | 
| 216 | 
            +
              }
         | 
| 217 | 
            +
              table tr:nth-child(even) {
         | 
| 218 | 
            +
                background: #ccc;
         | 
| 219 | 
            +
              }
         | 
| 220 | 
            +
              </style>
         | 
| 221 | 
            +
            </div>
         | 
| 222 | 
            +
            ```
         | 
| 223 | 
            +
             | 
| 224 | 
            +
            Screenshots:
         | 
| 225 | 
            +
             | 
| 226 | 
            +
            ---
         | 
| 227 | 
            +
             | 
| 228 | 
            +
            ***Hello, Button!***
         | 
| 229 | 
            +
             | 
| 230 | 
            +
            
         | 
| 231 | 
            +
             | 
| 232 | 
            +
            ---
         | 
| 233 | 
            +
             | 
| 234 | 
            +
            ***Hello, Button! Submitted Invalid Data***
         | 
| 235 | 
            +
             | 
| 236 | 
            +
            
         | 
| 237 | 
            +
             | 
| 238 | 
            +
            ---
         | 
| 239 | 
            +
             | 
| 240 | 
            +
            ***Hello, Button! Filled Valid Name and Email***
         | 
| 241 | 
            +
             | 
| 242 | 
            +
            
         | 
| 243 | 
            +
             | 
| 244 | 
            +
            ---
         | 
| 245 | 
            +
             | 
| 246 | 
            +
            ***Hello, Button! Added Contact***
         | 
| 247 | 
            +
             | 
| 248 | 
            +
            
         | 
| 249 | 
            +
             | 
| 250 | 
            +
            ---
         | 
| 251 | 
            +
             | 
| 252 | 
            +
            **Button Counter Sample**
         | 
| 253 | 
            +
             | 
| 80 254 | 
             
            **UPCOMING (NOT RELEASED OR SUPPORTED YET)**
         | 
| 81 255 |  | 
| 82 256 | 
             
            Glimmer GUI code demonstrating MVC + Glimmer Web Components (Views) + Data-Binding:
         | 
| @@ -106,7 +280,7 @@ class HelloButton | |
| 106 280 | 
             
              markup {
         | 
| 107 281 | 
             
                # This will hook into element #app-container and then build HTML inside it using Ruby DSL code
         | 
| 108 282 | 
             
                div(root_css_selector) {
         | 
| 109 | 
            -
                  text ' | 
| 283 | 
            +
                  text 'Button Counter'
         | 
| 110 284 |  | 
| 111 285 | 
             
                  button {
         | 
| 112 286 | 
             
                    # Unidirectional Data-Binding indicating that on every change to @counter.count, the value
         | 
| @@ -175,6 +349,7 @@ Learn more about the differences between various [Glimmer](https://github.com/An | |
| 175 349 | 
             
                - [Hello Samples](#hello-samples)
         | 
| 176 350 | 
             
                  - [Hello, World!](#hello-world)
         | 
| 177 351 | 
             
                  - [Hello, Button!](#hello-button)
         | 
| 352 | 
            +
                  - [Button Counter](#button-counter)
         | 
| 178 353 | 
             
              - [Glimmer Process](#glimmer-process)
         | 
| 179 354 | 
             
              - [Help](#help)
         | 
| 180 355 | 
             
                - [Issues](#issues)
         | 
| @@ -221,7 +396,7 @@ gem 'opal', '1.4.1' | |
| 221 396 | 
             
            gem 'opal-rails', '2.0.2'
         | 
| 222 397 | 
             
            gem 'opal-async', '~> 1.4.0'
         | 
| 223 398 | 
             
            gem 'opal-jquery', '~> 0.4.6'
         | 
| 224 | 
            -
            gem 'glimmer-dsl-web', '~> 0.0. | 
| 399 | 
            +
            gem 'glimmer-dsl-web', '~> 0.0.3'
         | 
| 225 400 | 
             
            gem 'glimmer-dsl-xml', '~> 1.3.1', require: false
         | 
| 226 401 | 
             
            gem 'glimmer-dsl-css', '~> 1.2.1', require: false
         | 
| 227 402 | 
             
            ```
         | 
| @@ -298,12 +473,14 @@ require 'glimmer-dsl-web' | |
| 298 473 |  | 
| 299 474 | 
             
            include Glimmer
         | 
| 300 475 |  | 
| 301 | 
            -
             | 
| 302 | 
            -
             | 
| 303 | 
            -
               | 
| 304 | 
            -
                ' | 
| 305 | 
            -
             | 
| 306 | 
            -
            }
         | 
| 476 | 
            +
            Document.ready? do
         | 
| 477 | 
            +
              # This will hook into element #app-container and then build HTML inside it using Ruby DSL code
         | 
| 478 | 
            +
              div(parent: '#app-container') {
         | 
| 479 | 
            +
                label(class: 'greeting') {
         | 
| 480 | 
            +
                  'Hello, World!'
         | 
| 481 | 
            +
                }
         | 
| 482 | 
            +
              }.render
         | 
| 483 | 
            +
            end
         | 
| 307 484 | 
             
            ```
         | 
| 308 485 |  | 
| 309 486 | 
             
            That produces:
         | 
| @@ -364,7 +541,7 @@ gem 'opal', '1.4.1' | |
| 364 541 | 
             
            gem 'opal-rails', '2.0.2'
         | 
| 365 542 | 
             
            gem 'opal-async', '~> 1.4.0'
         | 
| 366 543 | 
             
            gem 'opal-jquery', '~> 0.4.6'
         | 
| 367 | 
            -
            gem 'glimmer-dsl-web', '~> 0.0. | 
| 544 | 
            +
            gem 'glimmer-dsl-web', '~> 0.0.3'
         | 
| 368 545 | 
             
            gem 'glimmer-dsl-xml', '~> 1.3.1', require: false
         | 
| 369 546 | 
             
            gem 'glimmer-dsl-css', '~> 1.2.1', require: false
         | 
| 370 547 | 
             
            ```
         | 
| @@ -445,12 +622,14 @@ require 'glimmer-dsl-web' | |
| 445 622 |  | 
| 446 623 | 
             
            include Glimmer
         | 
| 447 624 |  | 
| 448 | 
            -
             | 
| 449 | 
            -
             | 
| 450 | 
            -
               | 
| 451 | 
            -
                ' | 
| 452 | 
            -
             | 
| 453 | 
            -
            }
         | 
| 625 | 
            +
            Document.ready? do
         | 
| 626 | 
            +
              # This will hook into element #app-container and then build HTML inside it using Ruby DSL code
         | 
| 627 | 
            +
              div(parent: '#app-container') {
         | 
| 628 | 
            +
                label(class: 'greeting') {
         | 
| 629 | 
            +
                  'Hello, World!'
         | 
| 630 | 
            +
                }
         | 
| 631 | 
            +
              }.render
         | 
| 632 | 
            +
            end
         | 
| 454 633 | 
             
            ```
         | 
| 455 634 |  | 
| 456 635 | 
             
            That produces:
         | 
| @@ -526,6 +705,174 @@ That produces the following under `<body></body>`: | |
| 526 705 |  | 
| 527 706 | 
             
            #### Hello, Button!
         | 
| 528 707 |  | 
| 708 | 
            +
            Glimmer GUI code:
         | 
| 709 | 
            +
             | 
| 710 | 
            +
            ```ruby
         | 
| 711 | 
            +
            require 'glimmer-dsl-web'
         | 
| 712 | 
            +
             | 
| 713 | 
            +
            include Glimmer
         | 
| 714 | 
            +
             | 
| 715 | 
            +
            Document.ready? do
         | 
| 716 | 
            +
              div {
         | 
| 717 | 
            +
                h1('Contact Form')
         | 
| 718 | 
            +
                form {
         | 
| 719 | 
            +
                  div(class: 'field-row') {
         | 
| 720 | 
            +
                    label('Name: ', for: 'name-field')
         | 
| 721 | 
            +
                    @name_input = input(id: 'name-field', class: 'field', type: 'text', required: true)
         | 
| 722 | 
            +
                  }
         | 
| 723 | 
            +
                  div(class: 'field-row') {
         | 
| 724 | 
            +
                    label('Email: ', for: 'email-field')
         | 
| 725 | 
            +
                    @email_input = input(id: 'email-field', class: 'field', type: 'email', required: true)
         | 
| 726 | 
            +
                  }
         | 
| 727 | 
            +
                  @add_button = button('Add Contact', class: 'submit-button') {
         | 
| 728 | 
            +
                    on_click do
         | 
| 729 | 
            +
                      if ([@name_input, @email_input].all? {|input| input.check_validity })
         | 
| 730 | 
            +
                        @table.content {
         | 
| 731 | 
            +
                          tr {
         | 
| 732 | 
            +
                            td { @name_input.value }
         | 
| 733 | 
            +
                            td { @email_input.value }
         | 
| 734 | 
            +
                          }
         | 
| 735 | 
            +
                        }
         | 
| 736 | 
            +
                        @email_input.value = @name_input.value = ''
         | 
| 737 | 
            +
                      else
         | 
| 738 | 
            +
                        error_messages = []
         | 
| 739 | 
            +
                        error_messages << "Name is not valid! Make sure it is filled." if !@name_input.check_validity
         | 
| 740 | 
            +
                        error_messages << "Email is not valid! Make sure it is filled and has a valid format." if !@email_input.check_validity
         | 
| 741 | 
            +
                        $$.alert(error_messages.join("\n"))
         | 
| 742 | 
            +
                      end
         | 
| 743 | 
            +
                    end
         | 
| 744 | 
            +
                  }
         | 
| 745 | 
            +
                }
         | 
| 746 | 
            +
                h1('Contacts Table')
         | 
| 747 | 
            +
                @table = table {
         | 
| 748 | 
            +
                  tr {
         | 
| 749 | 
            +
                    th('Name')
         | 
| 750 | 
            +
                    th('Email')
         | 
| 751 | 
            +
                  }
         | 
| 752 | 
            +
                  tr {
         | 
| 753 | 
            +
                    td('John Doe')
         | 
| 754 | 
            +
                    td('johndoe@example.com')
         | 
| 755 | 
            +
                  }
         | 
| 756 | 
            +
                  tr {
         | 
| 757 | 
            +
                    td('Jane Doe')
         | 
| 758 | 
            +
                    td('janedoe@example.com')
         | 
| 759 | 
            +
                  }
         | 
| 760 | 
            +
                }
         | 
| 761 | 
            +
                
         | 
| 762 | 
            +
                # CSS Styles
         | 
| 763 | 
            +
                style {
         | 
| 764 | 
            +
                  <<~CSS
         | 
| 765 | 
            +
                    .field-row {
         | 
| 766 | 
            +
                      margin: 10px 5px;
         | 
| 767 | 
            +
                    }
         | 
| 768 | 
            +
                    .field {
         | 
| 769 | 
            +
                      margin-left: 5px;
         | 
| 770 | 
            +
                    }
         | 
| 771 | 
            +
                    .submit-button {
         | 
| 772 | 
            +
                      display: block;
         | 
| 773 | 
            +
                      margin: 10px 5px;
         | 
| 774 | 
            +
                    }
         | 
| 775 | 
            +
                    table {
         | 
| 776 | 
            +
                      border:1px solid grey;
         | 
| 777 | 
            +
                      border-spacing: 0;
         | 
| 778 | 
            +
                    }
         | 
| 779 | 
            +
                    table tr td, table tr th {
         | 
| 780 | 
            +
                      padding: 5px;
         | 
| 781 | 
            +
                    }
         | 
| 782 | 
            +
                    table tr:nth-child(even) {
         | 
| 783 | 
            +
                      background: #ccc;
         | 
| 784 | 
            +
                    }
         | 
| 785 | 
            +
                  CSS
         | 
| 786 | 
            +
                }
         | 
| 787 | 
            +
              }.render
         | 
| 788 | 
            +
            end
         | 
| 789 | 
            +
            ```
         | 
| 790 | 
            +
             | 
| 791 | 
            +
            That produces the following under `<body></body>`:
         | 
| 792 | 
            +
             | 
| 793 | 
            +
            ```html
         | 
| 794 | 
            +
            <div data-parent="body" class="element element-1">
         | 
| 795 | 
            +
              <h1 class="element element-2">Contact Form</h1>
         | 
| 796 | 
            +
              <form class="element element-3">
         | 
| 797 | 
            +
                <div class="field-row element element-4">
         | 
| 798 | 
            +
                  <label for="name-field" class="element element-5">Name: </label>
         | 
| 799 | 
            +
                  <input id="name-field" class="field element element-6" type="text" required="true">
         | 
| 800 | 
            +
                </div>
         | 
| 801 | 
            +
                <div class="field-row element element-7">
         | 
| 802 | 
            +
                  <label for="email-field" class="element element-8">Email: </label>
         | 
| 803 | 
            +
                  <input id="email-field" class="field element element-9" type="email" required="true">
         | 
| 804 | 
            +
                </div>
         | 
| 805 | 
            +
                <button class="submit-button element element-10">Add Contact</button>
         | 
| 806 | 
            +
              </form>
         | 
| 807 | 
            +
              <h1 class="element element-11">Contacts Table</h1>
         | 
| 808 | 
            +
              <table class="element element-12">
         | 
| 809 | 
            +
                <tr class="element element-13">
         | 
| 810 | 
            +
                  <th class="element element-14">Name</th>
         | 
| 811 | 
            +
                  <th class="element element-15">Email</th>
         | 
| 812 | 
            +
                </tr>
         | 
| 813 | 
            +
                <tr class="element element-16">
         | 
| 814 | 
            +
                  <td class="element element-17">John Doe</td>
         | 
| 815 | 
            +
                  <td class="element element-18">johndoe@example.com</td>
         | 
| 816 | 
            +
                </tr>
         | 
| 817 | 
            +
                <tr class="element element-19">
         | 
| 818 | 
            +
                  <td class="element element-20">Jane Doe</td>
         | 
| 819 | 
            +
                  <td class="element element-21">janedoe@example.com</td>
         | 
| 820 | 
            +
                </tr>
         | 
| 821 | 
            +
              </table>
         | 
| 822 | 
            +
              <style class="element element-22">.field-row {
         | 
| 823 | 
            +
                margin: 10px 5px;
         | 
| 824 | 
            +
              }
         | 
| 825 | 
            +
              .field {
         | 
| 826 | 
            +
                margin-left: 5px;
         | 
| 827 | 
            +
              }
         | 
| 828 | 
            +
              .submit-button {
         | 
| 829 | 
            +
                display: block;
         | 
| 830 | 
            +
                margin: 10px 5px;
         | 
| 831 | 
            +
              }
         | 
| 832 | 
            +
              table {
         | 
| 833 | 
            +
                border:1px solid grey;
         | 
| 834 | 
            +
                border-spacing: 0;
         | 
| 835 | 
            +
              }
         | 
| 836 | 
            +
              table tr td, table tr th {
         | 
| 837 | 
            +
                padding: 5px;
         | 
| 838 | 
            +
              }
         | 
| 839 | 
            +
              table tr:nth-child(even) {
         | 
| 840 | 
            +
                background: #ccc;
         | 
| 841 | 
            +
              }
         | 
| 842 | 
            +
              </style>
         | 
| 843 | 
            +
            </div>
         | 
| 844 | 
            +
            ```
         | 
| 845 | 
            +
             | 
| 846 | 
            +
            Screenshots:
         | 
| 847 | 
            +
             | 
| 848 | 
            +
            ---
         | 
| 849 | 
            +
             | 
| 850 | 
            +
            ***Hello, Button!***
         | 
| 851 | 
            +
             | 
| 852 | 
            +
            
         | 
| 853 | 
            +
             | 
| 854 | 
            +
            ---
         | 
| 855 | 
            +
             | 
| 856 | 
            +
            ***Hello, Button! Submitted Invalid Data***
         | 
| 857 | 
            +
             | 
| 858 | 
            +
            
         | 
| 859 | 
            +
             | 
| 860 | 
            +
            ---
         | 
| 861 | 
            +
             | 
| 862 | 
            +
            ***Hello, Button! Filled Valid Name and Email***
         | 
| 863 | 
            +
             | 
| 864 | 
            +
            
         | 
| 865 | 
            +
             | 
| 866 | 
            +
            ---
         | 
| 867 | 
            +
             | 
| 868 | 
            +
            ***Hello, Button! Added Contact***
         | 
| 869 | 
            +
             | 
| 870 | 
            +
            
         | 
| 871 | 
            +
             | 
| 872 | 
            +
            ---
         | 
| 873 | 
            +
             | 
| 874 | 
            +
            #### Button Counter
         | 
| 875 | 
            +
             | 
| 529 876 | 
             
            **UPCOMING (NOT RELEASED OR SUPPORTED YET)**
         | 
| 530 877 |  | 
| 531 878 | 
             
            Glimmer GUI code demonstrating MVC + Glimmer Web Components (Views) + Data-Binding:
         | 
| @@ -555,7 +902,7 @@ class HelloButton | |
| 555 902 | 
             
              markup {
         | 
| 556 903 | 
             
                # This will hook into element #app-container and then build HTML inside it using Ruby DSL code
         | 
| 557 904 | 
             
                div(root_css_selector) {
         | 
| 558 | 
            -
                  text ' | 
| 905 | 
            +
                  text 'Button Counter'
         | 
| 559 906 |  | 
| 560 907 | 
             
                  button {
         | 
| 561 908 | 
             
                    # Unidirectional Data-Binding indicating that on every change to @counter.count, the value
         | 
    
        data/VERSION
    CHANGED
    
    | @@ -1 +1 @@ | |
| 1 | 
            -
            0.0. | 
| 1 | 
            +
            0.0.3
         | 
    
        data/glimmer-dsl-web.gemspec
    CHANGED
    
    | @@ -2,16 +2,16 @@ | |
| 2 2 | 
             
            # DO NOT EDIT THIS FILE DIRECTLY
         | 
| 3 3 | 
             
            # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
         | 
| 4 4 | 
             
            # -*- encoding: utf-8 -*-
         | 
| 5 | 
            -
            # stub: glimmer-dsl-web 0.0. | 
| 5 | 
            +
            # stub: glimmer-dsl-web 0.0.3 ruby lib
         | 
| 6 6 |  | 
| 7 7 | 
             
            Gem::Specification.new do |s|
         | 
| 8 8 | 
             
              s.name = "glimmer-dsl-web".freeze
         | 
| 9 | 
            -
              s.version = "0.0. | 
| 9 | 
            +
              s.version = "0.0.3".freeze
         | 
| 10 10 |  | 
| 11 11 | 
             
              s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
         | 
| 12 12 | 
             
              s.require_paths = ["lib".freeze]
         | 
| 13 13 | 
             
              s.authors = ["Andy Maleh".freeze]
         | 
| 14 | 
            -
              s.date = "2023-12- | 
| 14 | 
            +
              s.date = "2023-12-28"
         | 
| 15 15 | 
             
              s.description = "Glimmer DSL for Web (Ruby in the Browser Web GUI Library) - Enables frontend GUI development with Ruby by adopting a DSL that follows web-like HTML syntax, enabling the transfer of HTML/CSS/JS skills to Ruby frontend development. This library relies on Opal Ruby.".freeze
         | 
| 16 16 | 
             
              s.email = "andy.am@gmail.com".freeze
         | 
| 17 17 | 
             
              s.extra_rdoc_files = [
         | 
| @@ -30,6 +30,7 @@ Gem::Specification.new do |s| | |
| 30 30 | 
             
                "lib/glimmer-dsl-web/ext/class.rb",
         | 
| 31 31 | 
             
                "lib/glimmer-dsl-web/ext/date.rb",
         | 
| 32 32 | 
             
                "lib/glimmer-dsl-web/ext/exception.rb",
         | 
| 33 | 
            +
                "lib/glimmer-dsl-web/samples/hello/hello_button.rb",
         | 
| 33 34 | 
             
                "lib/glimmer-dsl-web/samples/hello/hello_world.rb",
         | 
| 34 35 | 
             
                "lib/glimmer-dsl-web/vendor/jquery.js",
         | 
| 35 36 | 
             
                "lib/glimmer/config/opal_logger.rb",
         | 
| @@ -37,9 +38,11 @@ Gem::Specification.new do |s| | |
| 37 38 | 
             
                "lib/glimmer/data_binding/observable_element.rb",
         | 
| 38 39 | 
             
                "lib/glimmer/dsl/web/dsl.rb",
         | 
| 39 40 | 
             
                "lib/glimmer/dsl/web/element_expression.rb",
         | 
| 41 | 
            +
                "lib/glimmer/dsl/web/listener_expression.rb",
         | 
| 40 42 | 
             
                "lib/glimmer/util/proc_tracker.rb",
         | 
| 41 43 | 
             
                "lib/glimmer/web.rb",
         | 
| 42 44 | 
             
                "lib/glimmer/web/element_proxy.rb",
         | 
| 45 | 
            +
                "lib/glimmer/web/listener_proxy.rb",
         | 
| 43 46 | 
             
                "lib/glimmer/web/property_owner.rb"
         | 
| 44 47 | 
             
              ]
         | 
| 45 48 | 
             
              s.homepage = "http://github.com/AndyObtiva/glimmer-dsl-web".freeze
         | 
    
        data/lib/glimmer/dsl/web/dsl.rb
    CHANGED
    
    | @@ -1,13 +1,14 @@ | |
| 1 1 | 
             
            require 'glimmer/dsl/engine'
         | 
| 2 2 | 
             
            # Dir[File.expand_path('../*_expression.rb', __FILE__)].each {|f| require f}
         | 
| 3 3 | 
             
            require 'glimmer/dsl/web/element_expression'
         | 
| 4 | 
            +
            require 'glimmer/dsl/web/listener_expression'
         | 
| 4 5 |  | 
| 5 6 | 
             
            module Glimmer
         | 
| 6 7 | 
             
              module DSL
         | 
| 7 8 | 
             
                module Web
         | 
| 8 9 | 
             
                  # TODO implement all those expressions
         | 
| 9 10 | 
             
            #        %w[
         | 
| 10 | 
            -
            #           | 
| 11 | 
            +
            #          listener
         | 
| 11 12 | 
             
            #          data_binding
         | 
| 12 13 | 
             
            #          attribute
         | 
| 13 14 | 
             
            #          shine_data_binding
         | 
| @@ -16,6 +17,7 @@ module Glimmer | |
| 16 17 | 
             
                  Engine.add_dynamic_expressions(
         | 
| 17 18 | 
             
                   Web,
         | 
| 18 19 | 
             
                   %w[
         | 
| 20 | 
            +
                     listener
         | 
| 19 21 | 
             
                     element
         | 
| 20 22 | 
             
                   ]
         | 
| 21 23 | 
             
                  )
         | 
| @@ -11,7 +11,7 @@ module Glimmer | |
| 11 11 | 
             
                    def can_interpret?(parent, keyword, *args, &block)
         | 
| 12 12 | 
             
                      # TODO automatically pass parent option as element if not passed instead of rejecting elements without a paraent nor root
         | 
| 13 13 | 
             
                      # TODO raise a proper error if root is an element that is not found (maybe do this in model)
         | 
| 14 | 
            -
                       | 
| 14 | 
            +
                      !keyword.to_s.start_with?('on_')
         | 
| 15 15 | 
             
                    end
         | 
| 16 16 |  | 
| 17 17 | 
             
                    def interpret(parent, keyword, *args, &block)
         | 
| @@ -21,7 +21,7 @@ module Glimmer | |
| 21 21 | 
             
                    def add_content(parent, keyword, *args, &block)
         | 
| 22 22 | 
             
                      if parent.rendered? || parent.skip_content_on_render_blocks?
         | 
| 23 23 | 
             
                        return_value = super(parent, keyword, *args, &block)
         | 
| 24 | 
            -
                        if return_value.is_a?(String)
         | 
| 24 | 
            +
                        if return_value.is_a?(String) && parent.dom_element.text.to_s.empty?
         | 
| 25 25 | 
             
                          parent.add_text_content(return_value)
         | 
| 26 26 | 
             
                        end
         | 
| 27 27 | 
             
                        parent.post_add_content
         | 
| @@ -0,0 +1,19 @@ | |
| 1 | 
            +
            require 'glimmer/dsl/expression'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Glimmer
         | 
| 4 | 
            +
              module DSL
         | 
| 5 | 
            +
                module Web
         | 
| 6 | 
            +
                  class ListenerExpression < Expression
         | 
| 7 | 
            +
                    def can_interpret?(parent, keyword, *args, &block)
         | 
| 8 | 
            +
                      parent and
         | 
| 9 | 
            +
                        parent.respond_to?(:can_handle_observation_request?) and
         | 
| 10 | 
            +
                        parent.can_handle_observation_request?(keyword)
         | 
| 11 | 
            +
                    end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                    def interpret(parent, keyword, *args, &block)
         | 
| 14 | 
            +
                      parent.handle_observation_request(keyword, block)
         | 
| 15 | 
            +
                    end
         | 
| 16 | 
            +
                  end
         | 
| 17 | 
            +
                end
         | 
| 18 | 
            +
              end
         | 
| 19 | 
            +
            end
         | 
| @@ -1,4 +1,4 @@ | |
| 1 | 
            -
            # Copyright (c)  | 
| 1 | 
            +
            # Copyright (c) 2023 Andy Maleh
         | 
| 2 2 | 
             
            #
         | 
| 3 3 | 
             
            # Permission is hereby granted, free of charge, to any person obtaining
         | 
| 4 4 | 
             
            # a copy of this software and associated documentation files (the
         | 
| @@ -21,6 +21,7 @@ | |
| 21 21 |  | 
| 22 22 | 
             
            # require 'glimmer/web/event_listener_proxy'
         | 
| 23 23 | 
             
            require 'glimmer/web/property_owner'
         | 
| 24 | 
            +
            require 'glimmer/web/listener_proxy'
         | 
| 24 25 |  | 
| 25 26 | 
             
            # TODO implement menu (which delays building it till render using add_content_on_render)
         | 
| 26 27 |  | 
| @@ -68,9 +69,11 @@ module Glimmer | |
| 68 69 |  | 
| 69 70 | 
             
                  include Glimmer
         | 
| 70 71 | 
             
                  include PropertyOwner
         | 
| 71 | 
            -
             | 
| 72 | 
            +
                  
         | 
| 72 73 | 
             
                  Event = Struct.new(:widget, keyword_init: true)
         | 
| 73 | 
            -
             | 
| 74 | 
            +
                  
         | 
| 75 | 
            +
                  GLIMMER_ATTRIBUTES = [:parent]
         | 
| 76 | 
            +
                  
         | 
| 74 77 | 
             
                  attr_reader :keyword, :parent, :args, :options, :children, :enabled, :foreground, :background, :focus, :removed?, :rendered
         | 
| 75 78 | 
             
                  alias rendered? rendered
         | 
| 76 79 |  | 
| @@ -198,6 +201,7 @@ module Glimmer | |
| 198 201 | 
             
                    if parent_selector
         | 
| 199 202 | 
             
                      Document.find(parent_selector)
         | 
| 200 203 | 
             
                    else
         | 
| 204 | 
            +
                      # TODO consider moving this to initializer
         | 
| 201 205 | 
             
                      options[:parent] ||= 'body'
         | 
| 202 206 | 
             
                      Document.find(options[:parent])
         | 
| 203 207 | 
             
                    end
         | 
| @@ -240,7 +244,7 @@ module Glimmer | |
| 240 244 | 
             
                  end
         | 
| 241 245 |  | 
| 242 246 | 
             
                  def add_text_content(text)
         | 
| 243 | 
            -
                    dom_element.append(text)
         | 
| 247 | 
            +
                    dom_element.append(text.to_s)
         | 
| 244 248 | 
             
                  end
         | 
| 245 249 |  | 
| 246 250 | 
             
                  def content_on_render_blocks
         | 
| @@ -263,16 +267,12 @@ module Glimmer | |
| 263 267 | 
             
                    # TODO consider passing parent element instead and having table item include a table cell widget only for opal
         | 
| 264 268 | 
             
                    @dom = nil
         | 
| 265 269 | 
             
                    @dom = dom # TODO unify how to build dom for most widgets based on element, id, and name (class)
         | 
| 266 | 
            -
             | 
| 270 | 
            +
            #         @dom = @parent.get_layout.dom(@dom) if @parent.respond_to?(:layout) && @parent.get_layout
         | 
| 267 271 | 
             
                    @dom
         | 
| 268 272 | 
             
                  end
         | 
| 269 273 |  | 
| 270 274 | 
             
                  def dom
         | 
| 271 | 
            -
                    body_class = ([name, element_id] + css_classes.to_a).join(' ')
         | 
| 272 275 | 
             
                    # TODO auto-convert known glimmer attributes like parent to data attributes like data-parent
         | 
| 273 | 
            -
                    html_options = options.dup
         | 
| 274 | 
            -
                    html_options[:class] ||= ''
         | 
| 275 | 
            -
                    html_options[:class] = "#{html_options[:class]} #{body_class}".strip
         | 
| 276 276 | 
             
                    @dom ||= html {
         | 
| 277 277 | 
             
                      send(keyword, html_options) {
         | 
| 278 278 | 
             
                        # TODO consider supporting the idea of dynamic CSS building on close of shell that adds only as much CSS as needed for widgets that were mentioned
         | 
| @@ -286,10 +286,24 @@ module Glimmer | |
| 286 286 | 
             
            #                 }
         | 
| 287 287 | 
             
            #               end
         | 
| 288 288 | 
             
            #             end
         | 
| 289 | 
            +
                        args.first if args.first.is_a?(String)
         | 
| 289 290 | 
             
                      }
         | 
| 290 291 | 
             
                    }.to_s
         | 
| 291 292 | 
             
                  end
         | 
| 292 293 |  | 
| 294 | 
            +
                  def html_options
         | 
| 295 | 
            +
                    body_class = ([name, element_id] + css_classes.to_a).join(' ')
         | 
| 296 | 
            +
                    html_options = options.dup
         | 
| 297 | 
            +
                    GLIMMER_ATTRIBUTES.each do |attribute|
         | 
| 298 | 
            +
                      next unless html_options.include?(attribute)
         | 
| 299 | 
            +
                      data_normalized_attribute = attribute.split('_').join('-')
         | 
| 300 | 
            +
                      html_options["data-#{data_normalized_attribute}"] = html_options.delete(attribute)
         | 
| 301 | 
            +
                    end
         | 
| 302 | 
            +
                    html_options[:class] ||= ''
         | 
| 303 | 
            +
                    html_options[:class] = "#{html_options[:class]} #{body_class}".strip
         | 
| 304 | 
            +
                    html_options
         | 
| 305 | 
            +
                  end
         | 
| 306 | 
            +
                  
         | 
| 293 307 | 
             
                  def content(&block)
         | 
| 294 308 | 
             
                    Glimmer::DSL::Engine.add_content(self, Glimmer::DSL::Web::ElementExpression.new, keyword, &block)
         | 
| 295 309 | 
             
                  end
         | 
| @@ -745,58 +759,69 @@ module Glimmer | |
| 745 759 | 
             
                    listeners[listener_event.to_s] ||= []
         | 
| 746 760 | 
             
                  end
         | 
| 747 761 |  | 
| 748 | 
            -
                  def can_handle_observation_request?( | 
| 762 | 
            +
                  def can_handle_observation_request?(keyword)
         | 
| 749 763 | 
             
                    # TODO sort this out for Opal
         | 
| 750 | 
            -
                     | 
| 751 | 
            -
                     | 
| 752 | 
            -
             | 
| 753 | 
            -
             | 
| 754 | 
            -
             | 
| 755 | 
            -
            # | 
| 756 | 
            -
            #            | 
| 757 | 
            -
             | 
| 758 | 
            -
             | 
| 764 | 
            +
                    keyword = keyword.to_s
         | 
| 765 | 
            +
                    keyword.start_with?('on_')
         | 
| 766 | 
            +
            #         if keyword.start_with?('on_swt_')
         | 
| 767 | 
            +
            #           constant_name = keyword.sub(/^on_swt_/, '')
         | 
| 768 | 
            +
            #           SWTProxy.has_constant?(constant_name)
         | 
| 769 | 
            +
            #         elsif keyword.start_with?('on_')
         | 
| 770 | 
            +
            #           # event = keyword.sub(/^on_/, '')
         | 
| 771 | 
            +
            #           # can_add_listener?(event) || can_handle_drag_observation_request?(keyword) || can_handle_drop_observation_request?(keyword)
         | 
| 772 | 
            +
            #           true # TODO filter by valid listeners only in the future
         | 
| 773 | 
            +
            #         end
         | 
| 759 774 | 
             
                  end
         | 
| 760 775 |  | 
| 761 776 | 
             
                  def handle_observation_request(keyword, original_event_listener)
         | 
| 762 | 
            -
             | 
| 763 | 
            -
             | 
| 764 | 
            -
             | 
| 765 | 
            -
             | 
| 777 | 
            +
            #         case keyword
         | 
| 778 | 
            +
            #         when 'on_widget_removed'
         | 
| 779 | 
            +
            #           listeners_for(keyword.sub(/^on_/, '')) << original_event_listener.to_proc
         | 
| 780 | 
            +
            #         else
         | 
| 766 781 | 
             
                      handle_javascript_observation_request(keyword, original_event_listener)
         | 
| 767 | 
            -
             | 
| 782 | 
            +
            #         end
         | 
| 768 783 | 
             
                  end
         | 
| 769 784 |  | 
| 770 785 | 
             
                  def handle_javascript_observation_request(keyword, original_event_listener)
         | 
| 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 | 
            -
             | 
| 786 | 
            +
                    listener = ListenerProxy.new(
         | 
| 787 | 
            +
                      element_proxy: self,
         | 
| 788 | 
            +
                      selector: selector,
         | 
| 789 | 
            +
                      dom_element: dom_element,
         | 
| 790 | 
            +
                      event: keyword.sub(/^on_/, ''),
         | 
| 791 | 
            +
                      listener: original_event_listener,
         | 
| 792 | 
            +
                      original_event_listener: original_event_listener
         | 
| 793 | 
            +
                    )
         | 
| 794 | 
            +
                    listener.register
         | 
| 795 | 
            +
                    listener
         | 
| 796 | 
            +
            #         return unless effective_observation_request_to_event_mapping.keys.include?(keyword)
         | 
| 797 | 
            +
            #         event = nil
         | 
| 798 | 
            +
            #         delegate = nil
         | 
| 799 | 
            +
            #         effective_observation_request_to_event_mapping[keyword].to_collection.each do |mapping|
         | 
| 800 | 
            +
            #           observation_requests[keyword] ||= Set.new
         | 
| 801 | 
            +
            #           observation_requests[keyword] << original_event_listener
         | 
| 802 | 
            +
            #           event = mapping[:event]
         | 
| 803 | 
            +
            #           event_handler = mapping[:event_handler]
         | 
| 804 | 
            +
            #           event_element_css_selector = mapping[:event_element_css_selector]
         | 
| 805 | 
            +
            #           potential_event_listener = event_handler&.call(original_event_listener)
         | 
| 806 | 
            +
            #           event_listener = potential_event_listener || original_event_listener
         | 
| 807 | 
            +
            #           async_event_listener = proc do |event|
         | 
| 808 | 
            +
                        ## TODO look into the issue with using async::task.new here. maybe put it in event listener (like not being able to call preventDefault or return false successfully )
         | 
| 809 | 
            +
                        ## maybe consider pushing inside the widget classes instead where needed only or implement universal doit support correctly to bypass this issue
         | 
| 810 | 
            +
                        ## Async::Task.new do
         | 
| 811 | 
            +
            #             @@widget_handling_listener = self
         | 
| 812 | 
            +
                        ## TODO also make sure to disable all widgets for suspension
         | 
| 813 | 
            +
            #             event_listener.call(event) unless dialog_ancestor&.event_handling_suspended?
         | 
| 814 | 
            +
            #             @widget_handling_listener = nil
         | 
| 815 | 
            +
                        ## end
         | 
| 816 | 
            +
            #           end
         | 
| 817 | 
            +
            #           the_listener_dom_element = event_element_css_selector ? Element[event_element_css_selector] : listener_dom_element
         | 
| 818 | 
            +
            #           unless the_listener_dom_element.empty?
         | 
| 819 | 
            +
            #             the_listener_dom_element.on(event, &async_event_listener)
         | 
| 820 | 
            +
                        ## TODO ensure uniqueness of insertion (perhaps adding equals/hash method to event listener proxy)
         | 
| 821 | 
            +
            #
         | 
| 822 | 
            +
            #             event_listener_proxies << EventListenerProxy.new(element_proxy: self, selector: selector, dom_element: the_listener_dom_element, event: event, listener: async_event_listener, original_event_listener: original_event_listener)
         | 
| 823 | 
            +
            #           end
         | 
| 824 | 
            +
            #         end
         | 
| 800 825 | 
             
                  end
         | 
| 801 826 |  | 
| 802 827 | 
             
                  def remove_event_listener_proxies
         | 
| @@ -819,11 +844,27 @@ module Glimmer | |
| 819 844 | 
             
                    super(attribute_name, *args) # PropertyOwner
         | 
| 820 845 | 
             
                  end
         | 
| 821 846 |  | 
| 822 | 
            -
                  def  | 
| 823 | 
            -
                     | 
| 824 | 
            -
                       | 
| 847 | 
            +
                  def respond_to_missing?(method_name, include_private = false)
         | 
| 848 | 
            +
                    super(method_name, include_private) ||
         | 
| 849 | 
            +
                      (dom_element && dom_element.length > 0 && Native.call(dom_element, '0').respond_to?(method_name, include_private)) ||
         | 
| 850 | 
            +
                      dom_element.respond_to?(method_name, include_private) ||
         | 
| 851 | 
            +
                      method_name.to_s.start_with?('on_')
         | 
| 852 | 
            +
                  end
         | 
| 853 | 
            +
                  
         | 
| 854 | 
            +
                  def method_missing(method_name, *args, &block)
         | 
| 855 | 
            +
                    if method_name.to_s.start_with?('on_')
         | 
| 856 | 
            +
                      handle_observation_request(method_name, block)
         | 
| 857 | 
            +
                    elsif dom_element.respond_to?(method_name)
         | 
| 858 | 
            +
                      dom_element.send(method_name, *args, &block)
         | 
| 859 | 
            +
                    elsif dom_element && dom_element.length > 0
         | 
| 860 | 
            +
                      begin
         | 
| 861 | 
            +
                        js_args = block.nil? ? args : (args + [block])
         | 
| 862 | 
            +
                        Native.call(dom_element, '0').method_missing(method_name.to_s.camelcase, *js_args)
         | 
| 863 | 
            +
                      rescue Exception
         | 
| 864 | 
            +
                        super(method_name, *args, &block)
         | 
| 865 | 
            +
                      end
         | 
| 825 866 | 
             
                    else
         | 
| 826 | 
            -
                      super( | 
| 867 | 
            +
                      super(method_name, *args, &block)
         | 
| 827 868 | 
             
                    end
         | 
| 828 869 | 
             
                  end
         | 
| 829 870 |  | 
| @@ -0,0 +1,37 @@ | |
| 1 | 
            +
            module Glimmer
         | 
| 2 | 
            +
              module Web
         | 
| 3 | 
            +
                class ListenerProxy
         | 
| 4 | 
            +
                  attr_reader :element_proxy, :event, :dom_element, :selector, :listener, :original_event_listener
         | 
| 5 | 
            +
             | 
| 6 | 
            +
                  def initialize(element_proxy:, event:, dom_element:, selector:, listener:)
         | 
| 7 | 
            +
                    @element_proxy = element_proxy
         | 
| 8 | 
            +
                    @event = event
         | 
| 9 | 
            +
                    @dom_element = dom_element
         | 
| 10 | 
            +
                    @selector = selector
         | 
| 11 | 
            +
                    @listener = listener
         | 
| 12 | 
            +
                    @js_listener = lambda do |event|
         | 
| 13 | 
            +
                      event.prevent
         | 
| 14 | 
            +
                      event.prevent_default
         | 
| 15 | 
            +
                      event.stop_propagation
         | 
| 16 | 
            +
                      event.stop_immediate_propagation
         | 
| 17 | 
            +
                      # TODO wrap event with a Ruby Event object before passing to listener
         | 
| 18 | 
            +
                      listener.call(event)
         | 
| 19 | 
            +
                      false
         | 
| 20 | 
            +
                    end
         | 
| 21 | 
            +
                    @original_event_listener = original_event_listener
         | 
| 22 | 
            +
                  end
         | 
| 23 | 
            +
                  
         | 
| 24 | 
            +
                  def register
         | 
| 25 | 
            +
                    @dom_element.on(@event, &@js_listener)
         | 
| 26 | 
            +
                  end
         | 
| 27 | 
            +
                  alias observe register
         | 
| 28 | 
            +
                  alias reregister register
         | 
| 29 | 
            +
                  
         | 
| 30 | 
            +
                  def unregister
         | 
| 31 | 
            +
                    @dom_element.off(@event, &@js_listener)
         | 
| 32 | 
            +
                  end
         | 
| 33 | 
            +
                  alias unobserve unregister
         | 
| 34 | 
            +
                  alias deregister unregister
         | 
| 35 | 
            +
                end
         | 
| 36 | 
            +
              end
         | 
| 37 | 
            +
            end
         | 
    
        data/lib/glimmer/web.rb
    CHANGED
    
    
| @@ -0,0 +1,99 @@ | |
| 1 | 
            +
            # Copyright (c) 2023 Andy Maleh
         | 
| 2 | 
            +
            #
         | 
| 3 | 
            +
            # Permission is hereby granted, free of charge, to any person obtaining
         | 
| 4 | 
            +
            # a copy of this software and associated documentation files (the
         | 
| 5 | 
            +
            # "Software"), to deal in the Software without restriction, including
         | 
| 6 | 
            +
            # without limitation the rights to use, copy, modify, merge, publish,
         | 
| 7 | 
            +
            # distribute, sublicense, and/or sell copies of the Software, and to
         | 
| 8 | 
            +
            # permit persons to whom the Software is furnished to do so, subject to
         | 
| 9 | 
            +
            # the following conditions:
         | 
| 10 | 
            +
            #
         | 
| 11 | 
            +
            # The above copyright notice and this permission notice shall be
         | 
| 12 | 
            +
            # included in all copies or substantial portions of the Software.
         | 
| 13 | 
            +
            #
         | 
| 14 | 
            +
            # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
         | 
| 15 | 
            +
            # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
         | 
| 16 | 
            +
            # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
         | 
| 17 | 
            +
            # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
         | 
| 18 | 
            +
            # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
         | 
| 19 | 
            +
            # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
         | 
| 20 | 
            +
            # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
         | 
| 21 | 
            +
             | 
| 22 | 
            +
            require 'glimmer-dsl-web'
         | 
| 23 | 
            +
             | 
| 24 | 
            +
            include Glimmer
         | 
| 25 | 
            +
             | 
| 26 | 
            +
            Document.ready? do
         | 
| 27 | 
            +
              div {
         | 
| 28 | 
            +
                h1('Contact Form')
         | 
| 29 | 
            +
                form {
         | 
| 30 | 
            +
                  div(class: 'field-row') {
         | 
| 31 | 
            +
                    label('Name: ', for: 'name-field')
         | 
| 32 | 
            +
                    @name_input = input(id: 'name-field', class: 'field', type: 'text', required: true)
         | 
| 33 | 
            +
                  }
         | 
| 34 | 
            +
                  div(class: 'field-row') {
         | 
| 35 | 
            +
                    label('Email: ', for: 'email-field')
         | 
| 36 | 
            +
                    @email_input = input(id: 'email-field', class: 'field', type: 'email', required: true)
         | 
| 37 | 
            +
                  }
         | 
| 38 | 
            +
                  @add_button = button('Add Contact', class: 'submit-button') {
         | 
| 39 | 
            +
                    on_click do
         | 
| 40 | 
            +
                      if ([@name_input, @email_input].all? {|input| input.check_validity })
         | 
| 41 | 
            +
                        @table.content {
         | 
| 42 | 
            +
                          tr {
         | 
| 43 | 
            +
                            td { @name_input.value }
         | 
| 44 | 
            +
                            td { @email_input.value }
         | 
| 45 | 
            +
                          }
         | 
| 46 | 
            +
                        }
         | 
| 47 | 
            +
                        @email_input.value = @name_input.value = ''
         | 
| 48 | 
            +
                      else
         | 
| 49 | 
            +
                        error_messages = []
         | 
| 50 | 
            +
                        error_messages << "Name is not valid! Make sure it is filled." if !@name_input.check_validity
         | 
| 51 | 
            +
                        error_messages << "Email is not valid! Make sure it is filled and has a valid format." if !@email_input.check_validity
         | 
| 52 | 
            +
                        $$.alert(error_messages.join("\n"))
         | 
| 53 | 
            +
                      end
         | 
| 54 | 
            +
                    end
         | 
| 55 | 
            +
                  }
         | 
| 56 | 
            +
                }
         | 
| 57 | 
            +
                h1('Contacts Table')
         | 
| 58 | 
            +
                @table = table {
         | 
| 59 | 
            +
                  tr {
         | 
| 60 | 
            +
                    th('Name')
         | 
| 61 | 
            +
                    th('Email')
         | 
| 62 | 
            +
                  }
         | 
| 63 | 
            +
                  tr {
         | 
| 64 | 
            +
                    td('John Doe')
         | 
| 65 | 
            +
                    td('johndoe@example.com')
         | 
| 66 | 
            +
                  }
         | 
| 67 | 
            +
                  tr {
         | 
| 68 | 
            +
                    td('Jane Doe')
         | 
| 69 | 
            +
                    td('janedoe@example.com')
         | 
| 70 | 
            +
                  }
         | 
| 71 | 
            +
                }
         | 
| 72 | 
            +
                
         | 
| 73 | 
            +
                # CSS Styles
         | 
| 74 | 
            +
                style {
         | 
| 75 | 
            +
                  <<~CSS
         | 
| 76 | 
            +
                    .field-row {
         | 
| 77 | 
            +
                      margin: 10px 5px;
         | 
| 78 | 
            +
                    }
         | 
| 79 | 
            +
                    .field {
         | 
| 80 | 
            +
                      margin-left: 5px;
         | 
| 81 | 
            +
                    }
         | 
| 82 | 
            +
                    .submit-button {
         | 
| 83 | 
            +
                      display: block;
         | 
| 84 | 
            +
                      margin: 10px 5px;
         | 
| 85 | 
            +
                    }
         | 
| 86 | 
            +
                    table {
         | 
| 87 | 
            +
                      border:1px solid grey;
         | 
| 88 | 
            +
                      border-spacing: 0;
         | 
| 89 | 
            +
                    }
         | 
| 90 | 
            +
                    table tr td, table tr th {
         | 
| 91 | 
            +
                      padding: 5px;
         | 
| 92 | 
            +
                    }
         | 
| 93 | 
            +
                    table tr:nth-child(even) {
         | 
| 94 | 
            +
                      background: #ccc;
         | 
| 95 | 
            +
                    }
         | 
| 96 | 
            +
                  CSS
         | 
| 97 | 
            +
                }
         | 
| 98 | 
            +
              }.render
         | 
| 99 | 
            +
            end
         | 
    
        data/lib/glimmer-dsl-web.rb
    CHANGED
    
    
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: glimmer-dsl-web
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0.0. | 
| 4 | 
            +
              version: 0.0.3
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Andy Maleh
         | 
| 8 8 | 
             
            autorequire:
         | 
| 9 9 | 
             
            bindir: bin
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date: 2023-12- | 
| 11 | 
            +
            date: 2023-12-28 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: glimmer
         | 
| @@ -258,6 +258,7 @@ files: | |
| 258 258 | 
             
            - lib/glimmer-dsl-web/ext/class.rb
         | 
| 259 259 | 
             
            - lib/glimmer-dsl-web/ext/date.rb
         | 
| 260 260 | 
             
            - lib/glimmer-dsl-web/ext/exception.rb
         | 
| 261 | 
            +
            - lib/glimmer-dsl-web/samples/hello/hello_button.rb
         | 
| 261 262 | 
             
            - lib/glimmer-dsl-web/samples/hello/hello_world.rb
         | 
| 262 263 | 
             
            - lib/glimmer-dsl-web/vendor/jquery.js
         | 
| 263 264 | 
             
            - lib/glimmer/config/opal_logger.rb
         | 
| @@ -265,9 +266,11 @@ files: | |
| 265 266 | 
             
            - lib/glimmer/data_binding/observable_element.rb
         | 
| 266 267 | 
             
            - lib/glimmer/dsl/web/dsl.rb
         | 
| 267 268 | 
             
            - lib/glimmer/dsl/web/element_expression.rb
         | 
| 269 | 
            +
            - lib/glimmer/dsl/web/listener_expression.rb
         | 
| 268 270 | 
             
            - lib/glimmer/util/proc_tracker.rb
         | 
| 269 271 | 
             
            - lib/glimmer/web.rb
         | 
| 270 272 | 
             
            - lib/glimmer/web/element_proxy.rb
         | 
| 273 | 
            +
            - lib/glimmer/web/listener_proxy.rb
         | 
| 271 274 | 
             
            - lib/glimmer/web/property_owner.rb
         | 
| 272 275 | 
             
            homepage: http://github.com/AndyObtiva/glimmer-dsl-web
         | 
| 273 276 | 
             
            licenses:
         |