rwdaddresses 0.95 → 0.97
Sign up to get free protection for your applications and to get access to all the features.
- data/Readme.txt +13 -1
- data/code/01rwdcore/03helptexthash.rb +11 -28
- data/code/01rwdcore/openhelpwindow.rb +10 -24
- data/code/01rwdcore/returntomain.rb +10 -0
- data/code/superant.com.rwdaddresses/helptexthashrwdaddresses.rb +27 -11
- data/code/superant.com.rwdaddresses/openhelpwindowrwdaddresses.rb +12 -7
- data/code/superant.com.rwdaddresses/returntomain.rb +10 -0
- data/code/superant.com.rwdaddresses/runaddresseswindow.rb +2 -1
- data/code/superant.com.rwdaddresses/runrwdaddressesmenu1.rb +34 -0
- data/code/superant.com.rwdaddresses/rwdaddressesbackwindow.rb +10 -0
- data/code/superant.com.rwdtinkerbackwindow/controlclient.rb +100 -0
- data/code/superant.com.rwdtinkerbackwindow/helptexthashtinkerwin2.rb +37 -0
- data/code/superant.com.rwdtinkerbackwindow/installapplet.rb +8 -8
- data/code/superant.com.rwdtinkerbackwindow/network.rb +87 -0
- data/code/superant.com.rwdtinkerbackwindow/openhelpwindowtinkerwin2.rb +39 -0
- data/code/superant.com.rwdtinkerbackwindow/runrwdtinkerbackwindow.rb +1 -1
- data/code/superant.com.rwdtinkerbackwindow/viewappletcontents.rb +21 -0
- data/configuration/rwdaddressesversion.cnf +1 -1
- data/configuration/rwdapplicationidentity.cnf +3 -0
- data/configuration/rwdtinker.cnf +1 -0
- data/configuration/rwdtinkerversion.cnf +1 -1
- data/ev/rwd.rb +4 -3
- data/extras/zip/ioextras.rb +114 -0
- data/extras/zip/stdrubyext.rb +111 -0
- data/extras/zip/tempfile_bugfixed.rb +195 -0
- data/extras/zip/zip.rb +1376 -0
- data/extras/zip/zipfilesystem.rb +558 -0
- data/extras/zip/ziprequire.rb +61 -0
- data/gui/00coreguibegin/applicationguitop.rwd +1 -1
- data/gui/frontwindow0/superant.com.rwdaddresses/16editrecord.rwd +9 -9
- data/gui/frontwindow0/superant.com.rwdaddresses/18contactutilities.rwd +1 -0
- data/gui/frontwindow0/superant.com.rwdaddresses/m01menubegin.rwd +10 -0
- data/gui/frontwindow0/superant.com.rwdaddresses/m02menu.rwd +36 -0
- data/gui/frontwindow0/superant.com.rwdaddresses/m03menu.rwd +12 -0
- data/gui/frontwindow0/superant.com.rwdaddresses/m85menu.rwd +21 -0
- data/gui/frontwindow0/superant.com.rwdaddresses/m88menu.rwd +12 -0
- data/gui/frontwindow0/superant.com.rwdaddresses/m99menuend.rwd +3 -0
- data/gui/tinkerbackwindows/superant.com.addressesphotowindow/yy7addressesphoto.rwd +2 -1
- data/gui/{frontwindow0/superant.com.rwdaddresses/zviewconfiguration.rwd → tinkerbackwindows/superant.com.rwdaddressessyncbackwindow/88viewconfiguration.rwd} +9 -9
- data/gui/tinkerbackwindows/superant.com.rwdaddressessyncbackwindow/m01menubegin.rwd +10 -0
- data/gui/tinkerbackwindows/superant.com.rwdaddressessyncbackwindow/m02menu.rwd +36 -0
- data/gui/tinkerbackwindows/superant.com.rwdaddressessyncbackwindow/m03menu.rwd +12 -0
- data/gui/tinkerbackwindows/superant.com.rwdaddressessyncbackwindow/m85menu.rwd +21 -0
- data/gui/tinkerbackwindows/superant.com.rwdaddressessyncbackwindow/m88menu.rwd +18 -0
- data/gui/tinkerbackwindows/superant.com.rwdaddressessyncbackwindow/m99menuend.rwd +3 -0
- data/gui/tinkerbackwindows/superant.com.tinkerbackwindow/{50rwdlistzips.rwd → 40rwdlistzips.rwd} +10 -3
- data/gui/tinkerbackwindows/superant.com.tinkerbackwindow/50rwdlistapplets.rwd +1 -1
- data/gui/tinkerbackwindows/superant.com.tinkerbackwindow/60editconfiguration.rwd +11 -10
- data/gui/tinkerbackwindows/superant.com.tinkerbackwindow/75rwdcontrol.rwd +33 -0
- data/gui/tinkerbackwindows/superant.com.tinkerbackwindow/80tab1.rwd +1 -1
- data/gui/tinkerbackwindows/superant.com.tinkerhelpwindow/1appname.rwd +1 -2
- data/init.rb +38 -2
- data/installed/addressessample1.inf +5 -0
- data/rwd_files/HowTo_Addresses.txt +13 -1
- data/rwd_files/HowTo_Tinker.txt +19 -6
- data/rwd_files/contacttmp.jpg +0 -0
- data/tests/rwdtinkertestEN.rb +163 -0
- data/tests/test.result +32 -0
- data/zips/rwdaschedule-0.921.zip +0 -0
- data/zips/rwdcalc-0.4.zip +0 -0
- metadata +86 -54
- data/gui/tinkerbackwindows/superant.com.rwdaddressessyncbackwindow/22viewconfiguration.rwd +0 -16
- data/gui/tinkerbackwindows/superant.com.rwdaddressessyncbackwindow/80returntab.rwd +0 -11
- data/installed/rwdviewlogo-0.4.inf +0 -4
data/Readme.txt
CHANGED
@@ -82,7 +82,7 @@ Click on "List Files"
|
|
82
82
|
== Attach photo
|
83
83
|
on the Contacts Utilities tab
|
84
84
|
select the name record,
|
85
|
-
put a
|
85
|
+
put a jpg photo in the /tmp directory
|
86
86
|
select the photo
|
87
87
|
click on Attach photo, it will rename the photo and copy it to the
|
88
88
|
addresses directory
|
@@ -147,6 +147,18 @@ erik = secret
|
|
147
147
|
Thanks, Steven Gibson
|
148
148
|
|
149
149
|
== Changelog
|
150
|
+
version 0.97
|
151
|
+
updated for rwdtinker version 1.51
|
152
|
+
added RwdAddresses Menu tab for easier navigation
|
153
|
+
Improvement to context sensitive help
|
154
|
+
wider text boxes for contact information
|
155
|
+
|
156
|
+
version 0.96
|
157
|
+
added tab for jump to addresses window
|
158
|
+
added return from photo window
|
159
|
+
added contact name on photo window
|
160
|
+
changed return from back window
|
161
|
+
|
150
162
|
version 0.95
|
151
163
|
added some context sensitive help
|
152
164
|
added edit config file and reload config variables
|
@@ -13,36 +13,19 @@ init.rb file to something like:\n
|
|
13
13
|
RwdTinker.file(RWDFile).serve(8080, '-r' ? '.rwduids' : nil)\n
|
14
14
|
Then you should be able to login at: http://yourhostname:8080",
|
15
15
|
|
16
|
-
|
17
|
-
To see the text of a install file
|
18
|
-
Type in the name of the file you want.
|
19
|
-
|
20
|
-
type it without the inf ending
|
21
|
-
rwdapplet-0.1
|
22
|
-
|
23
|
-
Then click the 'View Installed File' button
|
24
|
-
|
25
|
-
If it finds the file it will open it. It displays the text the search box.
|
26
|
-
|
27
|
-
You can uninstall applets on this screen",
|
28
|
-
|
29
|
-
:applet_installation => " You can download an applet created by someone else\n
|
30
|
-
put the file in the zips directory\n
|
31
|
-
|
32
|
-
click on the 'list zips' tab\n
|
33
|
-
click on 'list applets available for installing'\n
|
34
|
-
|
35
|
-
chose the applet you want\n
|
36
|
-
click on 'install applet'\n
|
37
|
-
If all goes well the applet should be installed.\n
|
38
|
-
You will need to restart rwdtinker to see the new screens",
|
39
|
-
:applet_tinkerbackeditconfiguration => " You can edit the configuration
|
40
|
-
|
41
|
-
Remember to reload the configuration variables if you want your
|
42
|
-
changes used.",
|
16
|
+
|
43
17
|
:documents_rwdtinker => "Documents:
|
44
18
|
|
45
19
|
|
46
20
|
when you click on a document name, the document should pop up in a
|
47
|
-
browser window, or in another browser tab."
|
21
|
+
browser window, or in another browser tab.",
|
22
|
+
:remote_control => "Remote control gives you a way to determine what applications are running on which ports
|
23
|
+
|
24
|
+
The name of the application is set in the config file
|
25
|
+
You need to click the show remote command choices first - this will show the port and query choices
|
26
|
+
Then you can click in 'Run Command' to see if other applications are running.
|
27
|
+
|
28
|
+
The remote ports daemon will not be running unless you start it.
|
29
|
+
|
30
|
+
"
|
48
31
|
|
@@ -1,36 +1,22 @@
|
|
1
1
|
# this code is to call the help window
|
2
2
|
def runhelpwindow
|
3
|
-
|
3
|
+
$helptopicsarray = $helptopicsarray | ["applet_installation","applet_viewing","applet_tinkerbackeditconfiguration","remote_control"]
|
4
|
+
|
4
5
|
if @rwd_window == "main"
|
5
6
|
if @rwd_tab == "documentsrwdtinkerone"
|
6
7
|
$help_topic = "documents_rwdtinker"
|
7
8
|
end
|
8
|
-
|
9
|
+
|
10
|
+
end
|
11
|
+
|
12
|
+
if @rwd_window == "rwdtinkerhelpwindow"
|
13
|
+
puts "in help window processing"
|
14
|
+
else
|
9
15
|
$previouswindow = @rwd_window
|
10
16
|
$previoustab = @rwd_tab
|
11
|
-
|
12
|
-
end
|
13
|
-
|
14
|
-
if @rwd_window == "tinkerbackwindow"
|
15
|
-
if @rwd_tab == "filelister"
|
16
|
-
$help_topic = "applet_viewing"
|
17
|
-
$helptopicsarray = $helptopicsarray | ["applet_installation","applet_viewing"]
|
18
|
-
end
|
19
|
-
if @rwd_tab == "zipslister"
|
20
|
-
$help_topic = "applet_installation"
|
21
|
-
$helptopicsarray = $helptopicsarray | ["applet_installation","applet_viewing"]
|
22
|
-
end
|
23
|
-
if @rwd_tab == "editconfigurationfile"
|
24
|
-
$help_topic = "applet_tinkerbackeditconfiguration"
|
25
|
-
$helptopicsarray = $helptopicsarray | ["applet_installation","applet_viewing"]
|
26
|
-
end
|
27
|
-
|
28
|
-
$previouswindow = @rwd_window
|
29
|
-
$previoustab = @rwd_tab
|
30
|
-
end
|
31
|
-
if @rwd_window == "rwdtinkerhelpwindow"
|
32
|
-
$helptopicsarray = $helptopicsarray | ["applet_installation","applet_viewing"]
|
33
17
|
end
|
18
|
+
|
19
|
+
|
34
20
|
@rwdhelptopic = $help_topic
|
35
21
|
@rwdhelpdataoutput = "#{Helptext[:"#{$help_topic}"]}"
|
36
22
|
@rwdhelpsubchoicesoutput = $helptopicsarray.rwd_method("choosenewhelptopic")
|
@@ -1,6 +1,16 @@
|
|
1
1
|
|
2
2
|
|
3
|
-
Helptext.update(
|
3
|
+
Helptext.update(:viewaddresses_help => " Type in the name of the contact you want.
|
4
|
+
|
5
|
+
Type it as the record file is saved or click on the list
|
6
|
+
|
7
|
+
|
8
|
+
Then click the 'Open' button
|
9
|
+
|
10
|
+
If it finds the record it will open it. It displays the record as text under the search box.
|
11
|
+
",
|
12
|
+
|
13
|
+
:editcontact_help => " Use the 'Edit Contact' tab
|
4
14
|
this is where you can add records.
|
5
15
|
The first field is the filename that will be created.
|
6
16
|
You can use the person's name, Ringo Starr
|
@@ -9,16 +19,22 @@
|
|
9
19
|
|
10
20
|
When you are finished press the Save Changes button
|
11
21
|
|
12
|
-
You use this same screen for editing an already saved event
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
+
You use this same screen for editing an already saved event
|
23
|
+
|
24
|
+
For the 'Fill Record' button to work, you need to select an existing record on 'view contact' tab
|
25
|
+
",
|
26
|
+
:contactutilities_help => "
|
27
|
+
on the Contacts Utilities tab you can
|
28
|
+
rename records
|
29
|
+
delete records
|
30
|
+
or attach photos
|
31
|
+
|
32
|
+
select the name record,
|
33
|
+
put a jpg photo in the /tmp directory
|
34
|
+
select the photo
|
35
|
+
click on Attach photo, it will rename the photo and copy it to the
|
36
|
+
addresses directory",
|
37
|
+
|
22
38
|
:rwdaddresses_configuration => " click on 'Edit Configuration'
|
23
39
|
You can view the configuration file
|
24
40
|
|
@@ -1,19 +1,24 @@
|
|
1
1
|
# this code is to call the help window
|
2
2
|
def runhelpwindowrwdaddresses
|
3
3
|
|
4
|
-
$help_topic = "
|
5
|
-
if @rwd_tab == "
|
6
|
-
$help_topic = "
|
4
|
+
$help_topic = "viewaddresses_help"
|
5
|
+
if @rwd_tab == "Viewnamedata"
|
6
|
+
$help_topic = "viewaddresses_help"
|
7
7
|
end
|
8
|
-
if @rwd_tab == "
|
8
|
+
if @rwd_tab == "viewaddressesconfiguration"
|
9
9
|
$help_topic = "rwdaddresses_configuration"
|
10
|
-
end
|
10
|
+
end
|
11
|
+
if @rwd_tab == "editnamerecordfile"
|
12
|
+
$help_topic = "editcontact_help"
|
13
|
+
end
|
14
|
+
if @rwd_tab == "contactutilitiesrwdaddresses"
|
15
|
+
$help_topic = "contactutilities_help"
|
16
|
+
end
|
11
17
|
if @rwd_tab == "rwdaddressessynctasks"
|
12
18
|
$help_topic = "rwdaddresses_sync"
|
13
19
|
end
|
14
|
-
$helptopicsarray = $helptopicsarray | ["
|
20
|
+
$helptopicsarray = $helptopicsarray | ["viewaddresses_help","editcontact_help","rwdaddresses_configuration","rwdaddresses_sync"]
|
15
21
|
|
16
|
-
|
17
22
|
|
18
23
|
if @rwd_window != "rwdtinkerhelpwindow"
|
19
24
|
$previouswindow = @rwd_window
|
@@ -0,0 +1,10 @@
|
|
1
|
+
# Application RwdAddresses orginally (c) 2004 Steven Gibson under GPL.
|
2
|
+
# This application is a contact book
|
3
|
+
# For more information about see http://www.rubyforge.net/projects/rwdapplications
|
4
|
+
|
5
|
+
# return to main
|
6
|
+
def returntomain
|
7
|
+
@rwd_window = "main"
|
8
|
+
@rwd_tab="rwdaddressesselectiontab"
|
9
|
+
end
|
10
|
+
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# Application RwdAddresses orginally (c) 2004 Steven Gibson under GPL.
|
2
|
+
#
|
3
|
+
# For more information about see http://www.rubyforge.net/projects/rwdapplications
|
4
|
+
|
5
|
+
# call the window
|
6
|
+
def runrwdaddressesmenu1
|
7
|
+
@rwd_window = "main"
|
8
|
+
@rwd_tab = "Viewnamedata"
|
9
|
+
end
|
10
|
+
def runrwdaddressesmenu2
|
11
|
+
@rwd_window = "main"
|
12
|
+
@rwd_tab = "editnamerecordfile"
|
13
|
+
end
|
14
|
+
|
15
|
+
|
16
|
+
def runrwdaddressesmenu3
|
17
|
+
@rwd_window = "main"
|
18
|
+
@rwd_tab = "contactutilitiesrwdaddresses"
|
19
|
+
end
|
20
|
+
def runrwdaddressesmenu4
|
21
|
+
@rwd_window = "rwdaddressessyncwindow"
|
22
|
+
@rwd_tab = "rwdaddressessynctasks"
|
23
|
+
end
|
24
|
+
|
25
|
+
def runrwdaddressesmenu10
|
26
|
+
@rwd_window = "rwdaddressessyncwindow"
|
27
|
+
@rwd_tab = "viewaddressesconfiguration"
|
28
|
+
end
|
29
|
+
|
30
|
+
def runrwdaddressesmenu11
|
31
|
+
@rwd_window = "main"
|
32
|
+
@rwd_tab = "documentsrwdtinkerone"
|
33
|
+
end
|
34
|
+
|
@@ -0,0 +1,10 @@
|
|
1
|
+
# Application RwdAddresses orginally (c) 2004 Steven Gibson under GPL.
|
2
|
+
# This application is a contact book
|
3
|
+
# For more information about see http://www.rubyforge.net/projects/rwdapplications
|
4
|
+
|
5
|
+
# call the window
|
6
|
+
def runrwdaddressesbackwindow
|
7
|
+
@rwd_window = "rwdaddresseswindow"
|
8
|
+
@rwd_tab="Viewnamedata"
|
9
|
+
end
|
10
|
+
|
@@ -0,0 +1,100 @@
|
|
1
|
+
#
|
2
|
+
|
3
|
+
def runcontrolcommand
|
4
|
+
require 'socket'
|
5
|
+
|
6
|
+
# Change this line...
|
7
|
+
ip = '127.0.0.1'
|
8
|
+
# And this one...
|
9
|
+
port = $rwdcontrolport1
|
10
|
+
if @a_remoteportinput
|
11
|
+
port = @a_remoteportinput.to_i
|
12
|
+
end
|
13
|
+
# Done!
|
14
|
+
|
15
|
+
|
16
|
+
commandtext = @a_remotecommandinput.to_s
|
17
|
+
|
18
|
+
remotehostnotfound = true
|
19
|
+
|
20
|
+
client = UDPSocket::new()
|
21
|
+
client.send(commandtext, 0, ip, port)
|
22
|
+
|
23
|
+
|
24
|
+
if (commandtext == "stop")
|
25
|
+
begin
|
26
|
+
@socket = UDPSocket::new()
|
27
|
+
@socket.bind(ip, port)
|
28
|
+
# Rescue the "Address in use" error
|
29
|
+
rescue Errno::EADDRINUSE
|
30
|
+
|
31
|
+
databack, info = client.recv(128)
|
32
|
+
@remotecommandresult = databack
|
33
|
+
@lastremotecommand = "rwdtinker remote:: Port #{port} on host #{ip} ."
|
34
|
+
remotehostnotfound = false
|
35
|
+
|
36
|
+
# Rescue the "Address not available" error
|
37
|
+
rescue Errno::EADDRNOTAVAIL
|
38
|
+
puts "Network plugin: Address #{host} is not available to bind."
|
39
|
+
|
40
|
+
# Rescue "permission denied errors
|
41
|
+
rescue Errno::EACCES
|
42
|
+
puts "Network plugin: Access denied when binding interface addresses. Did you try to bind a port under 1024 as a regular user?"
|
43
|
+
|
44
|
+
# Rescue all other errors
|
45
|
+
rescue
|
46
|
+
puts "Network plugin: An error occured."
|
47
|
+
|
48
|
+
# Rescue all other errors
|
49
|
+
end
|
50
|
+
if remotehostnotfound
|
51
|
+
@remotecommandresult = "Remote Host not running"
|
52
|
+
end
|
53
|
+
@socket.close
|
54
|
+
end
|
55
|
+
|
56
|
+
remotehostnotfound = true
|
57
|
+
if (commandtext == "query")
|
58
|
+
begin
|
59
|
+
@socket = UDPSocket::new()
|
60
|
+
@socket.bind(ip, port)
|
61
|
+
# Rescue the "Address in use" error
|
62
|
+
rescue Errno::EADDRINUSE
|
63
|
+
|
64
|
+
|
65
|
+
databack, info = client.recv(128)
|
66
|
+
@remotecommandresult = databack
|
67
|
+
@lastremotecommand = "rwdtinker remote: Port #{port} on host #{ip} "
|
68
|
+
remotehostnotfound = false
|
69
|
+
|
70
|
+
# Rescue the "Address not available' error
|
71
|
+
rescue Errno::EADDRNOTAVAIL
|
72
|
+
puts "Network plugin: Address #{host} is not available to bind."
|
73
|
+
|
74
|
+
# Rescue "permission denied errors
|
75
|
+
rescue Errno::EACCES
|
76
|
+
puts "Network plugin: Access denied when binding interface addresses. Did you try to bind a port under 1024 as a regular user?"
|
77
|
+
|
78
|
+
# Rescue all other errors
|
79
|
+
rescue
|
80
|
+
puts "Network plugin: An error occured."
|
81
|
+
|
82
|
+
# Rescue all other errors
|
83
|
+
end
|
84
|
+
@socket.close
|
85
|
+
end
|
86
|
+
if remotehostnotfound
|
87
|
+
@remotecommandresult = "Remote Host not running"
|
88
|
+
end
|
89
|
+
# Close the socket after use
|
90
|
+
client.close()
|
91
|
+
|
92
|
+
end
|
93
|
+
|
94
|
+
def showremoteportoptions
|
95
|
+
|
96
|
+
@remoteportoptions = $rwdcontrolports.rwd_options
|
97
|
+
@remotecommandoptions = ["query","stop"].rwd_options
|
98
|
+
end
|
99
|
+
|
100
|
+
|
@@ -0,0 +1,37 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
Helptext.update( :tinkerwin2_help => " You can view the list of applets you can install
|
4
|
+
by clicking on the list files button
|
5
|
+
|
6
|
+
You can download an applet created by someone else
|
7
|
+
|
8
|
+
put the file in the zips directory",
|
9
|
+
:applet_viewing => "You can get a list of all installed applets by clicking the 'listfiles button'
|
10
|
+
To see the text of a install file
|
11
|
+
Type in the name of the file you want.
|
12
|
+
|
13
|
+
type it without the inf ending
|
14
|
+
rwdapplet-0.1
|
15
|
+
|
16
|
+
Then click the 'View Installed File' button
|
17
|
+
|
18
|
+
If it finds the file it will open it. It displays the text the search box.
|
19
|
+
|
20
|
+
You can uninstall applets on this screen",
|
21
|
+
|
22
|
+
:applet_installation => " You can download an applet created by someone else\n
|
23
|
+
put the file in the zips directory\n
|
24
|
+
|
25
|
+
click on the 'list zips' tab\n
|
26
|
+
click on 'list applets available for installing'\n
|
27
|
+
|
28
|
+
chose the applet you want\n
|
29
|
+
click on 'install applet'\n
|
30
|
+
If all goes well the applet should be installed.\n
|
31
|
+
You will need to restart rwdtinker to see the new screens",
|
32
|
+
:applet_tinkerbackeditconfiguration => " You can edit the configuration
|
33
|
+
|
34
|
+
Remember to reload the configuration variables if you want your
|
35
|
+
changes used."
|
36
|
+
|
37
|
+
)
|
@@ -1,21 +1,21 @@
|
|
1
1
|
def installapplet
|
2
2
|
require 'fileutils'
|
3
|
+
require 'extras/zip/zip'
|
3
4
|
nametext = "%s" % [@a_installapplet]
|
4
5
|
|
5
6
|
begin # exception trapped block
|
6
7
|
fullname = nametext + ".zip"
|
7
8
|
fileName = File.join($zipslocation,fullname)
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
system(commandline)
|
9
|
+
|
10
|
+
zf = Zip::ZipFile.new(fileName)
|
11
|
+
zf.sort.each {
|
12
|
+
|entry|
|
13
|
+
zf.extract(entry.to_s, entry.to_s)
|
14
|
+
}
|
15
15
|
@installapplettext = Message[:applet_installed]
|
16
16
|
|
17
17
|
rescue
|
18
|
-
@installapplettext = "
|
18
|
+
@installapplettext = "unzip error - applet may not be installed correctly"
|
19
19
|
end # exception rescue
|
20
20
|
|
21
21
|
end
|