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
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d206b2b8ac5745b538a38390d5969f25a938bee0
4
+ data.tar.gz: 27970cdf165567fad934301c261f7c9e6b4abf2a
5
+ SHA512:
6
+ metadata.gz: c0fb1bd06c6e90c252aa874a76e0361136c9dbe0c2f3ff6f48ea2e2027df184daa6b64b2475225bd28f683b59c1be2bc282dbd756c9fb10a14074045f602cb75
7
+ data.tar.gz: 9bd0d1ea6396e00a98247f8024990ccbc2bbca354cf401c0ead9345720bb9258b7801d26a816f243ab52c2ce0e643d0f3ba8902f7af3dd763b40029eb4a3ad12
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 youchan
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,46 @@
1
+ Hyalite
2
+ ====
3
+
4
+ This is ruby virtual DOM implementation using opal. It is inspired by react.js.
5
+
6
+ Example
7
+ ----
8
+
9
+ ```ruby
10
+ require_relative 'hyalite.rb'
11
+ require 'browser/interval'
12
+
13
+ class ExampleView
14
+ include Hyalite::Component
15
+
16
+ def initial_state
17
+ @count = 0
18
+ { now: @count }
19
+ end
20
+
21
+ def component_did_mount
22
+ every(1) do
23
+ set_state({ now: @count += 1 })
24
+ end
25
+ end
26
+
27
+ def render
28
+ Hyalite.create_element("div", nil,
29
+ Hyalite.create_element("h2", nil, @props[:title]),
30
+ Hyalite.create_element("h3", nil, "count = #{@state[:now]}"))
31
+ end
32
+ end
33
+
34
+ $document.ready do
35
+ Hyalite.render(Hyalite.create_element(ExampleView, {title: "Hyalite counter example"}), $document['.container'])
36
+ end
37
+ ```
38
+
39
+ How to execute this example is the following.
40
+
41
+ ```
42
+ > cd example
43
+ > rackup
44
+ ```
45
+
46
+ Open url `http://localhost:9292`.
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
7
+
data/client/hyalite.rb ADDED
@@ -0,0 +1,98 @@
1
+ require 'opal'
2
+ require 'browser'
3
+ require 'hyalite/transaction'
4
+ require 'hyalite/adler32'
5
+ require 'hyalite/mount'
6
+ require 'hyalite/element'
7
+ require 'hyalite/dom_component'
8
+ require 'hyalite/text_component'
9
+
10
+ module Hyalite
11
+ class << self
12
+ RESERVED_PROPS = [:key, :ref, :children]
13
+
14
+ def create_element(type, config = nil, *children)
15
+ key = nil
16
+ ref = nil
17
+
18
+ props = {}
19
+
20
+ if config
21
+ key = config[:key]
22
+ ref = config[:ref]
23
+
24
+ config.each do |name, value|
25
+ unless RESERVED_PROPS.include?(name)
26
+ props[name] = config[name];
27
+ end
28
+ end
29
+ end
30
+
31
+ props[:children] = case children.length
32
+ when 0
33
+ nil
34
+ when 1
35
+ children.first
36
+ else
37
+ children
38
+ end
39
+
40
+ ElementObject.new(type, key, ref, Hyalite.current_owner, props)
41
+ end
42
+
43
+ def instantiate_component(node)
44
+ node = EmptyComponent.empty_element if node.nil?
45
+
46
+ case node
47
+ when ElementObject
48
+ case node.type
49
+ when String
50
+ DOMComponent.new node
51
+ when Class
52
+ if node.type.include?(InternalComponent)
53
+ node.type.new
54
+ elsif node.type.include?(Component)
55
+ CompositeComponent.new node
56
+ else
57
+ raise "Encountered invalid type of Hyalite node. type: #{node.type}"
58
+ end
59
+ end
60
+ when String, Numeric
61
+ TextComponent.new node
62
+ when EmptyComponent
63
+ CompositeComponent.new node
64
+ else
65
+ raise "Encountered invalid Hyalite node: #{node}"
66
+ end
67
+ end
68
+
69
+ def render(next_element, container, &block)
70
+ Mount.render_subtree_into_container(nil, next_element, container, &block);
71
+ end
72
+
73
+ def instance_map
74
+ @instance_map ||= {}
75
+ end
76
+
77
+ def current_owner(current_owner = nil)
78
+ if current_owner && block_given?
79
+ begin
80
+ @current_owner = current_owner
81
+ yield(@current_owner)
82
+ ensure
83
+ @current_owner = nil
84
+ end
85
+ else
86
+ @current_owner
87
+ end
88
+ end
89
+
90
+ def find_dom_node(component_or_element)
91
+ return component_or_element if component_or_element.respond_to?(:node_type) && component_or_element.node_type == Browser::DOM::Node::ELEMENT_NODE
92
+
93
+ if instance_map.has_key?(component_or_element)
94
+ return Mount.node(instance_map[component_or_element].root_node_id)
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,33 @@
1
+ module Adler32
2
+ MOD = 65521
3
+
4
+ class << self
5
+ def calc(*args)
6
+ a = 1
7
+ b = 0
8
+ c = 1000
9
+
10
+ args.each do |str|
11
+ throw ArgumentError, "Only string can be passed: #{str.inspect}:#{str.class}" unless str.is_a? String
12
+
13
+ str.each_char do |char|
14
+ a += char.ord
15
+ b += a
16
+ c -= 1
17
+ if c <= 0
18
+ a %= MOD
19
+ b %= MOD
20
+ c = 1000
21
+ end
22
+ end
23
+ end
24
+ a %= MOD
25
+ b %= MOD
26
+ (b << 16) | a
27
+ end
28
+
29
+ def checksum(*args)
30
+ "%08x" % calc(*args)
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,201 @@
1
+ require 'set'
2
+ require 'math'
3
+ require 'hyalite/event_dispatcher'
4
+ require 'hyalite/synthetic_event'
5
+ require 'hyalite/event_plugin/event_plugin_registry'
6
+ require 'hyalite/event_plugin/simple_event_plugin'
7
+ require 'hyalite/event_plugin/change_event_plugin'
8
+
9
+ module Hyalite
10
+ module BrowserEvent
11
+ TOP_EVENT_MAPPING = {
12
+ topBlur: 'blur',
13
+ topChange: 'change',
14
+ topClick: 'click',
15
+ topCompositionEnd: 'compositionend',
16
+ topCompositionStart: 'compositionstart',
17
+ topCompositionUpdate: 'compositionupdate',
18
+ topContextMenu: 'contextmenu',
19
+ topCopy: 'copy',
20
+ topCut: 'cut',
21
+ topDoubleClick: 'dblclick',
22
+ topDrag: 'drag',
23
+ topDragEnd: 'dragend',
24
+ topDragEnter: 'dragenter',
25
+ topDragExit: 'dragexit',
26
+ topDragLeave: 'dragleave',
27
+ topDragOver: 'dragover',
28
+ topDragStart: 'dragstart',
29
+ topDrop: 'drop',
30
+ topFocus: 'focus',
31
+ topInput: 'input',
32
+ topKeyDown: 'keydown',
33
+ topKeyPress: 'keypress',
34
+ topKeyUp: 'keyup',
35
+ topMouseDown: 'mousedown',
36
+ topMouseMove: 'mousemove',
37
+ topMouseOut: 'mouseout',
38
+ topMouseOver: 'mouseover',
39
+ topMouseUp: 'mouseup',
40
+ topPaste: 'paste',
41
+ topScroll: 'scroll',
42
+ topSelectionChange: 'selectionchange',
43
+ topTextInput: 'textInput',
44
+ topTouchCancel: 'touchcancel',
45
+ topTouchEnd: 'touchend',
46
+ topTouchMove: 'touchmove',
47
+ topTouchStart: 'touchstart',
48
+ topWheel: 'wheel'
49
+ }
50
+
51
+ TOP_LISTENERS_ID_KEY = '_hyliteListenersID' + Math.rand.to_s.chars.drop(2).join
52
+
53
+ class << self
54
+ def enabled?
55
+ event_dispatcher.enabled?
56
+ end
57
+
58
+ def enabled=(enabled)
59
+ event_dispatcher.enabled = enabled
60
+ end
61
+
62
+ def event_dispatcher
63
+ @event_dispatcher ||= EventDispatcher.new do |top_level_type, top_level_target, top_level_target_id, event|
64
+ event_plugin_registry.extract_events(top_level_type, top_level_target, top_level_target_id, event)
65
+ end
66
+ end
67
+
68
+ def event_plugin_registry
69
+ @event_plugin_registry ||= EventPluginRegistry.new(
70
+ SimpleEventPlugin.new,
71
+ ChangeEventPlugin.new
72
+ )
73
+ end
74
+
75
+ def include?(name)
76
+ event_plugin_registry.include? name
77
+ end
78
+
79
+ def listen_to(registration_name, content_document_handle)
80
+ mount_at = content_document_handle
81
+ is_listening = listening_for_document(mount_at)
82
+ dependencies = event_plugin_registry.dependencies(registration_name)
83
+
84
+ dependencies.each do |dependency|
85
+ unless is_listening[dependency]
86
+ case dependency
87
+ when :top_wheel
88
+ nil
89
+ # if isEventSupported('wheel')
90
+ # ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent(
91
+ # topLevelTypes.topWheel,
92
+ # 'wheel',
93
+ # mountAt
94
+ # );
95
+ # elsif isEventSupported('mousewheel')
96
+ # ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent(
97
+ # topLevelTypes.topWheel,
98
+ # 'mousewheel',
99
+ # mountAt
100
+ # );
101
+ # else
102
+ # ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent(
103
+ # topLevelTypes.topWheel,
104
+ # 'DOMMouseScroll',
105
+ # mountAt
106
+ # );
107
+ # end
108
+ # when :top_scroll
109
+ # if isEventSupported('scroll', true)
110
+ # ReactBrowserEventEmitter.ReactEventListener.trapCapturedEvent(
111
+ # topLevelTypes.topScroll,
112
+ # 'scroll',
113
+ # mountAt
114
+ # );
115
+ # else
116
+ # ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent(
117
+ # topLevelTypes.topScroll,
118
+ # 'scroll',
119
+ # ReactBrowserEventEmitter.ReactEventListener.WINDOW_HANDLE
120
+ # );
121
+ # end
122
+ # when :top_focus, :top_blur
123
+ # if isEventSupported('focus', true)
124
+ # ReactBrowserEventEmitter.ReactEventListener.trapCapturedEvent(
125
+ # topLevelTypes.topFocus,
126
+ # 'focus',
127
+ # mountAt
128
+ # );
129
+ # ReactBrowserEventEmitter.ReactEventListener.trapCapturedEvent(
130
+ # topLevelTypes.topBlur,
131
+ # 'blur',
132
+ # mountAt
133
+ # );
134
+ # elsif isEventSupported('focusin')
135
+ # ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent(
136
+ # topLevelTypes.topFocus,
137
+ # 'focusin',
138
+ # mountAt
139
+ # );
140
+ # ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent(
141
+ # topLevelTypes.topBlur,
142
+ # 'focusout',
143
+ # mountAt
144
+ # );
145
+ # end
146
+ #
147
+ # is_listening[:top_blur] = true
148
+ # is_listening[:top_focus] = true
149
+ else
150
+ if TOP_EVENT_MAPPING.has_key? dependency
151
+ trap_bubbled_event(dependency, TOP_EVENT_MAPPING[dependency], mount_at)
152
+ end
153
+ end
154
+
155
+ is_listening[dependency] = true
156
+ end
157
+ end
158
+ end
159
+
160
+ def put_listener(id, event_name, listener)
161
+ event_dispatcher.put_listener(id, event_name, listener)
162
+ end
163
+
164
+ def delete_listener(id, registration_name)
165
+ event_dispatcher.delete_listener(id, registration_name) do |id, registration_name|
166
+ plugin = event_plugin_registry[registration_name]
167
+ if plugin.respond_to? :will_delete_listener
168
+ plugin.will_delete_listener(id, registration_name)
169
+ end
170
+ end
171
+ end
172
+
173
+ def delete_all_listeners(id)
174
+ event_dispatcher.delete_all_listeners(id) do |id, registration_name|
175
+ plugin = event_plugin_registry[registration_name]
176
+ if plugin.respond_to? :will_delete_listener
177
+ plugin.will_delete_listener(id, registration_name)
178
+ end
179
+ end
180
+ end
181
+
182
+ def listener_at_phase(id, dispatch_config, propagation_phase)
183
+ registration_name = dispatch_config[:phasedRegistrationNames][propagation_phase]
184
+ event_dispatcher.get_listener(id, registration_name)
185
+ end
186
+
187
+ def listening_for_document(mount_at)
188
+ @already_listening_to ||= []
189
+ unless `Object.prototype.hasOwnProperty.call(mount_at.native, #{TOP_LISTENERS_ID_KEY})`
190
+ `mount_at.native[#{TOP_LISTENERS_ID_KEY}] = #{@already_listening_to.length}`
191
+ @already_listening_to << {}
192
+ end
193
+ @already_listening_to[`mount_at.native[#{TOP_LISTENERS_ID_KEY}]`]
194
+ end
195
+
196
+ def trap_bubbled_event(top_level_type, handler_base_name, handle)
197
+ event_dispatcher.trap_bubbled_event(top_level_type, handler_base_name, handle)
198
+ end
199
+ end
200
+ end
201
+ end
@@ -0,0 +1,22 @@
1
+ class CallbackQueue
2
+ def initialize
3
+ @queue = []
4
+ end
5
+
6
+ def enqueue(proc = nil, &block)
7
+ if proc
8
+ @queue << proc
9
+ elsif block_given?
10
+ @queue << block
11
+ end
12
+ end
13
+
14
+ def notify_all
15
+ queue = @queue
16
+ @queue = []
17
+ while queue.length > 0
18
+ proc = queue.shift
19
+ proc.call
20
+ end
21
+ end
22
+ end