lapillus 0.0.2
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.
- data/examples/guest_book.rb +50 -0
- data/examples/hello_world.rb +12 -0
- data/examples/persons.rb +30 -0
- data/examples/tc_guest_book.rb +34 -0
- data/examples/tc_hello_world.rb +24 -0
- data/examples/tc_persons.rb +13 -0
- data/html/GuestBook.html +24 -0
- data/html/HelloWorld.html +5 -0
- data/html/LapillusTesterTestPage.html +12 -0
- data/html/Persons.html +7 -0
- data/html/TestPage.html +8 -0
- data/html/TestRemotePanel.html +8 -0
- data/lib/changelog.txt +6 -0
- data/lib/lapillus/base.rb +276 -0
- data/lib/lapillus/behaviours.rb +208 -0
- data/lib/lapillus/components.rb +116 -0
- data/lib/lapillus/containers.rb +234 -0
- data/lib/lapillus/dispatcher.rb +216 -0
- data/lib/lapillus/form_components.rb +88 -0
- data/lib/lapillus/lapillus_server.rb +21 -0
- data/lib/lapillus/lapillus_testers.rb +167 -0
- data/lib/lapillus/mongrel_server.rb +69 -0
- data/lib/lapillus/multiview.rb +45 -0
- data/lib/lapillus/pager.rb +104 -0
- data/lib/lapillus/process_upload.rb +11 -0
- data/lib/lapillus/web_application.rb +12 -0
- data/lib/lapillus/webrick_server.rb +90 -0
- data/lib/lapillus.rb +33 -0
- data/lib/license.txt +24 -0
- data/test/tc_base.rb +63 -0
- data/test/tc_behaviours.rb +106 -0
- data/test/tc_bookmarkablepagelink.rb +58 -0
- data/test/tc_components.rb +285 -0
- data/test/tc_containers.rb +173 -0
- data/test/tc_dispatcher.rb +106 -0
- data/test/tc_examples.rb +193 -0
- data/test/tc_form.rb +231 -0
- data/test/tc_fragments.rb +114 -0
- data/test/tc_hierarchy.rb +34 -0
- data/test/tc_lapillus.rb +30 -0
- data/test/tc_lapillus_testers.rb +108 -0
- data/test/tc_multiview.rb +91 -0
- data/test/tc_pager.rb +94 -0
- data/test/tc_rendering.rb +74 -0
- data/test/ts_all_test.rb +20 -0
- metadata +94 -0
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
require 'rexml/attribute'
|
|
2
|
+
require 'yaml'
|
|
3
|
+
|
|
4
|
+
module Lapillus
|
|
5
|
+
|
|
6
|
+
class Behaviour < Component
|
|
7
|
+
def initialize(id="", value=nil)
|
|
8
|
+
super(id, value)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# def add_javascript_to_header root, src
|
|
12
|
+
# # add the neccesary javascript links to the head of the html file
|
|
13
|
+
# head=root.get_elements("/html/head")[0]
|
|
14
|
+
# raise "Error: no html head found in:\n#{root}" if head.nil?
|
|
15
|
+
#
|
|
16
|
+
# script_already_set=false
|
|
17
|
+
# head.each_element_with_attribute('src', src, 1, 'script'){ |e| script_already_set=true }
|
|
18
|
+
# return if script_already_set
|
|
19
|
+
#
|
|
20
|
+
# script= REXML::Element.new('script')
|
|
21
|
+
# script.add_attributes({'type'=>'text/javascript', 'language'=>'javascript', 'src'=>src})
|
|
22
|
+
# script.add_text(";")
|
|
23
|
+
# head.add_element(script)
|
|
24
|
+
# end
|
|
25
|
+
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
class AttributeModifier < Behaviour
|
|
29
|
+
def render_to_element(element)
|
|
30
|
+
attribute = REXML::Attribute.new(identifier, value)
|
|
31
|
+
def attribute.to_string
|
|
32
|
+
return "#@expanded_name=\"#@value\""
|
|
33
|
+
end
|
|
34
|
+
element.add_attribute(attribute)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
#TODO test
|
|
39
|
+
class UniqueIdentifier < AttributeModifier
|
|
40
|
+
def initialize
|
|
41
|
+
super("id")
|
|
42
|
+
end
|
|
43
|
+
def value
|
|
44
|
+
parent.path
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
class FadeIn < Behaviour
|
|
49
|
+
def render_to_element(element)
|
|
50
|
+
attribute = REXML::Attribute.new(identifier, "new Effect.Appear('#{value}');")
|
|
51
|
+
def attribute.to_string
|
|
52
|
+
return "#@expanded_name=\"#@value\""
|
|
53
|
+
end
|
|
54
|
+
element.add_attribute(attribute)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
class Draggable < UniqueIdentifier
|
|
59
|
+
|
|
60
|
+
def initialize(options=[])
|
|
61
|
+
super()
|
|
62
|
+
@extra_options=options.to_a
|
|
63
|
+
@option=Hash.new
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def handle= handleclass
|
|
67
|
+
@option[:handle] = "'#{handleclass}'"
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def revert= value
|
|
71
|
+
@option[:revert]= value ? "true" : "false"
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def snap= value
|
|
75
|
+
@option[:snap]= value ? "true" : "false"
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def zindex= value
|
|
79
|
+
@option[:zindex]= value
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def constraint= value
|
|
83
|
+
@option[:constraint]="'#{value}'"
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def ghosting= value
|
|
87
|
+
@option[:ghosting]= value ? "true" : "false"
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def start_effect= value
|
|
91
|
+
@option[:starteffect]= value
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def revert_effect= value
|
|
95
|
+
@option[:reverteffect]= value
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def end_effect= value
|
|
99
|
+
@option[:endeffect]= value
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def change= value
|
|
103
|
+
@option[:change]= value
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def render_to_element(element)
|
|
107
|
+
super
|
|
108
|
+
#add_javascript_to_header(element.root_node,"/editiemachine/context/javascript/prototype.js")
|
|
109
|
+
#add_javascript_to_header(element.root_node,"/editiemachine/context/javascript/scriptaculous.js")
|
|
110
|
+
|
|
111
|
+
script = REXML::Element.new('script')
|
|
112
|
+
script.add_attributes({'type'=>'text/javascript', 'language'=>'javascript'})
|
|
113
|
+
options=@option.to_a.collect{|o| "#{o[0]}:#{o[1]}" }.sort.concat(@extra_options).join(",")
|
|
114
|
+
text=REXML::Text.new("\n// <![CDATA[\nnew Draggable('#{parent.path}', {#{options}});\n// ]]>\n")
|
|
115
|
+
def text.to_s
|
|
116
|
+
@normalized=@string
|
|
117
|
+
end
|
|
118
|
+
script.add_text(text)
|
|
119
|
+
element.add_element(script)
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# duplication between OnClick and AjaxLink
|
|
124
|
+
# todo: add test
|
|
125
|
+
class OnClick < Behaviour
|
|
126
|
+
def initialize(component_to_refresh=nil)
|
|
127
|
+
super(nil, component_to_refresh)
|
|
128
|
+
component_to_refresh.add_behaviour(UniqueIdentifier.new) if has_model?
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def render_to_element(element)
|
|
132
|
+
if has_model?
|
|
133
|
+
javascript = "new Ajax.Updater('#{value.path}', '', { evalScripts: true, method:'get', parameters: { listener:'#{parent.path}' }}); return false;"
|
|
134
|
+
else
|
|
135
|
+
javascript = "new Ajax.Request('', { method: 'get', parameters: { listener:'#{parent.path}' }}); return false;"
|
|
136
|
+
end
|
|
137
|
+
attribute = REXML::Attribute.new("onclick", javascript)
|
|
138
|
+
# TODO: this should be the default for attributes in
|
|
139
|
+
# Lapillus somewhere!
|
|
140
|
+
def attribute.to_string
|
|
141
|
+
return "#@expanded_name=\"#@value\""
|
|
142
|
+
end
|
|
143
|
+
element.add_attribute(attribute)
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
# value is the component to rerender when a link is clicked
|
|
148
|
+
# the component should have an id attribute which is set to its path
|
|
149
|
+
class AjaxLink < Container
|
|
150
|
+
def initialize(id, value=nil)
|
|
151
|
+
super(id, value)
|
|
152
|
+
value.add_behaviour(UniqueIdentifier.new) if has_model?
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def render_to_element(element)
|
|
156
|
+
if has_model?
|
|
157
|
+
javascript = "new Ajax.Updater('#{value.path}', '', { evalScripts: true, method:'get', parameters: { listener:'#{path}' }}); return false;"
|
|
158
|
+
else
|
|
159
|
+
javascript = "new Ajax.Request('', { method: 'get', parameters: { listener:'#{path}' }}); return false;"
|
|
160
|
+
end
|
|
161
|
+
attribute = REXML::Attribute.new("onclick", javascript)
|
|
162
|
+
def attribute.to_string
|
|
163
|
+
return "#@expanded_name=\"#@value\""
|
|
164
|
+
end
|
|
165
|
+
element.add_attribute(attribute)
|
|
166
|
+
element.add_attribute("href","#")
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def on_click_handler=function
|
|
170
|
+
@function=function
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def on_click
|
|
174
|
+
instance_eval(&@function)
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def render_component
|
|
178
|
+
value.render_component if has_model?
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
class Droppable < UniqueIdentifier
|
|
183
|
+
def render_to_element(element)
|
|
184
|
+
javascript = <<EOF
|
|
185
|
+
Droppables.add('#{parent.path}', {
|
|
186
|
+
onDrop: function(element) {
|
|
187
|
+
new Ajax.Updater('#{parent.path}', '', {
|
|
188
|
+
method:'get', parameters: {
|
|
189
|
+
listener:'#{parent.path}',
|
|
190
|
+
component:element.id
|
|
191
|
+
}
|
|
192
|
+
}); return false;
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
EOF
|
|
196
|
+
super
|
|
197
|
+
script= REXML::Element.new('script')
|
|
198
|
+
script.add_attributes({'type'=>'text/javascript', 'language'=>'javascript'})
|
|
199
|
+
text=REXML::Text.new("\n// <![CDATA[\n#{javascript}\n// ]]>\n")
|
|
200
|
+
def text.to_s
|
|
201
|
+
@normalized=@string
|
|
202
|
+
end
|
|
203
|
+
script.add_text(text)
|
|
204
|
+
element.add_element(script)
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
end
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
require 'lapillus/base'
|
|
2
|
+
|
|
3
|
+
module Lapillus
|
|
4
|
+
|
|
5
|
+
class Label < Component
|
|
6
|
+
def render_to_element(element)
|
|
7
|
+
element.text = value
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def value
|
|
11
|
+
super.to_s
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def to_s
|
|
15
|
+
result = "#{identifier}:"
|
|
16
|
+
result += " #{value}" if has_model?
|
|
17
|
+
result += " (not visible)" if(!visible?)
|
|
18
|
+
return result
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def Container.label(id, options={})
|
|
23
|
+
options.keys.each {|key|
|
|
24
|
+
raise "Unknown key: #{key}" if key!=:model and key!=:property
|
|
25
|
+
}
|
|
26
|
+
internal_add_component(id) { Label.new(id, options[:model], options[:property]) }
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
#TODO: Add test!
|
|
30
|
+
class ListItem < Container
|
|
31
|
+
def label(id, options={})
|
|
32
|
+
options.keys.each {|key|
|
|
33
|
+
raise "Unknown key: #{key}" if key!=:model and key!=:property
|
|
34
|
+
}
|
|
35
|
+
add(Label.new(id, options[:model], options[:property]))
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
class MultiLineLabel < Component
|
|
40
|
+
attr_writer :html_content
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
attr_reader :html_content
|
|
44
|
+
|
|
45
|
+
public
|
|
46
|
+
def render_to_element(element)
|
|
47
|
+
element.text = ""
|
|
48
|
+
re = Regexp.new('\n\n+')
|
|
49
|
+
if((value =~ re)!=nil)
|
|
50
|
+
split_paragraphs(value,element,re)
|
|
51
|
+
else
|
|
52
|
+
split_breaks(value,element)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def split_paragraphs(text,parent,re)
|
|
57
|
+
paragr_array = text.split(re)
|
|
58
|
+
paragr_array.each { |elem|
|
|
59
|
+
split_breaks(elem,parent.add_element("p"))
|
|
60
|
+
}
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def split_breaks(text,parent)
|
|
64
|
+
break_array = text.split(/\n/,-1)
|
|
65
|
+
if !break_array.empty?
|
|
66
|
+
add_text(parent, break_array[0])
|
|
67
|
+
break_array[1..-1].each { |elem|
|
|
68
|
+
parent.add_element("br")
|
|
69
|
+
add_text(parent, elem)
|
|
70
|
+
}
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def add_text(element, text)
|
|
75
|
+
text = REXML::Text.new(text)
|
|
76
|
+
text.raw = html_content
|
|
77
|
+
element.add_text(text)
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
#TODO: root_url could be extracted from application object
|
|
82
|
+
#but the question is how can an image object easily get the application object?
|
|
83
|
+
|
|
84
|
+
class Image < Component
|
|
85
|
+
attr_reader :root_url
|
|
86
|
+
|
|
87
|
+
def initialize(id, model, root_url)
|
|
88
|
+
super(id, model)
|
|
89
|
+
raise "root_url is not set!" if root_url.nil?
|
|
90
|
+
@root_url = root_url
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def render_to_element(element)
|
|
94
|
+
url = "/images/#{value}"
|
|
95
|
+
url = root_url + url if root_url != "/"
|
|
96
|
+
element.add_attribute("src", url)
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def Container.image(id, options={})
|
|
101
|
+
options.keys.each {|key|
|
|
102
|
+
raise "Unknown key: #{key}" if key!=:model and key!=:root_url
|
|
103
|
+
}
|
|
104
|
+
internal_add_component(id) { Image.new(id, options[:model], options[:root_url]) }
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
class ListItem < Container
|
|
108
|
+
def image(id, options={})
|
|
109
|
+
options.keys.each {|key|
|
|
110
|
+
raise "Unknown key: #{key}" if key!=:model and key!=:root_url
|
|
111
|
+
}
|
|
112
|
+
add(Image.new(id, options[:model], options[:root_url]))
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
end
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
require 'lapillus/base'
|
|
2
|
+
require 'rexml/document'
|
|
3
|
+
|
|
4
|
+
#TODO: remove in ruby 1.9
|
|
5
|
+
#NOTE: see http://blog.jayfields.com/2006/09/ruby-instanceexec-aka-instanceeval.html
|
|
6
|
+
class Object
|
|
7
|
+
module InstanceExecHelper; end
|
|
8
|
+
include InstanceExecHelper
|
|
9
|
+
def instance_exec(*args, &block)
|
|
10
|
+
begin
|
|
11
|
+
old_critical, Thread.critical = Thread.critical, true
|
|
12
|
+
n = 0
|
|
13
|
+
n += 1 while respond_to?(mname="__instance_exec#{n}")
|
|
14
|
+
InstanceExecHelper.module_eval{ define_method(mname, &block) }
|
|
15
|
+
ensure
|
|
16
|
+
Thread.critical = old_critical
|
|
17
|
+
end
|
|
18
|
+
begin
|
|
19
|
+
ret = send(mname, *args)
|
|
20
|
+
ensure
|
|
21
|
+
InstanceExecHelper.module_eval{ remove_method(mname) } rescue nil
|
|
22
|
+
end
|
|
23
|
+
ret
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
module REXML
|
|
28
|
+
class Attribute
|
|
29
|
+
#TODO: what if " occurs inside to_s
|
|
30
|
+
def to_string
|
|
31
|
+
"#@expanded_name=\"#{to_s}\""
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
module Lapillus
|
|
37
|
+
class Webpage < Container
|
|
38
|
+
|
|
39
|
+
def initialize()
|
|
40
|
+
super("")
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def render(html=default_htmlfile)
|
|
44
|
+
# @html is al File
|
|
45
|
+
# html = @html if !@html.nil?
|
|
46
|
+
doc = REXML::Document.new html
|
|
47
|
+
root_element = doc.root
|
|
48
|
+
new_root_element = render_container(root_element)
|
|
49
|
+
return doc.doctype.to_s + new_root_element.to_s.gsub(''',"'").gsub('"','"')
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def default_htmlfile
|
|
53
|
+
htmlfile = "html/" + self.class.to_s + ".html"
|
|
54
|
+
File.new( htmlfile )
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
class ListView < Container
|
|
59
|
+
def build_refresh_etc
|
|
60
|
+
raise "value is nil, it could be that the model/property is not set correctly!" if value.nil?
|
|
61
|
+
block = parent.class.block(identifier)
|
|
62
|
+
raise "listview without a block! #{self.path}" if block.nil?
|
|
63
|
+
value.each_with_index { |item, index|
|
|
64
|
+
new_component = ListItem.new(index, item)
|
|
65
|
+
new_component.parent = self #NOTE: I could do this in the constructor!
|
|
66
|
+
new_component.instance_exec(item, &block)
|
|
67
|
+
@components << new_component
|
|
68
|
+
}
|
|
69
|
+
end
|
|
70
|
+
def refresh #TODO write test
|
|
71
|
+
@components = []
|
|
72
|
+
end
|
|
73
|
+
def components
|
|
74
|
+
if @components.empty?
|
|
75
|
+
build_refresh_etc
|
|
76
|
+
end
|
|
77
|
+
@components
|
|
78
|
+
end
|
|
79
|
+
def size #NOTE: move to container?
|
|
80
|
+
return components.size
|
|
81
|
+
end
|
|
82
|
+
#TODO: remove duplication
|
|
83
|
+
def [](index)
|
|
84
|
+
index = index.to_i if index.kind_of?(String)
|
|
85
|
+
return components[index]
|
|
86
|
+
end
|
|
87
|
+
def component(index)
|
|
88
|
+
index = index.to_i if index.kind_of?(String)
|
|
89
|
+
return components[index]
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
#TODO: result is a duplicate with super class method!
|
|
93
|
+
def render_container(listview_element)
|
|
94
|
+
result = REXML::Element.new(listview_element)
|
|
95
|
+
components.each {|container|
|
|
96
|
+
container.render_children(listview_element,result)
|
|
97
|
+
}
|
|
98
|
+
return result
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
class ListItem < Container
|
|
103
|
+
def method_missing(method)
|
|
104
|
+
component(method.to_s)
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
class Container < Component
|
|
109
|
+
def self.listview(id, options={}, &block)
|
|
110
|
+
options.keys.each {|key|
|
|
111
|
+
raise "Unknown key: #{key}" if key!=:model and key!=:property
|
|
112
|
+
}
|
|
113
|
+
raise "no block supplied!" if !block_given?
|
|
114
|
+
internal_add_component(id) { ListView.new(id, options[:model], options[:property]) }
|
|
115
|
+
internal_add_block(id, block)
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
class Form < Container
|
|
120
|
+
def initialize(id, value=nil)
|
|
121
|
+
super(id, value)
|
|
122
|
+
end
|
|
123
|
+
def on_submit_handler=function
|
|
124
|
+
@function=function
|
|
125
|
+
end
|
|
126
|
+
def on_submit(button=nil)
|
|
127
|
+
instance_eval(&@function)
|
|
128
|
+
end
|
|
129
|
+
def render_to_element(element)
|
|
130
|
+
element.add_attribute("method", "post")
|
|
131
|
+
element.attributes['enctype'] = 'multipart/form-data'
|
|
132
|
+
input = element.add_element("input")
|
|
133
|
+
input.attributes['type'] = 'hidden'
|
|
134
|
+
input.attributes['name'] = 'page'
|
|
135
|
+
input.attributes['value'] = webpage.class.to_s
|
|
136
|
+
end
|
|
137
|
+
def post(values)
|
|
138
|
+
components.each {|component| component.post(values)}
|
|
139
|
+
if values.keys.find {|key| key.include?(identifier)}
|
|
140
|
+
on_submit(values['submit'].nil? ? nil : values['submit'].string)
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
class BookmarkablePageLink < Container
|
|
147
|
+
attr_reader :page_parameters, :page_url
|
|
148
|
+
def initialize(id, page_url, page_parameters)
|
|
149
|
+
super(id, nil)
|
|
150
|
+
@page_url = page_url
|
|
151
|
+
@page_parameters = page_parameters
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def render_to_element(element)
|
|
155
|
+
element.add_attribute("href", url)
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def url
|
|
159
|
+
"#{@page_url}/"+page_parameters.sort.collect {|key, value| key.to_s+'/'+value.to_s}.join('/')
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
#TODO: merge constructor of BookmarkableWebpage and the standard Webpage class
|
|
164
|
+
class BookmarkableWebpage < Webpage
|
|
165
|
+
def initialize(page_parameters=Hash[])
|
|
166
|
+
super()
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
class Fragment < Container
|
|
171
|
+
def initialize(id, fragment_id, model=nil)
|
|
172
|
+
super(id, model)
|
|
173
|
+
@fragment_id = fragment_id
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def render_container(container_element)
|
|
177
|
+
root_node=container_element.root
|
|
178
|
+
fragment = REXML::XPath.first( root_node, "//*[@lapillus:id=\"#{@fragment_id}\"]" )
|
|
179
|
+
raise "Fragment with identifier "+@fragment_id+" not found!" if fragment == nil
|
|
180
|
+
new_element = REXML::Element.new(container_element)
|
|
181
|
+
render_children(fragment, new_element)
|
|
182
|
+
return new_element
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
class Panel < Container
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
class Container
|
|
190
|
+
def self.panel(id, clazz, model=nil, options={})
|
|
191
|
+
add_component(id, clazz, model)
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
class ListItem
|
|
196
|
+
def panel(id, clazz, model=nil, options={})
|
|
197
|
+
add(clazz.new(id, model))
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
#TODO: whats in a name
|
|
202
|
+
class RemotePanel < Container
|
|
203
|
+
def render_container(container_element)
|
|
204
|
+
#NOTE: duplicated in Webpage class above
|
|
205
|
+
doc = REXML::Document.new default_htmlfile
|
|
206
|
+
root_node = doc.root
|
|
207
|
+
fragment = REXML::XPath.first( root_node, "//body")
|
|
208
|
+
raise "Body element not found!" if fragment == nil
|
|
209
|
+
new_element = REXML::Element.new(container_element)
|
|
210
|
+
render_children(fragment, new_element)
|
|
211
|
+
render_to_element(new_element)
|
|
212
|
+
render_behaviours(new_element)
|
|
213
|
+
return new_element
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
#NOTE: duplicate between render and render_container
|
|
217
|
+
def render(html=default_htmlfile)
|
|
218
|
+
# @html is al File
|
|
219
|
+
# html = @html if !@html.nil?
|
|
220
|
+
doc = REXML::Document.new html
|
|
221
|
+
root_element = doc.root
|
|
222
|
+
new_element = REXML::Element.new(root_element)
|
|
223
|
+
render_children(root_element, new_element)
|
|
224
|
+
return doc.doctype.to_s + new_element.to_s
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
#duplicate with methods in webpage!
|
|
228
|
+
def default_htmlfile
|
|
229
|
+
htmlfile = "html/" + self.class.to_s + ".html"
|
|
230
|
+
File.new( htmlfile )
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
end
|
|
234
|
+
end
|