volt 0.2.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 +7 -0
- data/.gitignore +21 -0
- data/.rspec +2 -0
- data/.ruby-version +1 -0
- data/Gemfile +37 -0
- data/Guardfile +9 -0
- data/LICENSE.txt +22 -0
- data/Rakefile +23 -0
- data/Readme.md +34 -0
- data/VERSION +1 -0
- data/bin/volt +4 -0
- data/docs/GETTING_STARTED.md +7 -0
- data/docs/GUIDE.md +33 -0
- data/lib/volt.rb +15 -0
- data/lib/volt/benchmark/benchmark.rb +25 -0
- data/lib/volt/cli.rb +34 -0
- data/lib/volt/console.rb +19 -0
- data/lib/volt/controllers/model_controller.rb +29 -0
- data/lib/volt/extra_core/array.rb +10 -0
- data/lib/volt/extra_core/blank.rb +88 -0
- data/lib/volt/extra_core/extra_core.rb +7 -0
- data/lib/volt/extra_core/numeric.rb +9 -0
- data/lib/volt/extra_core/object.rb +36 -0
- data/lib/volt/extra_core/string.rb +29 -0
- data/lib/volt/extra_core/stringify_keys.rb +7 -0
- data/lib/volt/extra_core/true_false.rb +44 -0
- data/lib/volt/extra_core/try.rb +31 -0
- data/lib/volt/models.rb +5 -0
- data/lib/volt/models/array_model.rb +37 -0
- data/lib/volt/models/model.rb +210 -0
- data/lib/volt/models/model_wrapper.rb +23 -0
- data/lib/volt/models/params.rb +67 -0
- data/lib/volt/models/url.rb +192 -0
- data/lib/volt/page/url_tracker.rb +36 -0
- data/lib/volt/reactive/array_extensions.rb +13 -0
- data/lib/volt/reactive/event_chain.rb +126 -0
- data/lib/volt/reactive/events.rb +283 -0
- data/lib/volt/reactive/object_tracker.rb +99 -0
- data/lib/volt/reactive/object_tracking.rb +15 -0
- data/lib/volt/reactive/reactive_array.rb +222 -0
- data/lib/volt/reactive/reactive_tags.rb +64 -0
- data/lib/volt/reactive/reactive_value.rb +368 -0
- data/lib/volt/reactive/string_extensions.rb +34 -0
- data/lib/volt/router/routes.rb +83 -0
- data/lib/volt/server.rb +121 -0
- data/lib/volt/server/binding_setup.rb +2 -0
- data/lib/volt/server/channel_handler.rb +31 -0
- data/lib/volt/server/component_handler.rb +88 -0
- data/lib/volt/server/if_binding_setup.rb +29 -0
- data/lib/volt/server/request_handler.rb +16 -0
- data/lib/volt/server/scope.rb +43 -0
- data/lib/volt/server/source_map_server.rb +31 -0
- data/lib/volt/server/template_parser.rb +452 -0
- data/lib/volt/store/mongo.rb +5 -0
- data/lib/volt/templates/attribute_binding.rb +110 -0
- data/lib/volt/templates/base_binding.rb +37 -0
- data/lib/volt/templates/channel.rb +48 -0
- data/lib/volt/templates/content_binding.rb +35 -0
- data/lib/volt/templates/document_events.rb +80 -0
- data/lib/volt/templates/each_binding.rb +115 -0
- data/lib/volt/templates/event_binding.rb +51 -0
- data/lib/volt/templates/if_binding.rb +74 -0
- data/lib/volt/templates/memory_test.rb +26 -0
- data/lib/volt/templates/page.rb +146 -0
- data/lib/volt/templates/reactive_template.rb +38 -0
- data/lib/volt/templates/render_queue.rb +5 -0
- data/lib/volt/templates/sub_context.rb +23 -0
- data/lib/volt/templates/targets/attribute_section.rb +33 -0
- data/lib/volt/templates/targets/attribute_target.rb +18 -0
- data/lib/volt/templates/targets/base_section.rb +14 -0
- data/lib/volt/templates/targets/binding_document/base_node.rb +3 -0
- data/lib/volt/templates/targets/binding_document/component_node.rb +112 -0
- data/lib/volt/templates/targets/binding_document/html_node.rb +11 -0
- data/lib/volt/templates/targets/dom_section.rb +147 -0
- data/lib/volt/templates/targets/dom_target.rb +11 -0
- data/lib/volt/templates/template_binding.rb +159 -0
- data/lib/volt/templates/template_renderer.rb +50 -0
- data/spec/models/event_chain_spec.rb +129 -0
- data/spec/models/model_spec.rb +340 -0
- data/spec/models/old_model_spec.rb +109 -0
- data/spec/models/reactive_array_spec.rb +262 -0
- data/spec/models/reactive_tags_spec.rb +35 -0
- data/spec/models/reactive_value_spec.rb +336 -0
- data/spec/models/string_extensions_spec.rb +57 -0
- data/spec/router/routes_spec.rb +24 -0
- data/spec/server/template_parser_spec.rb +50 -0
- data/spec/spec_helper.rb +20 -0
- data/spec/store/mongo_spec.rb +4 -0
- data/spec/templates/targets/binding_document/component_node_spec.rb +18 -0
- data/spec/templates/template_binding_spec.rb +98 -0
- data/templates/.gitignore +12 -0
- data/templates/Gemfile.tt +8 -0
- data/templates/app/.empty_directory +0 -0
- data/templates/app/home/config/routes.rb +1 -0
- data/templates/app/home/controllers/index_controller.rb +5 -0
- data/templates/app/home/css/.empty_directory +0 -0
- data/templates/app/home/models/.empty_directory +0 -0
- data/templates/app/home/views/index/about.html +9 -0
- data/templates/app/home/views/index/home.html +7 -0
- data/templates/app/home/views/index/index.html +28 -0
- data/templates/config.ru +4 -0
- data/templates/public/css/ansi.css +0 -0
- data/templates/public/css/bootstrap-theme.css +459 -0
- data/templates/public/css/bootstrap.css +7098 -0
- data/templates/public/css/jumbotron.css +79 -0
- data/templates/public/fonts/glyphicons-halflings-regular.eot +0 -0
- data/templates/public/fonts/glyphicons-halflings-regular.svg +229 -0
- data/templates/public/fonts/glyphicons-halflings-regular.ttf +0 -0
- data/templates/public/fonts/glyphicons-halflings-regular.woff +0 -0
- data/templates/public/index.html +25 -0
- data/templates/public/js/bootstrap.js +0 -0
- data/templates/public/js/jquery-2.0.3.js +8829 -0
- data/templates/public/js/sockjs-0.2.1.min.js +27 -0
- data/templates/spec/spec_helper.rb +20 -0
- data/volt.gemspec +41 -0
- metadata +412 -0
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
require 'volt/templates/base_binding'
|
|
2
|
+
|
|
3
|
+
class IfBinding < BaseBinding
|
|
4
|
+
def initialize(target, context, binding_name, branches)
|
|
5
|
+
getter, template_name = branches[0]
|
|
6
|
+
# puts "New If Binding: #{binding_name}, #{getter.inspect}"
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
super(target, context, binding_name)
|
|
10
|
+
|
|
11
|
+
@branches = []
|
|
12
|
+
@listeners = []
|
|
13
|
+
|
|
14
|
+
branches.each do |branch|
|
|
15
|
+
getter, template_name = branch
|
|
16
|
+
|
|
17
|
+
if getter.present?
|
|
18
|
+
# Lookup the value
|
|
19
|
+
value = value_from_getter(getter)
|
|
20
|
+
|
|
21
|
+
if value.reactive?
|
|
22
|
+
# Trigger change when value changes
|
|
23
|
+
@listeners << value.on('changed') { update }
|
|
24
|
+
end
|
|
25
|
+
else
|
|
26
|
+
# A nil value means this is an unconditional else branch, it
|
|
27
|
+
# should always be true
|
|
28
|
+
value = true
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
@branches << [value, template_name]
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
update
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def update
|
|
38
|
+
# Find the true branch
|
|
39
|
+
true_template = nil
|
|
40
|
+
@branches.each do |branch|
|
|
41
|
+
value, template_name = branch
|
|
42
|
+
|
|
43
|
+
# TODO: A bug in opal requires us to check == true
|
|
44
|
+
if value.cur.true? == true
|
|
45
|
+
# This branch is currently true
|
|
46
|
+
true_template = template_name
|
|
47
|
+
break
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Change out the template only if the true branch has changed.
|
|
52
|
+
if @last_true_template != true_template
|
|
53
|
+
@last_true_template = true_template
|
|
54
|
+
|
|
55
|
+
if @template
|
|
56
|
+
@template.remove
|
|
57
|
+
@template = nil
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
if true_template
|
|
61
|
+
@template = TemplateRenderer.new(@target, @context, binding_name, true_template)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def remove
|
|
67
|
+
# Remove all listeners on any reactive values
|
|
68
|
+
@listeners.each(&:remove)
|
|
69
|
+
|
|
70
|
+
@template.remove if @template
|
|
71
|
+
|
|
72
|
+
super
|
|
73
|
+
end
|
|
74
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
require 'opal'
|
|
2
|
+
require 'volt/models'
|
|
3
|
+
|
|
4
|
+
class Test
|
|
5
|
+
def self.test1
|
|
6
|
+
a = ReactiveValue.new(1)
|
|
7
|
+
listener = a.on('changed') { puts "CHANGED" }
|
|
8
|
+
a.cur = 5
|
|
9
|
+
listener.remove
|
|
10
|
+
|
|
11
|
+
ObjectTracker.process_queue
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def self.test
|
|
15
|
+
a = ReactiveValue.new(Model.new)
|
|
16
|
+
a._cool = [1,2,3]
|
|
17
|
+
|
|
18
|
+
listener = a._cool.on('added') { puts "ADDED" }
|
|
19
|
+
a._cool << 4
|
|
20
|
+
puts a._cool[3]
|
|
21
|
+
|
|
22
|
+
listener.remove
|
|
23
|
+
|
|
24
|
+
ObjectTracker.process_queue
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
require 'opal'
|
|
2
|
+
|
|
3
|
+
ENV['CLIENT'] = true
|
|
4
|
+
|
|
5
|
+
require 'opal-jquery'
|
|
6
|
+
require 'volt/models'
|
|
7
|
+
require 'volt/models/params'
|
|
8
|
+
require 'volt/controllers/model_controller'
|
|
9
|
+
require 'volt/templates/attribute_binding'
|
|
10
|
+
require 'volt/templates/content_binding'
|
|
11
|
+
require 'volt/templates/each_binding'
|
|
12
|
+
require 'volt/templates/if_binding'
|
|
13
|
+
require 'volt/templates/template_binding'
|
|
14
|
+
require 'volt/templates/template_renderer'
|
|
15
|
+
require 'volt/templates/reactive_template'
|
|
16
|
+
require 'volt/templates/event_binding'
|
|
17
|
+
require 'volt/templates/document_events'
|
|
18
|
+
require 'volt/templates/sub_context'
|
|
19
|
+
require 'volt/templates/targets/dom_target'
|
|
20
|
+
require 'volt/templates/channel'
|
|
21
|
+
require 'volt/router/routes'
|
|
22
|
+
require 'volt/models/url'
|
|
23
|
+
require 'volt/page/url_tracker'
|
|
24
|
+
require 'volt'
|
|
25
|
+
require 'volt/benchmark/benchmark'
|
|
26
|
+
require 'volt/templates/render_queue'
|
|
27
|
+
|
|
28
|
+
class Page
|
|
29
|
+
attr_reader :url, :params, :page, :store, :templates, :routes, :render_queue
|
|
30
|
+
|
|
31
|
+
def initialize
|
|
32
|
+
|
|
33
|
+
# debugger
|
|
34
|
+
puts "------ Page Loaded -------"
|
|
35
|
+
@model_classes = {}
|
|
36
|
+
|
|
37
|
+
# Run the code to setup the page
|
|
38
|
+
@page = ReactiveValue.new(Model.new)#({}, nil, 'page', @model_classes))
|
|
39
|
+
@store = ReactiveValue.new(Model.new)#({}, nil, 'store', @model_classes))
|
|
40
|
+
|
|
41
|
+
@url = ReactiveValue.new(URL.new)
|
|
42
|
+
@params = @url.params
|
|
43
|
+
@url_tracker = UrlTracker.new(self)
|
|
44
|
+
|
|
45
|
+
@events = DocumentEvents.new
|
|
46
|
+
@render_queue = RenderQueue.new
|
|
47
|
+
|
|
48
|
+
# Add event for link clicks to handle all a onclick
|
|
49
|
+
# EventBinding.new(self, )
|
|
50
|
+
|
|
51
|
+
# Setup escape binding for console
|
|
52
|
+
%x{
|
|
53
|
+
$(document).keyup(function(e) {
|
|
54
|
+
if (e.keyCode == 27) {
|
|
55
|
+
Opal.gvars.page.$launch_console();
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
$(document).on('click', 'a', function(event) {
|
|
60
|
+
Opal.gvars.page.$link_clicked($(this).attr('href'));
|
|
61
|
+
event.stopPropagation();
|
|
62
|
+
|
|
63
|
+
return false;
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
# channel
|
|
68
|
+
# channel.on('message') do |message|
|
|
69
|
+
# # puts "GOT: #{message}"
|
|
70
|
+
# # `console.log('got: ', message);`
|
|
71
|
+
# end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def link_clicked(url)
|
|
75
|
+
puts "Link Clicked to: #{url}"
|
|
76
|
+
# Skip when href == ''
|
|
77
|
+
return if url.blank?
|
|
78
|
+
|
|
79
|
+
# Normalize url
|
|
80
|
+
# Benchmark.bm(1) do
|
|
81
|
+
@url.parse("http://localhost:3000" + url)
|
|
82
|
+
# end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# We provide a binding_name, so we can bind events on the document
|
|
86
|
+
def binding_name
|
|
87
|
+
'page'
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def launch_console
|
|
91
|
+
puts "Launch Console"
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def channel
|
|
95
|
+
@channel ||= Channel.new
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def events
|
|
99
|
+
@events
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def add_model(model_name)
|
|
103
|
+
# puts "ADD MODEL: #{model_name.inspect} - #{model_name.camelize.inspect}"
|
|
104
|
+
|
|
105
|
+
@model_classes[["*", "_#{model_name}"]] = Object.const_get(model_name.camelize)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def add_template(name, template, bindings)
|
|
109
|
+
# puts "Add Template: #{name}\n#{template.inspect}\n#{bindings.inspect}"
|
|
110
|
+
@templates ||= {}
|
|
111
|
+
@templates[name] = {'html' => template, 'bindings' => bindings}
|
|
112
|
+
# puts "Add Template: #{name}"
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def add_routes(&block)
|
|
116
|
+
@routes = Routes.new.define(&block)
|
|
117
|
+
@url.cur.router = @routes
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def start
|
|
121
|
+
# Setup to render template
|
|
122
|
+
Element.find('body').html = "<!-- $CONTENT --><!-- $/CONTENT -->"
|
|
123
|
+
|
|
124
|
+
main_controller = IndexController.new
|
|
125
|
+
|
|
126
|
+
# Setup main page template
|
|
127
|
+
TemplateRenderer.new(DomTarget.new, main_controller, 'CONTENT', 'home/index/index/body')
|
|
128
|
+
|
|
129
|
+
# Setup title listener template
|
|
130
|
+
title_target = AttributeTarget.new
|
|
131
|
+
title_target.on('changed') do
|
|
132
|
+
title = title_target.to_html
|
|
133
|
+
`document.title = title;`
|
|
134
|
+
end
|
|
135
|
+
TemplateRenderer.new(title_target, main_controller, "main", "home/index/index/title")
|
|
136
|
+
|
|
137
|
+
@url_tracker.url_updated(true)
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
$page = Page.new
|
|
142
|
+
|
|
143
|
+
# Call start once the page is loaded
|
|
144
|
+
Document.ready? do
|
|
145
|
+
$page.start
|
|
146
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
class ReactiveTemplate
|
|
2
|
+
include Events
|
|
3
|
+
|
|
4
|
+
def initialize(context, template_path)
|
|
5
|
+
@template_path = template_path
|
|
6
|
+
@target = AttributeTarget.new
|
|
7
|
+
@template = TemplateRenderer.new(@target, context, "main", template_path)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def event_added(event, scope_provider, first)
|
|
11
|
+
if first && !@template_listener
|
|
12
|
+
@template_listener = @target.on('changed') { update }
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def event_removed(event, last)
|
|
17
|
+
if last && @template_listener
|
|
18
|
+
@template_listener.remove
|
|
19
|
+
@template_listener = nil
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Render the template and get the current value
|
|
24
|
+
def cur
|
|
25
|
+
@target.to_html
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# TODO: improve
|
|
29
|
+
def skip_current_queue_flush
|
|
30
|
+
true
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def update
|
|
35
|
+
trigger!('changed')
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# A sub context takes in a hash of local variables that should be available
|
|
2
|
+
# in front of the current context. It basically proxies the local variables
|
|
3
|
+
# first, then failing those proxies the context.
|
|
4
|
+
|
|
5
|
+
class SubContext
|
|
6
|
+
attr_reader :locals
|
|
7
|
+
|
|
8
|
+
def initialize(locals, context=nil)
|
|
9
|
+
@locals = locals.stringify_keys
|
|
10
|
+
@context = context
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def method_missing(method_name, *args, &block)
|
|
14
|
+
method_name = method_name.to_s
|
|
15
|
+
if @locals[method_name]
|
|
16
|
+
return @locals[method_name]
|
|
17
|
+
elsif @context
|
|
18
|
+
return @context.send(method_name, *args, &block)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
raise NoMethodError.new("undefined method `#{method_name}' for \"#{self.inspect}\":#{self.class.to_s}")
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# AttributeSection provides a place to render templates that
|
|
2
|
+
# will be placed as text into an attribute.
|
|
3
|
+
|
|
4
|
+
require 'volt/templates/targets/base_section'
|
|
5
|
+
|
|
6
|
+
class AttributeSection
|
|
7
|
+
def initialize(target, binding_name)
|
|
8
|
+
@target = target
|
|
9
|
+
@binding_name = binding_name
|
|
10
|
+
# puts "init attr section on #{binding_name}"
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def text=(text)
|
|
14
|
+
set_content_and_rezero_bindings(text, {})
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Takes in our html and bindings, and rezero's the comment names, and the
|
|
18
|
+
# bindings. Returns an updated bindings hash
|
|
19
|
+
def set_content_and_rezero_bindings(html, bindings)
|
|
20
|
+
if @binding_name == 'main'
|
|
21
|
+
@target.html = html
|
|
22
|
+
else
|
|
23
|
+
@target.find_by_binding_id(@binding_name).html = html
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
return bindings
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def remove
|
|
30
|
+
node = @target.find_by_binding_id(@binding_name)
|
|
31
|
+
node.remove
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
require 'volt/templates/targets/base_section'
|
|
2
|
+
require 'volt/templates/targets/attribute_section'
|
|
3
|
+
require 'volt/templates/targets/binding_document/component_node'
|
|
4
|
+
require 'volt/templates/targets/binding_document/html_node'
|
|
5
|
+
|
|
6
|
+
# AttributeTarget's provide an interface that can render bindings into
|
|
7
|
+
# a string that can then be used to update a attribute binding.
|
|
8
|
+
|
|
9
|
+
class AttributeTarget < ComponentNode
|
|
10
|
+
# TODO: improve
|
|
11
|
+
def skip_current_queue_flush
|
|
12
|
+
true
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def section(*args)
|
|
16
|
+
return AttributeSection.new(self, *args)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
require 'volt/templates/targets/binding_document/html_node'
|
|
2
|
+
require 'volt/reactive/events'
|
|
3
|
+
|
|
4
|
+
# Component nodes contain an array of both HtmlNodes and ComponentNodes.
|
|
5
|
+
# Instead of providing a full DOM API, component nodes are the branch
|
|
6
|
+
# nodes and html nodes are the leafs. This is all we need to produce
|
|
7
|
+
# the html from templates outside of a normal dom.
|
|
8
|
+
class ComponentNode < BaseNode
|
|
9
|
+
include Events
|
|
10
|
+
|
|
11
|
+
attr_accessor :parent, :binding_id, :nodes
|
|
12
|
+
def initialize(binding_id=nil, parent=nil)
|
|
13
|
+
@nodes = []
|
|
14
|
+
@binding_id = binding_id
|
|
15
|
+
@parent = parent
|
|
16
|
+
|
|
17
|
+
@change_listener = on('changed') do
|
|
18
|
+
if @parent
|
|
19
|
+
@parent.trigger!('changed')
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# TODO: improve
|
|
25
|
+
def skip_current_queue_flush
|
|
26
|
+
true
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def text=(text)
|
|
30
|
+
self.html = text
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def html=(html)
|
|
34
|
+
parts = html.split(/(\<\!\-\- \$\/?[0-9]+ \-\-\>)/).reject {|v| v == '' }
|
|
35
|
+
|
|
36
|
+
# Clear current nodes
|
|
37
|
+
@nodes = []
|
|
38
|
+
|
|
39
|
+
current_node = self
|
|
40
|
+
|
|
41
|
+
parts.each do |part|
|
|
42
|
+
case part
|
|
43
|
+
when /\<\!\-\- \$[0-9]+ \-\-\>/
|
|
44
|
+
# Open
|
|
45
|
+
binding_id = part.match(/\<\!\-\- \$([0-9]+) \-\-\>/)[1].to_i
|
|
46
|
+
|
|
47
|
+
sub_node = ComponentNode.new(binding_id, current_node)
|
|
48
|
+
current_node << sub_node
|
|
49
|
+
current_node = sub_node
|
|
50
|
+
when /\<\!\-\- \$\/[0-9]+ \-\-\>/
|
|
51
|
+
# Close
|
|
52
|
+
# binding_id = part.match(/\<\!\-\- \$\/([0-9]+) \-\-\>/)[1].to_i
|
|
53
|
+
|
|
54
|
+
current_node = current_node.parent
|
|
55
|
+
else
|
|
56
|
+
# html string
|
|
57
|
+
current_node << HtmlNode.new(part)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
trigger!('changed')
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def <<(node)
|
|
65
|
+
@nodes << node
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def to_html
|
|
69
|
+
str = []
|
|
70
|
+
@nodes.each do |node|
|
|
71
|
+
str << node.to_html
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
return str.join('')
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def find_by_binding_id(binding_id)
|
|
78
|
+
if @binding_id == binding_id
|
|
79
|
+
return self
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
@nodes.each do |node|
|
|
83
|
+
if node.cur.is_a?(ComponentNode)
|
|
84
|
+
val = node.find_by_binding_id(binding_id)
|
|
85
|
+
return val if val
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
return nil
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def remove
|
|
93
|
+
@nodes = []
|
|
94
|
+
|
|
95
|
+
trigger!('changed')
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def remove_anchors
|
|
99
|
+
raise "not implemented"
|
|
100
|
+
|
|
101
|
+
@parent.nodes.delete(self)
|
|
102
|
+
|
|
103
|
+
@change_listener.remove
|
|
104
|
+
@change_listener = nil
|
|
105
|
+
|
|
106
|
+
trigger!('changed')
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def inspect
|
|
110
|
+
"<ComponentNode:#{@binding_id} #{@nodes.inspect}>"
|
|
111
|
+
end
|
|
112
|
+
end
|