mullen-wee 2.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +127 -0
- data/Rakefile +25 -0
- data/aWee.gemspec +26 -0
- data/examples/ObjectSpaceBrowser.rb +191 -0
- data/examples/ajax.rb +73 -0
- data/examples/apotomo-webhunter/main.rb +75 -0
- data/examples/apotomo-webhunter/public/images/bear_trap_charged.png +0 -0
- data/examples/apotomo-webhunter/public/images/bear_trap_snapped.png +0 -0
- data/examples/apotomo-webhunter/public/images/cheese.png +0 -0
- data/examples/apotomo-webhunter/public/images/dark_forest.jpg +0 -0
- data/examples/apotomo-webhunter/public/images/mouse.png +0 -0
- data/examples/apotomo-webhunter/public/javascripts/jquery-1.3.2.min.js +19 -0
- data/examples/apotomo-webhunter/public/javascripts/wee-jquery.js +19 -0
- data/examples/apotomo-webhunter/public/stylesheets/forest.css +33 -0
- data/examples/arc_challenge.rb +42 -0
- data/examples/arc_challenge2.rb +46 -0
- data/examples/cheese_task.rb +27 -0
- data/examples/continuations.rb +28 -0
- data/examples/demo.rb +135 -0
- data/examples/demo/calculator.rb +63 -0
- data/examples/demo/calendar.rb +333 -0
- data/examples/demo/counter.rb +38 -0
- data/examples/demo/editable_counter.rb +36 -0
- data/examples/demo/example.rb +142 -0
- data/examples/demo/file_upload.rb +19 -0
- data/examples/demo/messagebox.rb +15 -0
- data/examples/demo/radio.rb +33 -0
- data/examples/demo/window.rb +71 -0
- data/examples/hw.rb +11 -0
- data/examples/i18n/app.rb +16 -0
- data/examples/i18n/locale/de/app.po +25 -0
- data/examples/i18n/locale/en/app.po +25 -0
- data/examples/pager.rb +102 -0
- data/lib/wee.rb +109 -0
- data/lib/wee/application.rb +89 -0
- data/lib/wee/callback.rb +109 -0
- data/lib/wee/component.rb +363 -0
- data/lib/wee/decoration.rb +251 -0
- data/lib/wee/dialog.rb +171 -0
- data/lib/wee/external_resource.rb +39 -0
- data/lib/wee/html_brushes.rb +795 -0
- data/lib/wee/html_canvas.rb +254 -0
- data/lib/wee/html_document.rb +52 -0
- data/lib/wee/html_writer.rb +71 -0
- data/lib/wee/id_generator.rb +81 -0
- data/lib/wee/jquery.rb +11 -0
- data/lib/wee/jquery/jquery-1.3.2.min.js +19 -0
- data/lib/wee/jquery/wee-jquery.js +19 -0
- data/lib/wee/locale.rb +56 -0
- data/lib/wee/lru_cache.rb +91 -0
- data/lib/wee/presenter.rb +44 -0
- data/lib/wee/renderer.rb +72 -0
- data/lib/wee/request.rb +56 -0
- data/lib/wee/response.rb +68 -0
- data/lib/wee/rightjs.rb +11 -0
- data/lib/wee/rightjs/rightjs-1.5.2.min.js +9 -0
- data/lib/wee/rightjs/wee-rightjs.js +18 -0
- data/lib/wee/root_component.rb +45 -0
- data/lib/wee/session.rb +366 -0
- data/lib/wee/state.rb +102 -0
- data/lib/wee/task.rb +16 -0
- data/test/bm_render.rb +34 -0
- data/test/component_spec.rb +40 -0
- data/test/stress/plotter.rb +84 -0
- data/test/stress/stress_client.rb +51 -0
- data/test/stress/stress_local.rb +86 -0
- data/test/stress/stress_server.rb +83 -0
- data/test/test_component.rb +106 -0
- data/test/test_html_canvas.rb +25 -0
- data/test/test_html_writer.rb +32 -0
- data/test/test_lru_cache.rb +51 -0
- data/test/test_request.rb +42 -0
- metadata +185 -0
@@ -0,0 +1,38 @@
|
|
1
|
+
class Counter < Wee::Component
|
2
|
+
attr_accessor :count
|
3
|
+
|
4
|
+
def initialize(initial_count=0)
|
5
|
+
@count = initial_count
|
6
|
+
add_decoration Wee::StyleDecoration.new(self)
|
7
|
+
end
|
8
|
+
|
9
|
+
def state(s) super
|
10
|
+
s.add_ivar(self, :@count)
|
11
|
+
end
|
12
|
+
|
13
|
+
def dec
|
14
|
+
@count -= 1
|
15
|
+
end
|
16
|
+
|
17
|
+
def inc
|
18
|
+
@count += 1
|
19
|
+
end
|
20
|
+
|
21
|
+
def style
|
22
|
+
".wee-Counter a { border: 1px dotted blue; margin: 2px; }"
|
23
|
+
end
|
24
|
+
|
25
|
+
def render(r)
|
26
|
+
r.div.oid.css_class('wee-Counter').with {
|
27
|
+
r.anchor.callback_method(:dec).with("--")
|
28
|
+
r.space
|
29
|
+
render_count(r)
|
30
|
+
r.space
|
31
|
+
r.anchor.callback_method(:inc).with("++")
|
32
|
+
}
|
33
|
+
end
|
34
|
+
|
35
|
+
def render_count(r)
|
36
|
+
r.text @count.to_s
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# NEEDS: FormDecoration
|
2
|
+
|
3
|
+
require 'demo/counter'
|
4
|
+
|
5
|
+
class EditableCounter < Counter
|
6
|
+
|
7
|
+
def initialize(initial_count=0)
|
8
|
+
super
|
9
|
+
@show_edit_field = false
|
10
|
+
end
|
11
|
+
|
12
|
+
def state(s)
|
13
|
+
super
|
14
|
+
s.add_ivar(self, :@show_edit_field, @show_edit_field)
|
15
|
+
end
|
16
|
+
|
17
|
+
def render_count(r)
|
18
|
+
if @show_edit_field
|
19
|
+
r.text_input.value(@count).size(6).callback {|val| self.count = val}
|
20
|
+
r.submit_button.value('S').callback { submit }
|
21
|
+
else
|
22
|
+
r.anchor.callback { submit }.with(@count)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def submit
|
27
|
+
if @count.to_s !~ /^\d+$/
|
28
|
+
call Wee::MessageBox.new("You entered an invalid counter! Please try again!")
|
29
|
+
@count = 0
|
30
|
+
else
|
31
|
+
@show_edit_field = !@show_edit_field
|
32
|
+
end
|
33
|
+
@count = @count.to_i
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
@@ -0,0 +1,142 @@
|
|
1
|
+
$LOAD_PATH.unshift "../lib"
|
2
|
+
require 'rubygems'
|
3
|
+
require 'wee'
|
4
|
+
|
5
|
+
require 'demo/window'
|
6
|
+
require 'demo/editable_counter'
|
7
|
+
require 'demo/messagebox'
|
8
|
+
|
9
|
+
class RegexpValidatedInput < Wee::Component
|
10
|
+
|
11
|
+
def initialize(init_value, regexp)
|
12
|
+
super()
|
13
|
+
@regexp = regexp
|
14
|
+
self.value = init_value
|
15
|
+
@error = false
|
16
|
+
end
|
17
|
+
|
18
|
+
def state(s)
|
19
|
+
super
|
20
|
+
s.add(self)
|
21
|
+
end
|
22
|
+
|
23
|
+
def value
|
24
|
+
@value
|
25
|
+
end
|
26
|
+
|
27
|
+
def value=(new_value)
|
28
|
+
raise unless new_value =~ @regexp
|
29
|
+
@input = @value = new_value
|
30
|
+
end
|
31
|
+
|
32
|
+
def render(r)
|
33
|
+
r.form do
|
34
|
+
r.text_input.value(@input).callback {|val| self.input = val }
|
35
|
+
r.text %(<div style="color: red">Invalid input</div>) if @error
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def input
|
40
|
+
@input
|
41
|
+
end
|
42
|
+
|
43
|
+
def input=(str)
|
44
|
+
@input = str
|
45
|
+
|
46
|
+
if @input =~ @regexp
|
47
|
+
@value = str
|
48
|
+
@error = false
|
49
|
+
else
|
50
|
+
@error = true
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
class MainPage < Wee::Component
|
57
|
+
def initialize
|
58
|
+
super()
|
59
|
+
@counters = (1..10).map {|i|
|
60
|
+
Window.new {|w|
|
61
|
+
w.title = "Cnt #{ i }"
|
62
|
+
w.pos_x = "200px"
|
63
|
+
w.pos_y = "#{i*50}px"
|
64
|
+
w << EditableCounter.new(i)
|
65
|
+
}
|
66
|
+
}
|
67
|
+
|
68
|
+
@val_inp = RegexpValidatedInput.new('Michael Neumann', /^\w+\s+\w+$/)
|
69
|
+
|
70
|
+
@arr = []
|
71
|
+
@text = ""
|
72
|
+
|
73
|
+
@list1 = (0..9).to_a
|
74
|
+
@selected1 = []
|
75
|
+
@list2 = []
|
76
|
+
@selected2 = []
|
77
|
+
end
|
78
|
+
|
79
|
+
def children
|
80
|
+
[@val_inp, *@counters]
|
81
|
+
end
|
82
|
+
|
83
|
+
def state(s)
|
84
|
+
super
|
85
|
+
s.add(@counters)
|
86
|
+
state_decoration(s)
|
87
|
+
s.add(@arr)
|
88
|
+
s.add(@text)
|
89
|
+
|
90
|
+
s.add(@list1)
|
91
|
+
s.add(@selected1)
|
92
|
+
s.add(@list2)
|
93
|
+
s.add(@selected2)
|
94
|
+
end
|
95
|
+
|
96
|
+
attr_accessor :text
|
97
|
+
|
98
|
+
def render(r)
|
99
|
+
r.page.title("Counter Test").with do
|
100
|
+
|
101
|
+
r.form do
|
102
|
+
r.select_list(@list1).size(10).multiple.selected(@selected1).callback {|choosen| @selected1.replace(choosen)}
|
103
|
+
r.submit_button.value('->').callback { @list2.push(*@selected1); @list1.replace(@list1-@selected1); @selected1.replace([]) }
|
104
|
+
r.submit_button.value('<-').callback { @list1.push(*@selected2); @list2.replace(@list2-@selected2); @selected2.replace([]) }
|
105
|
+
r.select_list(@list2).size(10).multiple.selected(@selected2).callback {|choosen| @selected2.replace(choosen)}
|
106
|
+
end
|
107
|
+
|
108
|
+
r.form do
|
109
|
+
|
110
|
+
@counters.each { |cnt|
|
111
|
+
r.render(cnt)
|
112
|
+
}
|
113
|
+
|
114
|
+
r.render(@val_inp)
|
115
|
+
|
116
|
+
@arr.each do |a|
|
117
|
+
r.text(a)
|
118
|
+
r.break
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
122
|
+
|
123
|
+
r.form do
|
124
|
+
r.text_input.value(@text).callback{|val| @text = val}
|
125
|
+
r.submit_button.callback{add}.value('add')
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def add
|
132
|
+
call Wee::MessageBox.new("Do you really want to add '" + @text + "'?") do |res|
|
133
|
+
if res
|
134
|
+
call Wee::MessageBox.new("Do you really really really want to add '" + @text + "'?") do |res2|
|
135
|
+
@arr << @text if res2
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
Wee.run(MainPage) if __FILE__ == $0
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class FileUploadTest < Wee::Component
|
2
|
+
def render(r)
|
3
|
+
r.file_upload.callback {|f| call Uploaded.new(f[:tempfile]) }
|
4
|
+
r.break
|
5
|
+
r.submit_button.name('Upload')
|
6
|
+
end
|
7
|
+
|
8
|
+
class Uploaded < Wee::Component
|
9
|
+
def initialize(file)
|
10
|
+
super()
|
11
|
+
@file = file
|
12
|
+
end
|
13
|
+
|
14
|
+
def render(r)
|
15
|
+
r.pre { r.encode_text @file.read }
|
16
|
+
r.anchor.callback_method(:answer).with('back')
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class Wee::MessageBox < Wee::Component
|
2
|
+
def initialize(text)
|
3
|
+
super()
|
4
|
+
@text = text
|
5
|
+
end
|
6
|
+
|
7
|
+
def render(r)
|
8
|
+
r.bold(@text)
|
9
|
+
r.form do
|
10
|
+
r.submit_button.value('OK').callback { answer true }
|
11
|
+
r.space
|
12
|
+
r.submit_button.value('Cancel').callback { answer false }
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
class RadioTest < Wee::Component
|
2
|
+
def render(r)
|
3
|
+
grp1 = r.new_radio_group
|
4
|
+
grp2 = r.new_radio_group
|
5
|
+
|
6
|
+
r.paragraph
|
7
|
+
r.text "Group1"
|
8
|
+
r.text " (your choice: #{@g1})" if @g1
|
9
|
+
r.break
|
10
|
+
|
11
|
+
r.text "R1: "
|
12
|
+
r.radio_button.group(grp1).checked(@g1.nil? || @g1 == 'R1').callback { @g1 = 'R1' }
|
13
|
+
r.break
|
14
|
+
|
15
|
+
r.text "R2: "
|
16
|
+
r.radio_button.group(grp1).checked(@g1 == 'R2').callback { @g1 = 'R2' }
|
17
|
+
|
18
|
+
r.paragraph
|
19
|
+
r.text "Group2"
|
20
|
+
r.text " (your choice: #{@g2})" if @g2
|
21
|
+
r.break
|
22
|
+
|
23
|
+
r.text "R1: "
|
24
|
+
r.radio_button.group(grp2).checked(@g2.nil? || @g2 == 'R1').callback { @g2 = 'R1' }
|
25
|
+
r.break
|
26
|
+
|
27
|
+
r.text "R2: "
|
28
|
+
r.radio_button.group(grp2).checked(@g2 == 'R2').callback { @g2 = 'R2' }
|
29
|
+
|
30
|
+
r.paragraph
|
31
|
+
r.submit_button.value('Submit')
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
class Window < Wee::Component
|
2
|
+
|
3
|
+
attr_accessor :title, :pos_x, :pos_y
|
4
|
+
|
5
|
+
def initialize(&block)
|
6
|
+
super()
|
7
|
+
@status = :normal
|
8
|
+
@pos_x, @pos_y = "0px", "0px"
|
9
|
+
@children = []
|
10
|
+
block.call(self) if block
|
11
|
+
end
|
12
|
+
|
13
|
+
def <<(c)
|
14
|
+
@children << c
|
15
|
+
end
|
16
|
+
|
17
|
+
def children() @children end
|
18
|
+
|
19
|
+
def state(s)
|
20
|
+
super
|
21
|
+
s.add_ivar(self, :@status, @status)
|
22
|
+
end
|
23
|
+
|
24
|
+
def process_callbacks(callbacks)
|
25
|
+
return if @status == :closed
|
26
|
+
super
|
27
|
+
end
|
28
|
+
|
29
|
+
def render(r)
|
30
|
+
return if @status == :closed
|
31
|
+
|
32
|
+
r.table.cellspacing(0).style("border:solid 1px grey; position: absolute; left: #{@pos_x}; top: #{@pos_y};").with do
|
33
|
+
r.table_row.style("background-color: lightblue; width: 100%").with do
|
34
|
+
r.table_data.style("text-align: left; width: 66%").with(@title)
|
35
|
+
r.table_data.style("text-align: right").with do
|
36
|
+
if @status == :minimized
|
37
|
+
r.anchor.callback{maximize}.with("^")
|
38
|
+
else
|
39
|
+
r.anchor.callback{minimize}.with("_")
|
40
|
+
end
|
41
|
+
r.space
|
42
|
+
r.anchor.callback{close}.with("x")
|
43
|
+
end
|
44
|
+
end
|
45
|
+
r.table_row do
|
46
|
+
r.table_data.colspan(2).with do
|
47
|
+
if @status == :normal
|
48
|
+
for child in self.children do
|
49
|
+
r.render(child)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
public
|
58
|
+
|
59
|
+
def minimize
|
60
|
+
@status = :minimized
|
61
|
+
end
|
62
|
+
|
63
|
+
def maximize
|
64
|
+
@status = :normal
|
65
|
+
end
|
66
|
+
|
67
|
+
def close
|
68
|
+
@status = :closed
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
data/examples/hw.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
$LOAD_PATH.unshift "../../lib"
|
2
|
+
require 'rubygems'
|
3
|
+
require 'wee'
|
4
|
+
require 'wee/locale'
|
5
|
+
|
6
|
+
class HelloWorld < Wee::RootComponent
|
7
|
+
def render(r)
|
8
|
+
r.h1 _("Hello World!")
|
9
|
+
r.select_list(%w(en de)).selected(session.locale).labels(["English", "Deutsch"]).callback {|lang| session.locale = lang}
|
10
|
+
r.submit_button.value(_("Set"))
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
Wee::Application.load_locale("app", %w(en de), "en", :path => "locale", :type => :po)
|
15
|
+
|
16
|
+
HelloWorld.run if __FILE__ == $0
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# SOME DESCRIPTIVE TITLE.
|
2
|
+
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
3
|
+
# This file is distributed under the same license as the PACKAGE package.
|
4
|
+
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
5
|
+
#
|
6
|
+
#, fuzzy
|
7
|
+
msgid ""
|
8
|
+
msgstr ""
|
9
|
+
"Project-Id-Version: PACKAGE VERSION\n"
|
10
|
+
"POT-Creation-Date: 2010-01-05 00:27+0100\n"
|
11
|
+
"PO-Revision-Date: 2010-01-05 00:27+0100\n"
|
12
|
+
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
13
|
+
"Language-Team: LANGUAGE <LL@li.org>\n"
|
14
|
+
"MIME-Version: 1.0\n"
|
15
|
+
"Content-Type: text/plain; charset=UTF-8\n"
|
16
|
+
"Content-Transfer-Encoding: 8bit\n"
|
17
|
+
"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
|
18
|
+
|
19
|
+
#: app.rb:8
|
20
|
+
msgid "Hello World!"
|
21
|
+
msgstr "Hallo Welt!"
|
22
|
+
|
23
|
+
#: app.rb:10
|
24
|
+
msgid "Set"
|
25
|
+
msgstr "Einstellen"
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# SOME DESCRIPTIVE TITLE.
|
2
|
+
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
3
|
+
# This file is distributed under the same license as the PACKAGE package.
|
4
|
+
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
5
|
+
#
|
6
|
+
#, fuzzy
|
7
|
+
msgid ""
|
8
|
+
msgstr ""
|
9
|
+
"Project-Id-Version: PACKAGE VERSION\n"
|
10
|
+
"POT-Creation-Date: 2010-01-05 00:27+0100\n"
|
11
|
+
"PO-Revision-Date: 2010-01-05 00:27+0100\n"
|
12
|
+
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
13
|
+
"Language-Team: LANGUAGE <LL@li.org>\n"
|
14
|
+
"MIME-Version: 1.0\n"
|
15
|
+
"Content-Type: text/plain; charset=UTF-8\n"
|
16
|
+
"Content-Transfer-Encoding: 8bit\n"
|
17
|
+
"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
|
18
|
+
|
19
|
+
#: app.rb:8
|
20
|
+
msgid "Hello World!"
|
21
|
+
msgstr ""
|
22
|
+
|
23
|
+
#: app.rb:10
|
24
|
+
msgid "Set"
|
25
|
+
msgstr ""
|
data/examples/pager.rb
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
class Pager < Wee::Component
|
2
|
+
attr_accessor :num_entries, :entries_per_page
|
3
|
+
attr_reader :current_page
|
4
|
+
|
5
|
+
def initialize(num_entries=0)
|
6
|
+
super()
|
7
|
+
@num_entries = num_entries
|
8
|
+
@current_page = 0
|
9
|
+
@entries_per_page = 20
|
10
|
+
yield self if block_given?
|
11
|
+
end
|
12
|
+
|
13
|
+
# Returns the number of pages
|
14
|
+
|
15
|
+
def num_pages
|
16
|
+
n, rest = @num_entries.divmod(@entries_per_page)
|
17
|
+
if rest > 0 then n + 1 else n end
|
18
|
+
end
|
19
|
+
|
20
|
+
# Returns the index of the first entry on the current page
|
21
|
+
|
22
|
+
def current_start_index
|
23
|
+
@current_page * @entries_per_page
|
24
|
+
end
|
25
|
+
|
26
|
+
# Returns the index of the last page
|
27
|
+
|
28
|
+
def last_page_index
|
29
|
+
num_pages() - 1
|
30
|
+
end
|
31
|
+
|
32
|
+
# Go to first page
|
33
|
+
|
34
|
+
def first
|
35
|
+
goto(0)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Go to last page
|
39
|
+
|
40
|
+
def last
|
41
|
+
goto(last_page_index())
|
42
|
+
end
|
43
|
+
|
44
|
+
# Go to previous page
|
45
|
+
|
46
|
+
def prev
|
47
|
+
goto(@current_page - 1)
|
48
|
+
end
|
49
|
+
|
50
|
+
# Go to next page
|
51
|
+
|
52
|
+
def next
|
53
|
+
goto(@current_page + 1)
|
54
|
+
end
|
55
|
+
|
56
|
+
# Go to page with index +page+
|
57
|
+
# Note that page-indices start with zero!
|
58
|
+
|
59
|
+
def goto(page)
|
60
|
+
@current_page = page
|
61
|
+
validate
|
62
|
+
end
|
63
|
+
|
64
|
+
def render(r)
|
65
|
+
return if num_pages() <= 0
|
66
|
+
render_arrow(r, :first, "<<", "Go to first page"); r.space(2)
|
67
|
+
render_arrow(r, :prev, "<", "Go to previous page"); r.space(2)
|
68
|
+
render_index(r); r.space(2)
|
69
|
+
render_arrow(r, :next, ">", "Go to next page"); r.space(2)
|
70
|
+
render_arrow(r, :last, ">>", "Go to last page")
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
def render_arrow(r, sym, text, tooltip=text)
|
76
|
+
r.anchor.callback_method(sym).tooltip(tooltip).with { r.encode_text(text) }
|
77
|
+
end
|
78
|
+
|
79
|
+
def render_index(r)
|
80
|
+
last = last_page_index()
|
81
|
+
(0 .. last).each do |i|
|
82
|
+
if i == @current_page
|
83
|
+
render_page_num(r, i, true)
|
84
|
+
else
|
85
|
+
render_page_num(r, i, false)
|
86
|
+
end
|
87
|
+
r.space if i < last
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def render_page_num(r, num, current)
|
92
|
+
if current
|
93
|
+
r.bold(num+1)
|
94
|
+
else
|
95
|
+
r.anchor.callback{ goto(num) }.with(num+1)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def validate
|
100
|
+
@current_page = [[0, @current_page].max, last_page_index()].min
|
101
|
+
end
|
102
|
+
end
|