hyalite 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +3 -0
  3. data/LICENSE.txt +21 -0
  4. data/README.md +46 -0
  5. data/Rakefile +7 -0
  6. data/client/hyalite.rb +98 -0
  7. data/client/hyalite/adler32.rb +33 -0
  8. data/client/hyalite/browser_event.rb +201 -0
  9. data/client/hyalite/callback_queue.rb +22 -0
  10. data/client/hyalite/component.rb +77 -0
  11. data/client/hyalite/composite_component.rb +237 -0
  12. data/client/hyalite/dom_component.rb +329 -0
  13. data/client/hyalite/dom_operations.rb +17 -0
  14. data/client/hyalite/dom_property.rb +218 -0
  15. data/client/hyalite/dom_property_operations.rb +117 -0
  16. data/client/hyalite/element.rb +17 -0
  17. data/client/hyalite/event_dispatcher.rb +99 -0
  18. data/client/hyalite/event_plugin/change_event_plugin.rb +83 -0
  19. data/client/hyalite/event_plugin/event_plugin_registry.rb +49 -0
  20. data/client/hyalite/event_plugin/simple_event_plugin.rb +276 -0
  21. data/client/hyalite/input_wrapper.rb +94 -0
  22. data/client/hyalite/instance_handles.rb +78 -0
  23. data/client/hyalite/internal_component.rb +11 -0
  24. data/client/hyalite/linked_value_utils.rb +27 -0
  25. data/client/hyalite/mount.rb +285 -0
  26. data/client/hyalite/multi_children.rb +272 -0
  27. data/client/hyalite/reconcile_transaction.rb +41 -0
  28. data/client/hyalite/reconciler.rb +122 -0
  29. data/client/hyalite/short_hand.rb +23 -0
  30. data/client/hyalite/synthetic_event.rb +18 -0
  31. data/client/hyalite/text_component.rb +42 -0
  32. data/client/hyalite/transaction.rb +47 -0
  33. data/client/hyalite/try.rb +7 -0
  34. data/client/hyalite/update_queue.rb +52 -0
  35. data/client/hyalite/updates.rb +129 -0
  36. data/client/hyalite/utils.rb +19 -0
  37. data/example/Gemfile +5 -0
  38. data/example/Gemfile.lock +46 -0
  39. data/example/app/application.rb +27 -0
  40. data/example/config.ru +12 -0
  41. data/example/index.html.haml +8 -0
  42. data/hyalite.gemspec +27 -0
  43. data/lib/hyalite.rb +6 -0
  44. data/lib/hyalite/main.rb +5 -0
  45. data/lib/hyalite/version.rb +3 -0
  46. metadata +145 -0
