glimmer-dsl-web 0.0.6 → 0.0.7
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 +10 -0
- data/LICENSE.txt +1 -1
- data/README.md +164 -24
- data/VERSION +1 -1
- data/glimmer-dsl-web.gemspec +5 -5
- data/lib/glimmer/data_binding/element_binding.rb +1 -1
- data/lib/glimmer/dsl/web/bind_expression.rb +1 -1
- data/lib/glimmer/util/proc_tracker.rb +1 -1
- data/lib/glimmer/web/element_proxy.rb +120 -458
- data/lib/glimmer/web/event_proxy.rb +1 -1
- data/lib/glimmer/web/listener_proxy.rb +1 -1
- data/lib/glimmer/web.rb +1 -1
- data/lib/glimmer-dsl-web/ext/date.rb +4 -1
- data/lib/glimmer-dsl-web/samples/hello/hello_button.rb +1 -1
- data/lib/glimmer-dsl-web/samples/hello/hello_data_binding.rb +26 -11
- data/lib/glimmer-dsl-web/samples/hello/hello_form.rb +1 -1
- data/lib/glimmer-dsl-web/samples/hello/hello_input_date_time.rb +117 -0
- data/lib/glimmer-dsl-web/samples/hello/hello_world.rb +1 -1
- data/lib/glimmer-dsl-web.rb +1 -1
- metadata +3 -3
- data/lib/glimmer/web/property_owner.rb +0 -24
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright (c) 2023 Andy Maleh
|
1
|
+
# Copyright (c) 2023-2024 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
|
@@ -19,12 +19,8 @@
|
|
19
19
|
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
20
|
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
21
21
|
|
22
|
-
# require 'glimmer/web/event_listener_proxy'
|
23
|
-
require 'glimmer/web/property_owner'
|
24
22
|
require 'glimmer/web/listener_proxy'
|
25
23
|
|
26
|
-
# TODO implement menu (which delays building it till render using add_content_on_render)
|
27
|
-
|
28
24
|
module Glimmer
|
29
25
|
module Web
|
30
26
|
class ElementProxy
|
@@ -68,11 +64,16 @@ module Glimmer
|
|
68
64
|
end
|
69
65
|
|
70
66
|
include Glimmer
|
71
|
-
include PropertyOwner
|
72
67
|
|
73
68
|
Event = Struct.new(:widget, keyword_init: true)
|
74
69
|
|
75
70
|
GLIMMER_ATTRIBUTES = [:parent]
|
71
|
+
FORMAT_DATETIME = '%Y-%m-%dT%H:%M'
|
72
|
+
FORMAT_DATE = '%Y-%m-%d'
|
73
|
+
FORMAT_TIME = '%H:%M'
|
74
|
+
REGEX_FORMAT_DATETIME = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}$/
|
75
|
+
REGEX_FORMAT_DATE = /^\d{4}-\d{2}-\d{2}$/
|
76
|
+
REGEX_FORMAT_TIME = /^\d{2}:\d{2}$/
|
76
77
|
|
77
78
|
attr_reader :keyword, :parent, :args, :options, :children, :enabled, :foreground, :background, :removed?, :rendered
|
78
79
|
alias rendered? rendered
|
@@ -286,360 +287,6 @@ module Glimmer
|
|
286
287
|
{}
|
287
288
|
end
|
288
289
|
|
289
|
-
def effective_observation_request_to_event_mapping
|
290
|
-
default_observation_request_to_event_mapping.merge(observation_request_to_event_mapping)
|
291
|
-
end
|
292
|
-
|
293
|
-
def default_observation_request_to_event_mapping
|
294
|
-
myself = self
|
295
|
-
mouse_event_handler = -> (event_listener) {
|
296
|
-
-> (event) {
|
297
|
-
# TODO generalize this solution to all widgets that support key presses
|
298
|
-
event.define_singleton_method(:widget) {myself}
|
299
|
-
event.define_singleton_method(:button, &event.method(:which))
|
300
|
-
event.define_singleton_method(:count) {1} # TODO support double-click count of 2 in the future by using ondblclick
|
301
|
-
event.define_singleton_method(:x, &event.method(:page_x))
|
302
|
-
event.define_singleton_method(:y, &event.method(:page_y))
|
303
|
-
doit = true
|
304
|
-
event.define_singleton_method(:doit=) do |value|
|
305
|
-
doit = value
|
306
|
-
end
|
307
|
-
event.define_singleton_method(:doit) { doit }
|
308
|
-
|
309
|
-
if event.which == 1
|
310
|
-
# event.prevent # TODO consider if this is needed
|
311
|
-
event_listener.call(event)
|
312
|
-
end
|
313
|
-
|
314
|
-
# TODO Imlement doit properly for all different kinds of events
|
315
|
-
# unless doit
|
316
|
-
# event.prevent
|
317
|
-
# event.stop
|
318
|
-
# event.stop_immediate
|
319
|
-
# end
|
320
|
-
}
|
321
|
-
}
|
322
|
-
mouse_move_event_handler = -> (event_listener) {
|
323
|
-
-> (event) {
|
324
|
-
# TODO generalize this solution to all widgets that support key presses
|
325
|
-
event.define_singleton_method(:widget) {myself}
|
326
|
-
event.define_singleton_method(:button, &event.method(:which))
|
327
|
-
event.define_singleton_method(:count) {1} # TODO support double-click count of 2 in the future by using ondblclick
|
328
|
-
event.define_singleton_method(:x, &event.method(:page_x))
|
329
|
-
event.define_singleton_method(:y, &event.method(:page_y))
|
330
|
-
doit = true
|
331
|
-
event.define_singleton_method(:doit=) do |value|
|
332
|
-
doit = value
|
333
|
-
end
|
334
|
-
event.define_singleton_method(:doit) { doit }
|
335
|
-
|
336
|
-
event_listener.call(event)
|
337
|
-
|
338
|
-
# TODO Imlement doit properly for all different kinds of events
|
339
|
-
# unless doit
|
340
|
-
# event.prevent
|
341
|
-
# event.stop
|
342
|
-
# event.stop_immediate
|
343
|
-
# end
|
344
|
-
}
|
345
|
-
}
|
346
|
-
context_menu_handler = -> (event_listener) {
|
347
|
-
-> (event) {
|
348
|
-
# TODO generalize this solution to all widgets that support key presses
|
349
|
-
event.define_singleton_method(:widget) {myself}
|
350
|
-
event.define_singleton_method(:button, &event.method(:which))
|
351
|
-
event.define_singleton_method(:count) {1} # TODO support double-click count of 2 in the future by using ondblclick
|
352
|
-
event.define_singleton_method(:x, &event.method(:page_x))
|
353
|
-
event.define_singleton_method(:y, &event.method(:page_y))
|
354
|
-
doit = true
|
355
|
-
event.define_singleton_method(:doit=) do |value|
|
356
|
-
doit = value
|
357
|
-
end
|
358
|
-
event.define_singleton_method(:doit) { doit }
|
359
|
-
|
360
|
-
if event.which == 3
|
361
|
-
event.prevent
|
362
|
-
event_listener.call(event)
|
363
|
-
end
|
364
|
-
# TODO Imlement doit properly for all different kinds of events
|
365
|
-
# unless doit
|
366
|
-
# event.prevent
|
367
|
-
# event.stop
|
368
|
-
# event.stop_immediate
|
369
|
-
# end
|
370
|
-
}
|
371
|
-
}
|
372
|
-
{
|
373
|
-
'on_focus_gained' => {
|
374
|
-
event: 'focus',
|
375
|
-
},
|
376
|
-
'on_focus_lost' => {
|
377
|
-
event: 'blur',
|
378
|
-
},
|
379
|
-
'on_mouse_move' => [
|
380
|
-
{
|
381
|
-
event: 'mousemove',
|
382
|
-
event_handler: mouse_move_event_handler,
|
383
|
-
},
|
384
|
-
],
|
385
|
-
'on_mouse_up' => [
|
386
|
-
{
|
387
|
-
event: 'mouseup',
|
388
|
-
event_handler: mouse_event_handler,
|
389
|
-
},
|
390
|
-
{
|
391
|
-
event: 'contextmenu',
|
392
|
-
event_handler: context_menu_handler,
|
393
|
-
},
|
394
|
-
],
|
395
|
-
'on_mouse_down' => [
|
396
|
-
{
|
397
|
-
event: 'mousedown',
|
398
|
-
event_handler: mouse_event_handler,
|
399
|
-
},
|
400
|
-
{
|
401
|
-
event: 'contextmenu',
|
402
|
-
event_handler: context_menu_handler,
|
403
|
-
},
|
404
|
-
],
|
405
|
-
'on_swt_mouseup' => [
|
406
|
-
{
|
407
|
-
event: 'mouseup',
|
408
|
-
event_handler: mouse_event_handler,
|
409
|
-
},
|
410
|
-
{
|
411
|
-
event: 'contextmenu',
|
412
|
-
event_handler: context_menu_handler,
|
413
|
-
},
|
414
|
-
],
|
415
|
-
'on_swt_mousedown' => [
|
416
|
-
{
|
417
|
-
event: 'mousedown',
|
418
|
-
event_handler: mouse_event_handler,
|
419
|
-
},
|
420
|
-
{
|
421
|
-
event: 'contextmenu',
|
422
|
-
event_handler: context_menu_handler,
|
423
|
-
},
|
424
|
-
],
|
425
|
-
'on_key_pressed' => {
|
426
|
-
event: 'keypress',
|
427
|
-
event_handler: -> (event_listener) {
|
428
|
-
-> (event) {
|
429
|
-
event.define_singleton_method(:widget) {myself}
|
430
|
-
event.define_singleton_method(:keyLocation) do
|
431
|
-
location = `#{event.to_n}.originalEvent.location`
|
432
|
-
JS_LOCATION_TO_SWT_KEY_LOCATION_MAP[location] || location
|
433
|
-
end
|
434
|
-
event.define_singleton_method(:key_location, &event.method(:keyLocation))
|
435
|
-
event.define_singleton_method(:keyCode) {
|
436
|
-
JS_KEY_CODE_TO_SWT_KEY_CODE_MAP[event.which] || event.which
|
437
|
-
}
|
438
|
-
event.define_singleton_method(:key_code, &event.method(:keyCode))
|
439
|
-
event.define_singleton_method(:character) {event.which.chr}
|
440
|
-
event.define_singleton_method(:stateMask) do
|
441
|
-
state_mask = 0
|
442
|
-
state_mask |= SWTProxy[:alt] if event.alt_key
|
443
|
-
state_mask |= SWTProxy[:ctrl] if event.ctrl_key
|
444
|
-
state_mask |= SWTProxy[:shift] if event.shift_key
|
445
|
-
state_mask |= SWTProxy[:command] if event.meta_key
|
446
|
-
state_mask
|
447
|
-
end
|
448
|
-
event.define_singleton_method(:state_mask, &event.method(:stateMask))
|
449
|
-
doit = true
|
450
|
-
event.define_singleton_method(:doit=) do |value|
|
451
|
-
doit = value
|
452
|
-
end
|
453
|
-
event.define_singleton_method(:doit) { doit }
|
454
|
-
event_listener.call(event)
|
455
|
-
|
456
|
-
# TODO Fix doit false, it's not stopping input
|
457
|
-
unless doit
|
458
|
-
event.prevent
|
459
|
-
event.prevent_default
|
460
|
-
event.stop_propagation
|
461
|
-
event.stop_immediate_propagation
|
462
|
-
end
|
463
|
-
|
464
|
-
doit
|
465
|
-
}
|
466
|
-
} },
|
467
|
-
'on_key_released' => {
|
468
|
-
event: 'keyup',
|
469
|
-
event_handler: -> (event_listener) {
|
470
|
-
-> (event) {
|
471
|
-
event.define_singleton_method(:keyLocation) do
|
472
|
-
location = `#{event.to_n}.originalEvent.location`
|
473
|
-
JS_LOCATION_TO_SWT_KEY_LOCATION_MAP[location] || location
|
474
|
-
end
|
475
|
-
event.define_singleton_method(:key_location, &event.method(:keyLocation))
|
476
|
-
event.define_singleton_method(:widget) {myself}
|
477
|
-
event.define_singleton_method(:keyCode) {
|
478
|
-
JS_KEY_CODE_TO_SWT_KEY_CODE_MAP[event.which] || event.which
|
479
|
-
}
|
480
|
-
event.define_singleton_method(:key_code, &event.method(:keyCode))
|
481
|
-
event.define_singleton_method(:character) {event.which.chr}
|
482
|
-
event.define_singleton_method(:stateMask) do
|
483
|
-
state_mask = 0
|
484
|
-
state_mask |= SWTProxy[:alt] if event.alt_key
|
485
|
-
state_mask |= SWTProxy[:ctrl] if event.ctrl_key
|
486
|
-
state_mask |= SWTProxy[:shift] if event.shift_key
|
487
|
-
state_mask |= SWTProxy[:command] if event.meta_key
|
488
|
-
state_mask
|
489
|
-
end
|
490
|
-
event.define_singleton_method(:state_mask, &event.method(:stateMask))
|
491
|
-
doit = true
|
492
|
-
event.define_singleton_method(:doit=) do |value|
|
493
|
-
doit = value
|
494
|
-
end
|
495
|
-
event.define_singleton_method(:doit) { doit }
|
496
|
-
event_listener.call(event)
|
497
|
-
|
498
|
-
# TODO Fix doit false, it's not stopping input
|
499
|
-
unless doit
|
500
|
-
event.prevent
|
501
|
-
event.prevent_default
|
502
|
-
event.stop_propagation
|
503
|
-
event.stop_immediate_propagation
|
504
|
-
end
|
505
|
-
|
506
|
-
doit
|
507
|
-
}
|
508
|
-
}
|
509
|
-
},
|
510
|
-
'on_swt_keydown' => [
|
511
|
-
{
|
512
|
-
event: 'keypress',
|
513
|
-
event_handler: -> (event_listener) {
|
514
|
-
-> (event) {
|
515
|
-
event.define_singleton_method(:keyLocation) do
|
516
|
-
location = `#{event.to_n}.originalEvent.location`
|
517
|
-
JS_LOCATION_TO_SWT_KEY_LOCATION_MAP[location] || location
|
518
|
-
end
|
519
|
-
event.define_singleton_method(:key_location, &event.method(:keyLocation))
|
520
|
-
event.define_singleton_method(:keyCode) {
|
521
|
-
JS_KEY_CODE_TO_SWT_KEY_CODE_MAP[event.which] || event.which
|
522
|
-
}
|
523
|
-
event.define_singleton_method(:key_code, &event.method(:keyCode))
|
524
|
-
event.define_singleton_method(:widget) {myself}
|
525
|
-
event.define_singleton_method(:character) {event.which.chr}
|
526
|
-
event.define_singleton_method(:stateMask) do
|
527
|
-
state_mask = 0
|
528
|
-
state_mask |= SWTProxy[:alt] if event.alt_key
|
529
|
-
state_mask |= SWTProxy[:ctrl] if event.ctrl_key
|
530
|
-
state_mask |= SWTProxy[:shift] if event.shift_key
|
531
|
-
state_mask |= SWTProxy[:command] if event.meta_key
|
532
|
-
state_mask
|
533
|
-
end
|
534
|
-
event.define_singleton_method(:state_mask, &event.method(:stateMask))
|
535
|
-
doit = true
|
536
|
-
event.define_singleton_method(:doit=) do |value|
|
537
|
-
doit = value
|
538
|
-
end
|
539
|
-
event.define_singleton_method(:doit) { doit }
|
540
|
-
event_listener.call(event)
|
541
|
-
|
542
|
-
# TODO Fix doit false, it's not stopping input
|
543
|
-
unless doit
|
544
|
-
event.prevent
|
545
|
-
event.prevent_default
|
546
|
-
event.stop_propagation
|
547
|
-
event.stop_immediate_propagation
|
548
|
-
end
|
549
|
-
|
550
|
-
doit
|
551
|
-
}
|
552
|
-
}
|
553
|
-
},
|
554
|
-
{
|
555
|
-
event: 'keydown',
|
556
|
-
event_handler: -> (event_listener) {
|
557
|
-
-> (event) {
|
558
|
-
event.define_singleton_method(:keyLocation) do
|
559
|
-
location = `#{event.to_n}.originalEvent.location`
|
560
|
-
JS_LOCATION_TO_SWT_KEY_LOCATION_MAP[location] || location
|
561
|
-
end
|
562
|
-
event.define_singleton_method(:key_location, &event.method(:keyLocation))
|
563
|
-
event.define_singleton_method(:keyCode) {
|
564
|
-
JS_KEY_CODE_TO_SWT_KEY_CODE_MAP[event.which] || event.which
|
565
|
-
}
|
566
|
-
event.define_singleton_method(:key_code, &event.method(:keyCode))
|
567
|
-
event.define_singleton_method(:widget) {myself}
|
568
|
-
event.define_singleton_method(:character) {event.which.chr}
|
569
|
-
event.define_singleton_method(:stateMask) do
|
570
|
-
state_mask = 0
|
571
|
-
state_mask |= SWTProxy[:alt] if event.alt_key
|
572
|
-
state_mask |= SWTProxy[:ctrl] if event.ctrl_key
|
573
|
-
state_mask |= SWTProxy[:shift] if event.shift_key
|
574
|
-
state_mask |= SWTProxy[:command] if event.meta_key
|
575
|
-
state_mask
|
576
|
-
end
|
577
|
-
event.define_singleton_method(:state_mask, &event.method(:stateMask))
|
578
|
-
doit = true
|
579
|
-
event.define_singleton_method(:doit=) do |value|
|
580
|
-
doit = value
|
581
|
-
end
|
582
|
-
event.define_singleton_method(:doit) { doit }
|
583
|
-
event_listener.call(event) if event.which != 13 && (event.which == 127 || event.which <= 40)
|
584
|
-
|
585
|
-
# TODO Fix doit false, it's not stopping input
|
586
|
-
unless doit
|
587
|
-
event.prevent
|
588
|
-
event.prevent_default
|
589
|
-
event.stop_propagation
|
590
|
-
event.stop_immediate_propagation
|
591
|
-
end
|
592
|
-
doit
|
593
|
-
}
|
594
|
-
}
|
595
|
-
}
|
596
|
-
],
|
597
|
-
'on_swt_keyup' => {
|
598
|
-
event: 'keyup',
|
599
|
-
event_handler: -> (event_listener) {
|
600
|
-
-> (event) {
|
601
|
-
event.define_singleton_method(:keyLocation) do
|
602
|
-
location = `#{event.to_n}.originalEvent.location`
|
603
|
-
JS_LOCATION_TO_SWT_KEY_LOCATION_MAP[location] || location
|
604
|
-
end
|
605
|
-
event.define_singleton_method(:key_location, &event.method(:keyLocation))
|
606
|
-
event.define_singleton_method(:widget) {myself}
|
607
|
-
event.define_singleton_method(:keyCode) {
|
608
|
-
JS_KEY_CODE_TO_SWT_KEY_CODE_MAP[event.which] || event.which
|
609
|
-
}
|
610
|
-
event.define_singleton_method(:key_code, &event.method(:keyCode))
|
611
|
-
event.define_singleton_method(:character) {event.which.chr}
|
612
|
-
event.define_singleton_method(:stateMask) do
|
613
|
-
state_mask = 0
|
614
|
-
state_mask |= SWTProxy[:alt] if event.alt_key
|
615
|
-
state_mask |= SWTProxy[:ctrl] if event.ctrl_key
|
616
|
-
state_mask |= SWTProxy[:shift] if event.shift_key
|
617
|
-
state_mask |= SWTProxy[:command] if event.meta_key
|
618
|
-
state_mask
|
619
|
-
end
|
620
|
-
event.define_singleton_method(:state_mask, &event.method(:stateMask))
|
621
|
-
doit = true
|
622
|
-
event.define_singleton_method(:doit=) do |value|
|
623
|
-
doit = value
|
624
|
-
end
|
625
|
-
event.define_singleton_method(:doit) { doit }
|
626
|
-
event_listener.call(event)
|
627
|
-
|
628
|
-
# TODO Fix doit false, it's not stopping input
|
629
|
-
unless doit
|
630
|
-
event.prevent
|
631
|
-
event.prevent_default
|
632
|
-
event.stop_propagation
|
633
|
-
event.stop_immediate_propagation
|
634
|
-
end
|
635
|
-
|
636
|
-
doit
|
637
|
-
}
|
638
|
-
}
|
639
|
-
},
|
640
|
-
}
|
641
|
-
end
|
642
|
-
|
643
290
|
def name
|
644
291
|
self.class.name.split('::').last.underscore.sub(/_proxy$/, '').gsub('_', '-')
|
645
292
|
end
|
@@ -767,35 +414,6 @@ module Glimmer
|
|
767
414
|
listener.register
|
768
415
|
listeners_for(keyword) << listener
|
769
416
|
listener
|
770
|
-
# return unless effective_observation_request_to_event_mapping.keys.include?(keyword)
|
771
|
-
# event = nil
|
772
|
-
# delegate = nil
|
773
|
-
# effective_observation_request_to_event_mapping[keyword].to_collection.each do |mapping|
|
774
|
-
# observation_requests[keyword] ||= Set.new
|
775
|
-
# observation_requests[keyword] << original_event_listener
|
776
|
-
# event = mapping[:event]
|
777
|
-
# event_handler = mapping[:event_handler]
|
778
|
-
# event_element_css_selector = mapping[:event_element_css_selector]
|
779
|
-
# potential_event_listener = event_handler&.call(original_event_listener)
|
780
|
-
# event_listener = potential_event_listener || original_event_listener
|
781
|
-
# async_event_listener = proc do |event|
|
782
|
-
## 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 )
|
783
|
-
## maybe consider pushing inside the widget classes instead where needed only or implement universal doit support correctly to bypass this issue
|
784
|
-
## Async::Task.new do
|
785
|
-
# @@widget_handling_listener = self
|
786
|
-
## TODO also make sure to disable all widgets for suspension
|
787
|
-
# event_listener.call(event) unless dialog_ancestor&.event_handling_suspended?
|
788
|
-
# @widget_handling_listener = nil
|
789
|
-
## end
|
790
|
-
# end
|
791
|
-
# the_listener_dom_element = event_element_css_selector ? Element[event_element_css_selector] : listener_dom_element
|
792
|
-
# unless the_listener_dom_element.empty?
|
793
|
-
# the_listener_dom_element.on(event, &async_event_listener)
|
794
|
-
## TODO ensure uniqueness of insertion (perhaps adding equals/hash method to event listener proxy)
|
795
|
-
#
|
796
|
-
# 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)
|
797
|
-
# end
|
798
|
-
# end
|
799
417
|
end
|
800
418
|
|
801
419
|
def remove_event_listener_proxies
|
@@ -806,26 +424,26 @@ module Glimmer
|
|
806
424
|
end
|
807
425
|
|
808
426
|
def data_bind(property, model_binding)
|
809
|
-
|
427
|
+
element_binding_translator = value_converters_for_input_type(type)[:model_to_view]
|
428
|
+
element_binding_parameters = [self, property, element_binding_translator]
|
810
429
|
element_binding = DataBinding::ElementBinding.new(*element_binding_parameters)
|
811
430
|
element_binding.call(model_binding.evaluate_property)
|
812
431
|
#TODO make this options observer dependent and all similar observers in element specific data binding handlers
|
813
432
|
element_binding.observe(model_binding)
|
814
433
|
unless model_binding.binding_options[:read_only]
|
815
434
|
# TODO add guards against nil cases for hash below
|
816
|
-
listener_keyword =
|
817
|
-
|
818
|
-
|
435
|
+
listener_keyword = data_binding_listener_for_element_and_property(keyword, property)
|
436
|
+
if listener_keyword
|
437
|
+
data_binding_read_listener = lambda do |event|
|
438
|
+
view_property_value = send(property)
|
439
|
+
converted_view_property_value = value_converters_for_input_type(type)[:view_to_model].call(view_property_value, model_binding.evaluate_property)
|
440
|
+
model_binding.call(converted_view_property_value)
|
441
|
+
end
|
442
|
+
handle_observation_request(listener_keyword, data_binding_read_listener)
|
819
443
|
end
|
820
|
-
handle_observation_request(listener_keyword, data_binding_read_listener)
|
821
444
|
end
|
822
445
|
end
|
823
446
|
|
824
|
-
def set_attribute(attribute_name, *args)
|
825
|
-
apply_property_type_converters(attribute_name, args)
|
826
|
-
super(attribute_name, *args) # PropertyOwner
|
827
|
-
end
|
828
|
-
|
829
447
|
def respond_to_missing?(method_name, include_private = false)
|
830
448
|
# TODO consider doing more correct checking of availability of properties/methods using native `` ticks
|
831
449
|
property_name = property_name_for(method_name)
|
@@ -870,73 +488,19 @@ module Glimmer
|
|
870
488
|
self
|
871
489
|
end
|
872
490
|
|
873
|
-
def
|
874
|
-
|
875
|
-
value = args.first
|
876
|
-
converter = property_type_converters[attribute_name.to_sym]
|
877
|
-
args[0] = converter.call(value) if converter
|
878
|
-
end
|
879
|
-
# if args.count == 1 && args.first.is_a?(ColorProxy)
|
880
|
-
# g_color = args.first
|
881
|
-
# args[0] = g_color.swt_color
|
882
|
-
# end
|
491
|
+
def data_binding_listener_for_element_and_property(element_keyword, property)
|
492
|
+
data_binding_property_listener_map_for_element(element_keyword)[property]
|
883
493
|
end
|
884
494
|
|
885
|
-
def
|
886
|
-
|
887
|
-
if value.is_a?(Symbol) || value.is_a?(String)
|
888
|
-
ColorProxy.new(value)
|
889
|
-
else
|
890
|
-
value
|
891
|
-
end
|
892
|
-
end
|
893
|
-
@property_type_converters ||= {
|
894
|
-
:background => color_converter,
|
895
|
-
# :background_image => proc do |value|
|
896
|
-
# if value.is_a?(String)
|
897
|
-
# if value.start_with?('uri:classloader')
|
898
|
-
# value = value.sub(/^uri\:classloader\:\//, '')
|
899
|
-
# object = java.lang.Object.new
|
900
|
-
# value = object.java_class.resource_as_stream(value)
|
901
|
-
# value = java.io.BufferedInputStream.new(value)
|
902
|
-
# end
|
903
|
-
# image_data = ImageData.new(value)
|
904
|
-
# on_event_Resize do |resize_event|
|
905
|
-
# new_image_data = image_data.scaledTo(@swt_widget.getSize.x, @swt_widget.getSize.y)
|
906
|
-
# @swt_widget.getBackgroundImage&.remove
|
907
|
-
# @swt_widget.setBackgroundImage(Image.new(@swt_widget.getDisplay, new_image_data))
|
908
|
-
# end
|
909
|
-
# Image.new(@swt_widget.getDisplay, image_data)
|
910
|
-
# else
|
911
|
-
# value
|
912
|
-
# end
|
913
|
-
# end,
|
914
|
-
:foreground => color_converter,
|
915
|
-
# :font => proc do |value|
|
916
|
-
# if value.is_a?(Hash)
|
917
|
-
# font_properties = value
|
918
|
-
# FontProxy.new(self, font_properties).swt_font
|
919
|
-
# else
|
920
|
-
# value
|
921
|
-
# end
|
922
|
-
# end,
|
923
|
-
:text => proc do |value|
|
924
|
-
# if swt_widget.is_a?(Browser)
|
925
|
-
# value.to_s
|
926
|
-
# else
|
927
|
-
value.to_s
|
928
|
-
# end
|
929
|
-
end,
|
930
|
-
# :visible => proc do |value|
|
931
|
-
# !!value
|
932
|
-
# end,
|
933
|
-
}
|
495
|
+
def data_binding_property_listener_map_for_element(element_keyword)
|
496
|
+
data_binding_element_keyword_to_property_listener_map[element_keyword] || {}
|
934
497
|
end
|
935
498
|
|
936
499
|
def data_binding_element_keyword_to_property_listener_map
|
937
500
|
@data_binding_element_keyword_to_property_listener_map ||= {
|
938
501
|
'input' => {
|
939
502
|
'value' => 'oninput',
|
503
|
+
'checked' => 'oninput',
|
940
504
|
},
|
941
505
|
'select' => {
|
942
506
|
'value' => 'onchange',
|
@@ -947,8 +511,106 @@ module Glimmer
|
|
947
511
|
}
|
948
512
|
end
|
949
513
|
|
514
|
+
def value_converters_for_input_type(input_type)
|
515
|
+
input_value_converters[input_type] || {model_to_view: ->(value, old_value) {value}, view_to_model: ->(value, old_value) {value}}
|
516
|
+
end
|
517
|
+
|
518
|
+
def input_value_converters
|
519
|
+
@input_value_converters ||= {
|
520
|
+
'number' => {
|
521
|
+
model_to_view: -> (value, old_value) { value.to_s },
|
522
|
+
view_to_model: -> (value, old_value) {
|
523
|
+
value.include?('.') ? value.to_f : value.to_i
|
524
|
+
},
|
525
|
+
},
|
526
|
+
'range' => {
|
527
|
+
model_to_view: -> (value, old_value) { value.to_s },
|
528
|
+
view_to_model: -> (value, old_value) {
|
529
|
+
value.include?('.') ? value.to_f : value.to_i
|
530
|
+
},
|
531
|
+
},
|
532
|
+
'datetime-local' => {
|
533
|
+
model_to_view: -> (value, old_value) {
|
534
|
+
if value.respond_to?(:strftime)
|
535
|
+
value.strftime(FORMAT_DATETIME)
|
536
|
+
elsif value.is_a?(String) && valid_js_date_string?(value)
|
537
|
+
value
|
538
|
+
else
|
539
|
+
old_value
|
540
|
+
end
|
541
|
+
},
|
542
|
+
view_to_model: -> (value, old_value) {
|
543
|
+
if value.to_s.empty?
|
544
|
+
nil
|
545
|
+
else
|
546
|
+
date = Native(`new Date(Date.parse(#{value}))`)
|
547
|
+
year = Native.call(date, 'getFullYear')
|
548
|
+
month = Native.call(date, 'getMonth') + 1
|
549
|
+
day = Native.call(date, 'getDate')
|
550
|
+
hour = Native.call(date, 'getHours')
|
551
|
+
minute = Native.call(date, 'getMinutes')
|
552
|
+
Time.new(year, month, day, hour, minute)
|
553
|
+
end
|
554
|
+
},
|
555
|
+
},
|
556
|
+
'date' => {
|
557
|
+
model_to_view: -> (value, old_value) {
|
558
|
+
if value.respond_to?(:strftime)
|
559
|
+
value.strftime(FORMAT_DATE)
|
560
|
+
elsif value.is_a?(String) && valid_js_date_string?(value)
|
561
|
+
value
|
562
|
+
else
|
563
|
+
old_value
|
564
|
+
end
|
565
|
+
},
|
566
|
+
view_to_model: -> (value, old_value) {
|
567
|
+
if value.to_s.empty?
|
568
|
+
nil
|
569
|
+
else
|
570
|
+
year, month, day = value.split('-')
|
571
|
+
if old_value
|
572
|
+
Time.new(year, month, day, old_value.hour, old_value.min)
|
573
|
+
else
|
574
|
+
Time.new(year, month, day)
|
575
|
+
end
|
576
|
+
end
|
577
|
+
},
|
578
|
+
},
|
579
|
+
'time' => {
|
580
|
+
model_to_view: -> (value, old_value) {
|
581
|
+
if value.respond_to?(:strftime)
|
582
|
+
value.strftime(FORMAT_TIME)
|
583
|
+
elsif value.is_a?(String) && valid_js_date_string?(value)
|
584
|
+
value
|
585
|
+
else
|
586
|
+
old_value
|
587
|
+
end
|
588
|
+
},
|
589
|
+
view_to_model: -> (value, old_value) {
|
590
|
+
if value.to_s.empty?
|
591
|
+
nil
|
592
|
+
else
|
593
|
+
hour, minute = value.split(':')
|
594
|
+
if old_value
|
595
|
+
Time.new(old_value.year, old_value.month, old_value.day, hour, minute)
|
596
|
+
else
|
597
|
+
now = Time.now
|
598
|
+
Time.new(now.year, now.month, now.day, hour, minute)
|
599
|
+
end
|
600
|
+
end
|
601
|
+
},
|
602
|
+
},
|
603
|
+
}
|
604
|
+
end
|
605
|
+
|
950
606
|
private
|
951
607
|
|
608
|
+
def valid_js_date_string?(string)
|
609
|
+
[REGEX_FORMAT_DATETIME, REGEX_FORMAT_DATE, REGEX_FORMAT_TIME].any? do |format|
|
610
|
+
string.match(format)
|
611
|
+
end
|
612
|
+
end
|
613
|
+
|
952
614
|
def css_cursor
|
953
615
|
SWT_CURSOR_TO_CSS_CURSOR_MAP[@cursor]
|
954
616
|
end
|
data/lib/glimmer/web.rb
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
# TODO double check if the latest Opal implemented everything below already
|
2
1
|
require 'date'
|
3
2
|
require 'time'
|
4
3
|
|
@@ -13,6 +12,10 @@ class DateTime < Date
|
|
13
12
|
end
|
14
13
|
end
|
15
14
|
end
|
15
|
+
|
16
|
+
def now
|
17
|
+
Time.now.to_datetime
|
18
|
+
end
|
16
19
|
|
17
20
|
def to_date
|
18
21
|
@time.to_date
|