hyalite 0.0.1
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 +7 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +21 -0
- data/README.md +46 -0
- data/Rakefile +7 -0
- data/client/hyalite.rb +98 -0
- data/client/hyalite/adler32.rb +33 -0
- data/client/hyalite/browser_event.rb +201 -0
- data/client/hyalite/callback_queue.rb +22 -0
- data/client/hyalite/component.rb +77 -0
- data/client/hyalite/composite_component.rb +237 -0
- data/client/hyalite/dom_component.rb +329 -0
- data/client/hyalite/dom_operations.rb +17 -0
- data/client/hyalite/dom_property.rb +218 -0
- data/client/hyalite/dom_property_operations.rb +117 -0
- data/client/hyalite/element.rb +17 -0
- data/client/hyalite/event_dispatcher.rb +99 -0
- data/client/hyalite/event_plugin/change_event_plugin.rb +83 -0
- data/client/hyalite/event_plugin/event_plugin_registry.rb +49 -0
- data/client/hyalite/event_plugin/simple_event_plugin.rb +276 -0
- data/client/hyalite/input_wrapper.rb +94 -0
- data/client/hyalite/instance_handles.rb +78 -0
- data/client/hyalite/internal_component.rb +11 -0
- data/client/hyalite/linked_value_utils.rb +27 -0
- data/client/hyalite/mount.rb +285 -0
- data/client/hyalite/multi_children.rb +272 -0
- data/client/hyalite/reconcile_transaction.rb +41 -0
- data/client/hyalite/reconciler.rb +122 -0
- data/client/hyalite/short_hand.rb +23 -0
- data/client/hyalite/synthetic_event.rb +18 -0
- data/client/hyalite/text_component.rb +42 -0
- data/client/hyalite/transaction.rb +47 -0
- data/client/hyalite/try.rb +7 -0
- data/client/hyalite/update_queue.rb +52 -0
- data/client/hyalite/updates.rb +129 -0
- data/client/hyalite/utils.rb +19 -0
- data/example/Gemfile +5 -0
- data/example/Gemfile.lock +46 -0
- data/example/app/application.rb +27 -0
- data/example/config.ru +12 -0
- data/example/index.html.haml +8 -0
- data/hyalite.gemspec +27 -0
- data/lib/hyalite.rb +6 -0
- data/lib/hyalite/main.rb +5 -0
- data/lib/hyalite/version.rb +3 -0
- metadata +145 -0
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'hyalite/dom_property_operations'
|
2
|
+
|
3
|
+
module Hyalite
|
4
|
+
module DOMOperations
|
5
|
+
class << self
|
6
|
+
def update_property_by_id(id, name, value)
|
7
|
+
node = Mount.node(id)
|
8
|
+
|
9
|
+
if value
|
10
|
+
DOMPropertyOperations.set_value_for_property(node, name, value)
|
11
|
+
else
|
12
|
+
DOMPropertyOperations.delete_value_for_property(node, name)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,218 @@
|
|
1
|
+
module Hyalite
|
2
|
+
module DOMProperty
|
3
|
+
ID_ATTRIBUTE_NAME = 'data-hyalite-id'
|
4
|
+
|
5
|
+
MUST_USE_ATTRIBUTE = 1
|
6
|
+
MUST_USE_PROPERTY = 2
|
7
|
+
HAS_BOOLEAN_VALUE = 4
|
8
|
+
HAS_SIDE_EFFECTS = 8
|
9
|
+
HAS_NUMERIC_VALUE = 16
|
10
|
+
HAS_POSITIVE_NUMERIC_VALUE = 32
|
11
|
+
HAS_OVERLOADED_BOOLEAN_VALUE = 64
|
12
|
+
|
13
|
+
PROPERTIES = {
|
14
|
+
###############################
|
15
|
+
### Standard Properties
|
16
|
+
###############################
|
17
|
+
accept: nil,
|
18
|
+
acceptCharset: nil,
|
19
|
+
accessKey: nil,
|
20
|
+
action: nil,
|
21
|
+
allowFullScreen: MUST_USE_ATTRIBUTE | HAS_BOOLEAN_VALUE,
|
22
|
+
allowTransparency: MUST_USE_ATTRIBUTE,
|
23
|
+
alt: nil,
|
24
|
+
async: HAS_BOOLEAN_VALUE,
|
25
|
+
autoComplete: nil,
|
26
|
+
# autoFocus is polyfilled/normalized by AutoFocusUtils
|
27
|
+
# autoFocus: HAS_BOOLEAN_VALUE,
|
28
|
+
autoPlay: HAS_BOOLEAN_VALUE,
|
29
|
+
capture: MUST_USE_ATTRIBUTE | HAS_BOOLEAN_VALUE,
|
30
|
+
cellPadding: nil,
|
31
|
+
cellSpacing: nil,
|
32
|
+
charSet: MUST_USE_ATTRIBUTE,
|
33
|
+
challenge: MUST_USE_ATTRIBUTE,
|
34
|
+
checked: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE,
|
35
|
+
classID: MUST_USE_ATTRIBUTE,
|
36
|
+
className: MUST_USE_PROPERTY,
|
37
|
+
cols: MUST_USE_ATTRIBUTE | HAS_POSITIVE_NUMERIC_VALUE,
|
38
|
+
colSpan: nil,
|
39
|
+
content: nil,
|
40
|
+
contentEditable: nil,
|
41
|
+
contextMenu: MUST_USE_ATTRIBUTE,
|
42
|
+
controls: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE,
|
43
|
+
coords: nil,
|
44
|
+
crossOrigin: nil,
|
45
|
+
data: nil, # For `<object />` acts as `src`.
|
46
|
+
dateTime: MUST_USE_ATTRIBUTE,
|
47
|
+
defer: HAS_BOOLEAN_VALUE,
|
48
|
+
dir: nil,
|
49
|
+
disabled: MUST_USE_ATTRIBUTE | HAS_BOOLEAN_VALUE,
|
50
|
+
download: HAS_OVERLOADED_BOOLEAN_VALUE,
|
51
|
+
draggable: nil,
|
52
|
+
encType: nil,
|
53
|
+
form: MUST_USE_ATTRIBUTE,
|
54
|
+
formAction: MUST_USE_ATTRIBUTE,
|
55
|
+
formEncType: MUST_USE_ATTRIBUTE,
|
56
|
+
formMethod: MUST_USE_ATTRIBUTE,
|
57
|
+
formNoValidate: HAS_BOOLEAN_VALUE,
|
58
|
+
formTarget: MUST_USE_ATTRIBUTE,
|
59
|
+
frameBorder: MUST_USE_ATTRIBUTE,
|
60
|
+
headers: nil,
|
61
|
+
height: MUST_USE_ATTRIBUTE,
|
62
|
+
hidden: MUST_USE_ATTRIBUTE | HAS_BOOLEAN_VALUE,
|
63
|
+
high: nil,
|
64
|
+
href: nil,
|
65
|
+
hrefLang: nil,
|
66
|
+
htmlFor: nil,
|
67
|
+
httpEquiv: nil,
|
68
|
+
icon: nil,
|
69
|
+
id: MUST_USE_PROPERTY,
|
70
|
+
inputMode: MUST_USE_ATTRIBUTE,
|
71
|
+
is: MUST_USE_ATTRIBUTE,
|
72
|
+
keyParams: MUST_USE_ATTRIBUTE,
|
73
|
+
keyType: MUST_USE_ATTRIBUTE,
|
74
|
+
label: nil,
|
75
|
+
lang: nil,
|
76
|
+
list: MUST_USE_ATTRIBUTE,
|
77
|
+
loop: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE,
|
78
|
+
low: nil,
|
79
|
+
manifest: MUST_USE_ATTRIBUTE,
|
80
|
+
marginHeight: nil,
|
81
|
+
marginWidth: nil,
|
82
|
+
max: nil,
|
83
|
+
maxLength: MUST_USE_ATTRIBUTE,
|
84
|
+
media: MUST_USE_ATTRIBUTE,
|
85
|
+
mediaGroup: nil,
|
86
|
+
method: nil,
|
87
|
+
min: nil,
|
88
|
+
minLength: MUST_USE_ATTRIBUTE,
|
89
|
+
multiple: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE,
|
90
|
+
muted: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE,
|
91
|
+
name: nil,
|
92
|
+
noValidate: HAS_BOOLEAN_VALUE,
|
93
|
+
open: HAS_BOOLEAN_VALUE,
|
94
|
+
optimum: nil,
|
95
|
+
pattern: nil,
|
96
|
+
placeholder: nil,
|
97
|
+
poster: nil,
|
98
|
+
preload: nil,
|
99
|
+
radioGroup: nil,
|
100
|
+
readOnly: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE,
|
101
|
+
rel: nil,
|
102
|
+
required: HAS_BOOLEAN_VALUE,
|
103
|
+
role: MUST_USE_ATTRIBUTE,
|
104
|
+
rows: MUST_USE_ATTRIBUTE | HAS_POSITIVE_NUMERIC_VALUE,
|
105
|
+
rowSpan: nil,
|
106
|
+
sandbox: nil,
|
107
|
+
scope: nil,
|
108
|
+
scoped: HAS_BOOLEAN_VALUE,
|
109
|
+
scrolling: nil,
|
110
|
+
seamless: MUST_USE_ATTRIBUTE | HAS_BOOLEAN_VALUE,
|
111
|
+
selected: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE,
|
112
|
+
shape: nil,
|
113
|
+
size: MUST_USE_ATTRIBUTE | HAS_POSITIVE_NUMERIC_VALUE,
|
114
|
+
sizes: MUST_USE_ATTRIBUTE,
|
115
|
+
span: HAS_POSITIVE_NUMERIC_VALUE,
|
116
|
+
spellCheck: nil,
|
117
|
+
src: nil,
|
118
|
+
srcDoc: MUST_USE_PROPERTY,
|
119
|
+
srcSet: MUST_USE_ATTRIBUTE,
|
120
|
+
start: HAS_NUMERIC_VALUE,
|
121
|
+
step: nil,
|
122
|
+
style: nil,
|
123
|
+
tabIndex: nil,
|
124
|
+
target: nil,
|
125
|
+
title: nil,
|
126
|
+
type: nil,
|
127
|
+
useMap: nil,
|
128
|
+
value: MUST_USE_PROPERTY | HAS_SIDE_EFFECTS,
|
129
|
+
width: MUST_USE_ATTRIBUTE,
|
130
|
+
wmode: MUST_USE_ATTRIBUTE,
|
131
|
+
|
132
|
+
###############################
|
133
|
+
### Non-standard Properties
|
134
|
+
###############################
|
135
|
+
# autoCapitalize and autoCorrect are supported in Mobile Safari for
|
136
|
+
# keyboard hints.
|
137
|
+
autoCapitalize: nil,
|
138
|
+
autoCorrect: nil,
|
139
|
+
# itemProp, itemScope, itemType are for
|
140
|
+
# Microdata support. See http:#schema.org/docs/gs.html
|
141
|
+
itemProp: MUST_USE_ATTRIBUTE,
|
142
|
+
itemScope: MUST_USE_ATTRIBUTE | HAS_BOOLEAN_VALUE,
|
143
|
+
itemType: MUST_USE_ATTRIBUTE,
|
144
|
+
# itemID and itemRef are for Microdata support as well but
|
145
|
+
# only specified in the the WHATWG spec document. See
|
146
|
+
# https:#html.spec.whatwg.org/multipage/microdata.html#microdata-dom-api
|
147
|
+
itemID: MUST_USE_ATTRIBUTE,
|
148
|
+
itemRef: MUST_USE_ATTRIBUTE,
|
149
|
+
# property is supported for OpenGraph in meta tags.
|
150
|
+
property: nil,
|
151
|
+
# IE-only attribute that specifies security restrictions on an iframe
|
152
|
+
# as an alternative to the sandbox attribute on IE<10
|
153
|
+
security: MUST_USE_ATTRIBUTE,
|
154
|
+
# IE-only attribute that controls focus behavior
|
155
|
+
unselectable: MUST_USE_ATTRIBUTE,
|
156
|
+
}
|
157
|
+
|
158
|
+
DOM_ATTRIBUTE_NAMES = {
|
159
|
+
acceptCharset: 'accept-charset',
|
160
|
+
className: 'class',
|
161
|
+
htmlFor: 'for',
|
162
|
+
httpEquiv: 'http-equiv'
|
163
|
+
}
|
164
|
+
|
165
|
+
class << self
|
166
|
+
def property_info(name)
|
167
|
+
return nil unless PROPERTIES.has_key? name.to_sym
|
168
|
+
|
169
|
+
@property_info ||= {}
|
170
|
+
unless @property_info.has_key? name
|
171
|
+
property = PROPERTIES[name.to_sym]
|
172
|
+
|
173
|
+
attribute_name = DOM_ATTRIBUTE_NAMES.has_key?(name) ? DOM_ATTRIBUTE_NAMES[name] : name.downcase
|
174
|
+
|
175
|
+
@property_info[name] = {
|
176
|
+
attribute_name: attribute_name,
|
177
|
+
attribute_namespace: nil,
|
178
|
+
property_name: name,
|
179
|
+
mutation_method: nil,
|
180
|
+
must_use_attribute: property && property & MUST_USE_ATTRIBUTE > 0,
|
181
|
+
must_use_property: property && property & MUST_USE_PROPERTY > 0,
|
182
|
+
has_side_effects: property && property & HAS_SIDE_EFFECTS > 0,
|
183
|
+
has_boolean_value: property && property & HAS_BOOLEAN_VALUE > 0,
|
184
|
+
has_numeric_value: property && property & HAS_NUMERIC_VALUE > 0,
|
185
|
+
has_positive_numeric_value: property && property & HAS_POSITIVE_NUMERIC_VALUE > 0,
|
186
|
+
has_overloaded_boolean_value: property && property & HAS_OVERLOADED_BOOLEAN_VALUE > 0
|
187
|
+
}
|
188
|
+
end
|
189
|
+
|
190
|
+
@property_info[name]
|
191
|
+
end
|
192
|
+
|
193
|
+
def include?(name)
|
194
|
+
PROPERTIES.has_key? name
|
195
|
+
end
|
196
|
+
|
197
|
+
def default_value_for_property(node_name, prop)
|
198
|
+
node_defaults = default_value_cache[node_name]
|
199
|
+
unless node_defaults
|
200
|
+
default_value_cache[node_name] = node_defaults = {}
|
201
|
+
end
|
202
|
+
unless node_defaults.include? prop
|
203
|
+
test_element = $document.create_element(node_name)
|
204
|
+
node_defaults[prop] = test_element[prop]
|
205
|
+
end
|
206
|
+
node_defaults[prop]
|
207
|
+
end
|
208
|
+
|
209
|
+
def default_value_cache
|
210
|
+
@default_value_cache ||= {}
|
211
|
+
end
|
212
|
+
|
213
|
+
def is_custom_attribute(attribute_name)
|
214
|
+
/^(data|aria)-[a-z_][a-z\d_.\-]*$/ =~ attribute_name
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
require 'hyalite/dom_property'
|
2
|
+
require 'hyalite/utils'
|
3
|
+
|
4
|
+
module Hyalite
|
5
|
+
module DOMPropertyOperations
|
6
|
+
VALID_ATTRIBUTE_NAME_REGEX = /^[a-zA-Z_][a-zA-Z_\.\-\d]*$/
|
7
|
+
|
8
|
+
class << self
|
9
|
+
def create_markup_for_property(element, name, value)
|
10
|
+
property_info = DOMProperty.property_info(name)
|
11
|
+
if property_info
|
12
|
+
return if should_ignore_value(property_info, value)
|
13
|
+
|
14
|
+
attribute_name = property_info[:attribute_name]
|
15
|
+
if property_info[:has_boolean_value] || property_info[:has_overloaded_boolean_value] && value == true
|
16
|
+
element[attribute_name] = ""
|
17
|
+
return
|
18
|
+
end
|
19
|
+
|
20
|
+
element[attribute_name]=Hyalite.escape_text_content_for_browser(value)
|
21
|
+
elsif DOMProperty.is_custom_attribute(name)
|
22
|
+
return if value.nil?
|
23
|
+
|
24
|
+
element[name]=Hyalite.escape_text_content_for_browser(value)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def create_markup_for_styles(element, styles)
|
29
|
+
element.style(styles)
|
30
|
+
end
|
31
|
+
|
32
|
+
def create_markup_for_custom_attribute(element, name, value)
|
33
|
+
return if (!is_attribute_name_safe(name) || value == null)
|
34
|
+
|
35
|
+
element[name]=Hyalite.escape_text_content_for_browser(value)
|
36
|
+
end
|
37
|
+
|
38
|
+
def is_attribute_name_safe(attribute_name)
|
39
|
+
@illegal_attribute_name_cache ||= {}
|
40
|
+
@validated_attribute_name_cache ||= {}
|
41
|
+
|
42
|
+
return true if @validated_attribute_name_cache.has_key? attribute_name
|
43
|
+
return false if @illegal_attribute_name_cache.has_key? attribute_name
|
44
|
+
|
45
|
+
if VALID_ATTRIBUTE_NAME_REGEX =~ attribute_name
|
46
|
+
@validated_attribute_name_cache[attributeName] = true
|
47
|
+
return true
|
48
|
+
end
|
49
|
+
|
50
|
+
@illegal_attribute_name_cache[attributeName] = true
|
51
|
+
false
|
52
|
+
end
|
53
|
+
|
54
|
+
def set_value_for_property(node, name, value)
|
55
|
+
property_info = DOMProperty.property_info(name)
|
56
|
+
if property_info
|
57
|
+
mutation_method = property_info[:mutation_method]
|
58
|
+
if mutation_method
|
59
|
+
mutation_method.call(node, value);
|
60
|
+
elsif should_ignore_value(property_info, value)
|
61
|
+
delete_value_for_property(node, name)
|
62
|
+
elsif property_info[:must_use_attribute]
|
63
|
+
attribute_name = property_info[:attribute_name]
|
64
|
+
namespace = property_info[:attribute_namespace]
|
65
|
+
if namespace
|
66
|
+
node[attribute_name, {namespace: namespace}] = value.to_s
|
67
|
+
elsif property_info[:has_boolean_value] ||
|
68
|
+
(property_info[:has_overloaded_boolean_value] && value == true)
|
69
|
+
node[attribute_name] = ''
|
70
|
+
else
|
71
|
+
node[attribute_name] = value.to_s
|
72
|
+
end
|
73
|
+
else
|
74
|
+
prop_name = property_info[:property_name]
|
75
|
+
unless property_info[:has_side_effects] && `'' + node[#{prop_name}]` == value.to_s
|
76
|
+
`node.native[#{prop_name}] = value`
|
77
|
+
end
|
78
|
+
end
|
79
|
+
elsif DOMProperty.is_custom_attribute(name)
|
80
|
+
DOMPropertyOperations.set_value_for_attribute(node, name, value)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def delete_value_for_property(node, name)
|
85
|
+
property_info = DOMProperty.property_info(name)
|
86
|
+
if property_info
|
87
|
+
mutation_method = property_info[:mutation_method]
|
88
|
+
if mutation_method
|
89
|
+
mutation_method.call(node, nil)
|
90
|
+
elsif property_info[:must_use_attribute]
|
91
|
+
node.remove_attribute(property_info[:attribute_name])
|
92
|
+
else
|
93
|
+
prop_name = property_info[:property_name]
|
94
|
+
default_value = DOMProperty.default_value_for_property(node.node_name, prop_name)
|
95
|
+
unless property_info[:has_side_effects] || (node[prop_name].to_s == default_value)
|
96
|
+
node[prop_name] = default_value
|
97
|
+
end
|
98
|
+
end
|
99
|
+
elsif DOMProperty.is_custom_attribute(name)
|
100
|
+
node.remove_attribute(name)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def set_attribute_for_id(node, id)
|
105
|
+
node[DOMProperty::ID_ATTRIBUTE_NAME] = id
|
106
|
+
end
|
107
|
+
|
108
|
+
def should_ignore_value(property_info, value)
|
109
|
+
value.nil? ||
|
110
|
+
(property_info[:has_boolean_value] && !value) ||
|
111
|
+
(property_info[:has_numeric_value] && value.nan?) ||
|
112
|
+
(property_info[:has_positive_numeric_value] && (value < 1)) ||
|
113
|
+
(property_info[:has_overloaded_boolean_value] && value == false)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Hyalite
|
2
|
+
class ElementObject
|
3
|
+
attr_reader :type, :key, :ref, :props, :owner
|
4
|
+
|
5
|
+
def initialize(type, key, ref, owner, props)
|
6
|
+
@type = type
|
7
|
+
@key = key
|
8
|
+
@ref = ref
|
9
|
+
@owner = owner
|
10
|
+
@props = props
|
11
|
+
end
|
12
|
+
|
13
|
+
def inspect
|
14
|
+
"<#{type} #{props.inspect} />"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
module Hyalite
|
2
|
+
class EventDispatcher
|
3
|
+
def initialize(&extract_events)
|
4
|
+
@enabled = true
|
5
|
+
@listener_bank = {}
|
6
|
+
@extract_events = extract_events
|
7
|
+
@event_queue = []
|
8
|
+
end
|
9
|
+
|
10
|
+
def enabled?
|
11
|
+
@enabled
|
12
|
+
end
|
13
|
+
|
14
|
+
def enabled=(enabled)
|
15
|
+
@enabled = enabled
|
16
|
+
end
|
17
|
+
|
18
|
+
def trap_bubbled_event(top_level_type, handler_base_name, element)
|
19
|
+
return nil unless element
|
20
|
+
|
21
|
+
element.on handler_base_name do |event|
|
22
|
+
dispatch_event(top_level_type, event)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def find_parent(node)
|
27
|
+
node_id = Mount.node_id(node)
|
28
|
+
root_id = InstanceHandles.root_id_from_node_id(node_id)
|
29
|
+
container = Mount.container_for_id(root_id)
|
30
|
+
Mount.find_first_hyalite_dom(container)
|
31
|
+
end
|
32
|
+
|
33
|
+
def handle_top_level(book_keeping)
|
34
|
+
ancestor = Mount.find_first_hyalite_dom(book_keeping.event.target)
|
35
|
+
while ancestor
|
36
|
+
book_keeping.ancestors << ancestor
|
37
|
+
ancestor = find_parent(ancestor)
|
38
|
+
end
|
39
|
+
|
40
|
+
book_keeping.ancestors.each do |top_level_target|
|
41
|
+
top_level_target_id = Mount.node_id(top_level_target) || ''
|
42
|
+
synthetic_events = @extract_events.call(
|
43
|
+
book_keeping.top_level_type,
|
44
|
+
top_level_target,
|
45
|
+
top_level_target_id,
|
46
|
+
book_keeping.event,
|
47
|
+
).flatten
|
48
|
+
|
49
|
+
synthetic_events.each do |synthetic_event|
|
50
|
+
synthetic_event.each_listener do |listener, dom_id|
|
51
|
+
target = Mount.node(dom_id);
|
52
|
+
listener.call(synthetic_event.event, target)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def dispatch_event(top_level_type, event)
|
59
|
+
return unless @enabled
|
60
|
+
|
61
|
+
book_keeping = TopLevelCallbackBookKeeping.new(top_level_type, event)
|
62
|
+
Hyalite.updates.batched_updates { handle_top_level(book_keeping) }
|
63
|
+
end
|
64
|
+
|
65
|
+
def put_listener(id, registration_name, listener)
|
66
|
+
listeners = @listener_bank[registration_name] ||= {}
|
67
|
+
listeners[id] = listener
|
68
|
+
end
|
69
|
+
|
70
|
+
def get_listener(id, registration_name)
|
71
|
+
@listener_bank[registration_name].try {|listeners| listeners[id] }
|
72
|
+
end
|
73
|
+
|
74
|
+
def delete_listener(id, registration_name)
|
75
|
+
if @listener_bank.has_key? registration_name
|
76
|
+
yield(id, registration_name)
|
77
|
+
@listener_bank[registration_name].delete(id)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def delete_all_listeners(id, &block)
|
82
|
+
@listener_bank.each do |registration_name, bank|
|
83
|
+
next unless bank[id]
|
84
|
+
yield(id, registration_name) if block_given?
|
85
|
+
bank.delete(id)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
class TopLevelCallbackBookKeeping
|
90
|
+
attr_reader :event, :top_level_type, :ancestors
|
91
|
+
|
92
|
+
def initialize(top_level_type, event)
|
93
|
+
@top_level_type = top_level_type
|
94
|
+
@event = event
|
95
|
+
@ancestors = []
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|