@@ -0,0 +1,41 @@
1
+ require 'hyalite/transaction'
2
+
3
+ module Hyalite
4
+ class ReconcileTransaction < Transaction
5
+ def initialize
6
+ @mount_ready_wrapper = MountReadyWrapper.new
7
+ super [ @mount_ready_wrapper, EventSuppressionWrapper.new ]
8
+ end
9
+
10
+ def mount_ready
11
+ @mount_ready_wrapper.queue
12
+ end
13
+
14
+ class MountReadyWrapper
15
+ include TransactionWrapper
16
+
17
+ attr_reader :queue
18
+
19
+ def initialize
20
+ @queue = CallbackQueue.new
21
+ end
22
+
23
+ def close
24
+ @queue.notify_all
25
+ end
26
+ end
27
+
28
+ class EventSuppressionWrapper
29
+ include TransactionWrapper
30
+
31
+ def initialize
32
+ @previous_enabled = BrowserEvent.enabled?
33
+ BrowserEvent.enabled = false
34
+ end
35
+
36
+ def close
37
+ BrowserEvent.enabled = @previous_enabled
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,122 @@
1
+ module Hyalite
2
+ module Reconciler
3
+
4
+ SEPARATOR = '.';
5
+ SUBSEPARATOR = ':';
6
+
7
+ class << self
8
+ def mount_component(internal_instance, root_id, mount_ready, context)
9
+ markup = internal_instance.mount_component(root_id, mount_ready, context)
10
+ if internal_instance.current_element.respond_to?(:ref) && internal_instance.current_element.ref
11
+ mount_ready.enqueue do
12
+ internal_instance.current_element.owner.attach_ref(internal_instance.current_element.ref, internal_instance)
13
+ end
14
+ end
15
+ markup
16
+ end
17
+
18
+ def unmount_component(internal_instance)
19
+ #ReactRef.detachRefs(internalInstance, internalInstance._currentElement);
20
+ internal_instance.unmount_component
21
+ end
22
+
23
+ def receive_component(internal_instance, next_element, mount_ready, context)
24
+ prev_element = internal_instance.current_element
25
+
26
+ return if next_element == prev_element && internal_instance.respond_to?(:context) && context == internal_instance.context
27
+
28
+ # refs_changed = ReactRef.should_update_refs(prev_element, next_element)
29
+ #
30
+ # ReactRef.detach_refs(internal_instance, prev_element) if refs_changed
31
+
32
+ internal_instance.receive_component(next_element, mount_ready, context)
33
+
34
+ # transaction.enqueue(attach_refs, internal_instance) if refs_changed
35
+ end
36
+
37
+ def perform_update_if_necessary(internal_instance, mount_ready)
38
+ internal_instance.perform_update_if_necessary(mount_ready)
39
+ end
40
+
41
+ def update_children(prev_children, next_nested_child_nodes, mount_ready, context)
42
+ next_children = flatten_children(next_nested_child_nodes)
43
+ return nil if next_children.nil? && prev_children.nil?
44
+
45
+ next_children.each do |name, next_element|
46
+ prev_child = prev_children && prev_children[name]
47
+ prev_element = prev_child && prev_child.current_element
48
+ if should_update_component(prev_element, next_element)
49
+ receive_component(prev_child, next_element, mount_ready, context)
50
+ next_children[name] = prev_child
51
+ else
52
+ if prev_child
53
+ unmount_component(prev_child, name)
54
+ end
55
+
56
+ next_children[name] = Hyalite.instantiate_component(next_element, nil)
57
+ end
58
+ end
59
+
60
+ prev_children.each do |name, prev_child|
61
+ unless next_children && next_children.has_key?(name)
62
+ unmount_component(prev_children[name])
63
+ end
64
+ end
65
+
66
+ next_children;
67
+ end
68
+
69
+ def flatten_children(nested_child_nodes)
70
+ {}.tap do |res|
71
+ traverse_children(nested_child_nodes, "") do |name, child_node|
72
+ res[name] = child_node if child_node
73
+ end
74
+ end
75
+ end
76
+
77
+ def traverse_children(children, name_so_far)
78
+ children = nil if children == true || children == false
79
+
80
+ if children.nil? || children.is_a?(String) || children.is_a?(Numeric)
81
+ name = name_so_far.empty? ? SEPARATOR + component_key(children, 0) : name_so_far
82
+ yield [name, children]
83
+ return 1
84
+ end
85
+
86
+ case children
87
+ when Array
88
+ children.each_with_index do |child, i|
89
+ next_name = (name_so_far.empty? ? SEPARATOR : name_so_far + SUBSEPARATOR) + component_key(child, i)
90
+ traverse_children(child, next_name) {|n, c| yield [n, c] }
91
+ end
92
+ else
93
+ name = name_so_far.empty? ? SEPARATOR + component_key(children, 0) : name_so_far
94
+ yield [name, children]
95
+ end
96
+ end
97
+
98
+ def component_key(component, index)
99
+ return "$#{component.key}" if component && component.respond_to?(:key) && component.key
100
+ index.to_s(36)
101
+ end
102
+
103
+ def should_update_component(prev_element, next_element)
104
+ if prev_element && next_element
105
+ if prev_element.is_a?(String) || prev_element.is_a?(Numeric)
106
+ return next_element.is_a?(String) || next_element.is_a?(Numeric)
107
+ else
108
+ return prev_element.type == next_element.type && prev_element.key == next_element.key
109
+ end
110
+ end
111
+ false
112
+ end
113
+
114
+ def unmount_children(rendered_children)
115
+ rendered_children.values.each do |rendered_child|
116
+ unmount_component(rendered_child)
117
+ end
118
+ end
119
+ end
120
+
121
+ end
122
+ end
@@ -0,0 +1,23 @@
1
+ module Hyalite
2
+ module Component
3
+ module ShortHand
4
+ TAGS = %w(h1 h2 h3 h4 h5 h6 header footer section div p span code strong a img input button label ul li)
5
+
6
+ def self.included(klass)
7
+ TAGS.each do |tag|
8
+ define_method(tag) do |props, *children|
9
+ Hyalite.create_element(tag, props, *children)
10
+ end
11
+ end
12
+
13
+ klass.extend ClassMethods
14
+ end
15
+
16
+ module ClassMethods
17
+ def el(props, *children)
18
+ Hyalite.create_element(self, props, *children)
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,18 @@
1
+ module Hyalite
2
+ class SyntheticEvent
3
+ attr_reader :event
4
+
5
+ def initialize(event)
6
+ @event = event
7
+ @listeners = []
8
+ end
9
+
10
+ def add_listener(listener, target_id)
11
+ @listeners << [listener, target_id]
12
+ end
13
+
14
+ def each_listener(&block)
15
+ @listeners.each {|listener| yield(listener) }
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,42 @@
1
+ require 'hyalite/internal_component'
2
+
3
+ module Hyalite
4
+ class TextComponent
5
+ include InternalComponent
6
+
7
+ def initialize(text)
8
+ @text = text
9
+ end
10
+
11
+ def current_element
12
+ @text
13
+ end
14
+
15
+ def mount_component(root_id, mount_ready, context)
16
+ @root_node_id = root_id
17
+ @native_node = $document.create_element('span').tap do |node|
18
+ DOMPropertyOperations.set_attribute_for_id(node, root_id)
19
+ Mount.node_id(node)
20
+ node.text = @text
21
+ end
22
+ end
23
+
24
+ def unmount_component
25
+ @native_node = nil
26
+ Mount.purge_id(@root_node_id)
27
+ end
28
+
29
+ def receive_component(next_text, mount_ready)
30
+ if next_text != @text
31
+ @text = next_text
32
+ node.text = @text
33
+ end
34
+ end
35
+
36
+ private
37
+
38
+ def node
39
+ @node ||= Mount.node(@root_node_id)
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,47 @@
1
+ module TransactionWrapper
2
+ def init
3
+ end
4
+
5
+ def close
6
+ end
7
+ end
8
+
9
+ class Transaction
10
+ include TransactionWrapper
11
+
12
+ def initialize(transaction_wrappers = nil, &block)
13
+ @transaction_wrappers = transaction_wrappers || []
14
+ if block_given?
15
+ @close_proc = block
16
+ @transaction_wrappers << self
17
+ end
18
+ end
19
+
20
+ def close
21
+ if @close_proc
22
+ @close_proc.call
23
+ else
24
+ close_all
25
+ end
26
+ end
27
+
28
+ def close_all
29
+ @transaction_wrappers.each do |wrapper|
30
+ wrapper.close
31
+ end
32
+ end
33
+
34
+ def init_all
35
+ @transaction_wrappers.each do |wrapper|
36
+ wrapper.init
37
+ end
38
+ end
39
+
40
+ def perform
41
+ init_all
42
+
43
+ yield(self)
44
+ ensure
45
+ close_all
46
+ end
47
+ end
@@ -0,0 +1,7 @@
1
+ class Object
2
+ def try
3
+ unless self.nil?
4
+ yield self
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,52 @@
1
+ module Hyalite
2
+ class UpdateQueue
3
+ class << self
4
+ def is_mounted(public_instance)
5
+ internal_instance = Hyalite.instance_map[public_instance]
6
+ if internal_instance
7
+ !!internal_instance.rendered_component
8
+ else
9
+ false
10
+ end
11
+ end
12
+
13
+ def enqueue_callback(public_instance, &block)
14
+ internal_instance = Hyalite.instance_map[public_instance]
15
+ return nil unless internal_instance
16
+
17
+ internal_instance.pending_callbacks ||= []
18
+ internal_instance.pending_callbacks << block
19
+ enqueue_update(internal_instance);
20
+ end
21
+
22
+ def enqueue_set_state(public_instance, partial_state)
23
+ internal_instance = Hyalite.instance_map[public_instance]
24
+
25
+ return unless internal_instance
26
+
27
+ queue = internal_instance.pending_state_queue || (internal_instance.pending_state_queue = [])
28
+ queue.push(partial_state)
29
+
30
+ enqueue_update(internal_instance);
31
+ end
32
+
33
+ def enqueue_update(internal_instance)
34
+ Hyalite.updates.enqueue_update(internal_instance)
35
+ end
36
+
37
+ def enqueue_element_internal(internal_instance, new_element)
38
+ internal_instance.pending_element = new_element
39
+ enqueue_update(internal_instance)
40
+ end
41
+
42
+ def enqueue_callback_internal(internal_instance, &callback)
43
+ if internal_instance.pending_callbacks
44
+ internal_instance.pending_callbacks << callback
45
+ else
46
+ internal_instance.pending_callbacks = [callback]
47
+ end
48
+ enqueue_update(internal_instance)
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,129 @@
1
+ require 'hyalite/transaction'
2
+ require 'hyalite/callback_queue'
3
+ require 'hyalite/reconciler'
4
+ require 'hyalite/reconcile_transaction'
5
+
6
+ module Hyalite
7
+ class Updates
8
+ class NestedUpdate
9
+ include TransactionWrapper
10
+
11
+ def initialize(dirty_components)
12
+ @dirty_components = dirty_components
13
+ end
14
+
15
+ def init
16
+ @init_length = @dirty_components.length
17
+ end
18
+
19
+ def close
20
+ if @dirty_components.length - @init_length > 0
21
+ @dirty_components.shift(@init_length)
22
+ else
23
+ @dirty_components.clear
24
+ end
25
+ end
26
+ end
27
+
28
+ class UpdateQueueing
29
+ include TransactionWrapper
30
+
31
+ def initialize(queue)
32
+ @queue = queue
33
+ end
34
+
35
+ def close
36
+ @queue.notify_all
37
+ end
38
+ end
39
+
40
+ attr_reader :reconcile_transaction
41
+
42
+ def initialize
43
+ @dirty_components = []
44
+
45
+ @is_batching_updates = false
46
+
47
+ @callback_queue = CallbackQueue.new
48
+ @asap_callback_queue = CallbackQueue.new
49
+ @asap_enqueued = false
50
+ @reconcile_transaction = ReconcileTransaction.new
51
+
52
+ @flush_transaction = Transaction.new([NestedUpdate.new(@dirty_components), UpdateQueueing.new(@callback_queue), @reconcile_transaction])
53
+ end
54
+
55
+ def enqueue_update(component)
56
+ unless @is_batching_updates
57
+ batched_updates do
58
+ enqueue_update(component)
59
+ end
60
+ end
61
+
62
+ @dirty_components << component
63
+ end
64
+
65
+ def batched_updates
66
+ already_batching_updates = @is_batching_updates
67
+
68
+ @is_batching_updates = true
69
+
70
+
71
+ if already_batching_updates
72
+ yield
73
+ else
74
+ transaction = Transaction.new do
75
+ flush_batched_updates
76
+ @is_batching_updates = false
77
+ end
78
+
79
+ transaction.perform do
80
+ yield
81
+ end
82
+ end
83
+ end
84
+
85
+ def flush_batched_updates
86
+ while @dirty_components.length > 0 || @asap_enqueued
87
+ if @dirty_components.length > 0
88
+ @flush_transaction.perform do |transaction|
89
+ run_batched_updates(transaction)
90
+ end
91
+ end
92
+
93
+ if @asap_enqueued
94
+ @asap_enqueued = false
95
+ @asap_callback_queue.notify_all
96
+ next
97
+ end
98
+ end
99
+ end
100
+
101
+ def run_batched_updates(transaction)
102
+ @dirty_components.sort{|c1, c2| c1.mount_order <=> c2.mount_order}.each do |component|
103
+ callbacks = component.pending_callbacks
104
+ component.pending_callbacks = nil
105
+
106
+ Reconciler.perform_update_if_necessary(component, @reconcile_transaction.mount_ready)
107
+
108
+ if callbacks
109
+ callbacks.each do |callback|
110
+ @callback_queue.enqueue(callback)
111
+ end
112
+ end
113
+ end
114
+ end
115
+
116
+ def asap(&callback)
117
+ @asap_callback_queue.enqueue(&callback)
118
+ @asap_enqueued = true
119
+ end
120
+
121
+ def mount_ready
122
+ @reconcile_transaction.mount_ready
123
+ end
124
+ end
125
+
126
+ def self.updates
127
+ @updates ||= Updates.new
128
+ end
129
+ end