├── .gitignore
├── .idea
├── .gitignore
├── artifacts
│ └── CurrencyConverterApp.xml
├── compiler.xml
├── description.html
├── encodings.xml
├── gradle.xml
├── libraries
│ ├── json_20201115.xml
│ └── lib.xml
├── misc.xml
├── modules.xml
├── uiDesigner.xml
└── vcs.xml
├── CurrencyConverterApp.iml
├── README.md
├── screens
├── AcnhorPane.PNG
├── Artifacts.PNG
├── GridPane.png
├── Properties.PNG
├── empty_text.PNG
├── empty_window.PNG
├── example.PNG
├── exe1.PNG
├── exe2.PNG
├── final_layout.PNG
├── fx_id.PNG
├── java8.PNG
├── skeleton.PNG
└── sout_itworks.PNG
└── src
├── META-INF
└── MANIFEST.MF
└── sample
├── Controller.java
├── Main.java
└── sample.fxml
/.gitignore:
--------------------------------------------------------------------------------
1 | /out
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 |
--------------------------------------------------------------------------------
/.idea/artifacts/CurrencyConverterApp.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | $PROJECT_DIR$/out/artifacts/CurrencyConverterApp
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/.idea/description.html:
--------------------------------------------------------------------------------
1 | Simple JavaFX 2.0 application that includes simple .fxml file with attached controller and Main class to quick start. Artifact to build JavaFX application is provided.
2 |
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/libraries/json_20201115.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/libraries/lib.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/uiDesigner.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | -
6 |
7 |
8 | -
9 |
10 |
11 | -
12 |
13 |
14 | -
15 |
16 |
17 | -
18 |
19 |
20 |
21 |
22 |
23 | -
24 |
25 |
26 |
27 |
28 |
29 | -
30 |
31 |
32 |
33 |
34 |
35 | -
36 |
37 |
38 |
39 |
40 |
41 | -
42 |
43 |
44 |
45 |
46 | -
47 |
48 |
49 |
50 |
51 | -
52 |
53 |
54 |
55 |
56 | -
57 |
58 |
59 |
60 |
61 | -
62 |
63 |
64 |
65 |
66 | -
67 |
68 |
69 |
70 |
71 | -
72 |
73 |
74 | -
75 |
76 |
77 |
78 |
79 | -
80 |
81 |
82 |
83 |
84 | -
85 |
86 |
87 |
88 |
89 | -
90 |
91 |
92 |
93 |
94 | -
95 |
96 |
97 |
98 |
99 | -
100 |
101 |
102 | -
103 |
104 |
105 | -
106 |
107 |
108 | -
109 |
110 |
111 | -
112 |
113 |
114 |
115 |
116 | -
117 |
118 |
119 | -
120 |
121 |
122 |
123 |
124 |
125 |
126 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/CurrencyConverterApp.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Building desktop app (.exe) with JavaFX
2 | ## Introduction
3 | In this guide, you will learn how to build an executable **JavaFX** desktop application and later can run it without IDE by just clicking *.exe* file.
4 | The problem with JavaFX is that Oracle is ruining this awesome tool by making it harder to execute on JDK 11, but referencing this [link](https://stackoverflow.com/questions/61555515/intellij-idea-javafx-export-cant-build-artifact-fxdeploy-is-not-available) I have just chosen to download JDK 8 since it is building JavaFX without any issues.
5 | ## Install JDK 8
6 | To do that go [there](https://docs.aws.amazon.com/corretto/latest/corretto-8-ug/downloads-list.html) and download a particular version for your OS
7 | ## Install SceneBuilder for building GUI for our app
8 | Go to the [site](https://gluonhq.com/products/scene-builder/) and download the app.
9 | ## Download JavaFX
10 | Go to the [site](https://gluonhq.com/products/javafx/) and download JavaFX Windows SDK.
11 | Then you unzip in your chosen directory. You will source this for each JavaFX project. (Unfortunately each time the same steps).
12 |
13 | ## Run your first JavaFX app via IntelliJ
14 | 1. Click **[+ New Project]** -> **[Java FX]** -> **[Project SDK]** -> **[Corretto-11 java version “11.0.8”]** -> **[Next]** -> **[Name and propject location]** -> **[Finish]**
15 | 2. Go to **[File]** -> **[Project Structure…]** *or Ctr+Alt+Shift+S* -> **[Libraries]** -> **[New Project Library (+) sign]** -> **[Java]** -> **[Choose the directory of unzipped SDK folder lib]** -> **[Press okay]**
16 | 3. Go to **[Run]** -> In Application/Main in *Configuration* tab under field **[VM Options:]** you have to place the following fields:
17 | ```shell
18 | --module-path \javafx-sdk-11.0.2\lib --add-modules javafx.controls,javafx.fxml
19 | ```
20 | Instead of *\javafx-sdk-11.0.2\lib* you need to set your own directory of JavaFX lib folder.
21 |
22 | 4. Run (shift + f10) Main.java and you will see this window if you do all these steps correctly.
23 | 
24 |
25 | ## Build a JavaFX app with SceneBuilder
26 | For the sake of simplicity, I will build a simple currency converter app which will use API.
27 | 
28 | 1. Open SceneBuilder
29 | 2. Choose **Open Project**
30 | 3. Find in a directory of the app (in src folder) **.fxml** file and open it
31 | 4. For the moment we will drop default GridPane (0x0)
32 | 
33 | 5. Drag AnchorPane and move to the main screen. This is the main window of our application.
34 | 
35 | 6. We can adjust the size of the window as well as colour and style if you are familiar with *CSS*
36 | 7. We can use the search window to quickly find an object, for example, “Text”. Drag to the main screen and adjust the size of Text area and styling.
37 | 
38 | 8. Then keep filling like you wish the App. And at the end, the layout should look similar to this:
39 | 
40 |
41 | You now can preview the app, go to **[Preview]** -> **[Show Preview in Window]** or simply *Ctrl+P*
42 | Now steps to use this template in our app as well as set-up IDs for further uses.
43 | 1. To further use an object in code and provide the reactivity for that we need to set **fx:id**. Select the object and on the right side of the SceneBuilder click tab **[Code]**. You will see the **fx:id** fill the id of the object. Place IDs for other objects.
44 | 
45 | 2. I built three empty text objects and assign **fx:id** for them to display the information about currency rate; conversion and date when button “Convert” is clicked.
46 | 
47 | 3. In button left side click **[Controller]** and in field **[Contoller class]** and provide the root to Controller.java. For example **[sample.Controller]**
48 | 
49 | 4. After previous steps, we save the changes (Ctrl + S) it will replace **.fxml** layout to our built layout in SceneBuilder.
50 | 5. Finally, we need to click **[View]** -> **[Show Sample Controller Skeleton]** -> click in right button **[Full]** then Copy and paste in the app directory *Controller.java*
51 | 
52 | I got something like this:
53 | ```Java
54 | package sample;
55 |
56 | import java.net.URL;
57 | import java.util.ResourceBundle;
58 | import javafx.fxml.FXML;
59 | import javafx.scene.control.Button;
60 | import javafx.scene.control.TextField;
61 | import javafx.scene.text.Text;
62 |
63 | public class Controller {
64 |
65 | @FXML
66 | private ResourceBundle resources;
67 |
68 | @FXML
69 | private URL location;
70 |
71 | @FXML
72 | private TextField amountField;
73 |
74 | @FXML
75 | private TextField ccyOne;
76 |
77 | @FXML
78 | private TextField ccyTwo;
79 |
80 | @FXML
81 | private Button applyBtn;
82 |
83 | @FXML
84 | private Text rateCcy;
85 |
86 | @FXML
87 | private Text conversionTotal;
88 |
89 | @FXML
90 | private Text dateStamp;
91 |
92 | @FXML
93 | void initialize() {
94 | assert amountField != null : "fx:id=\"amountField\" was not injected: check your FXML file 'sample.fxml'.";
95 | assert ccyOne != null : "fx:id=\"ccyOne\" was not injected: check your FXML file 'sample.fxml'.";
96 | assert ccyTwo != null : "fx:id=\"ccyTwo\" was not injected: check your FXML file 'sample.fxml'.";
97 | assert applyBtn != null : "fx:id=\"applyBtn\" was not injected: check your FXML file 'sample.fxml'.";
98 | assert rateCcy != null : "fx:id=\"rateCcy\" was not injected: check your FXML file 'sample.fxml'.";
99 | assert conversionTotal != null : "fx:id=\"conversionTotal\" was not injected: check your FXML file 'sample.fxml'.";
100 | assert dateStamp != null : "fx:id=\"dateStamp\" was not injected: check your FXML file 'sample.fxml'.";
101 |
102 | }
103 | }
104 |
105 | ```
106 | ## Coding application
107 | Firstly, let's quickly fix **Main.java**:
108 | 1. Set title: ``primaryStage.setTitle("Currency Converter");``
109 | 2. Place size from SceneBuilder in object **Scene**:
110 | `` primaryStage.setScene(new Scene(root, 384, 283));``
111 | 3. Fix the window to not be scaled:
112 | ``primaryStage.setResizable(false);``
113 |
114 | Now we can go to Contoller.java and do all work there.
115 | There you could see the ``initialize()``. From this method we can drop warnings and rewrite the code to check is our template is functional or not. Let’s just add **sout** command to get output in the console by clicking the button:
116 |
117 | ```Java
118 | @FXML
119 | void initialize() {
120 | applyBtn.setOnAction(event -> {
121 | System.out.println("IT WORKS!");
122 | });
123 | }
124 | ```
125 |
126 | 
127 |
128 | You might notice the red warning. This is because of compitability issues of JavaFX. Just go to **.fxml** file in **AnchorPane** and replace ``xmlns="http://javafx.com/javafx/15.0.1"`` to which version it asks (``xmlns="http://javafx.com/javafx/11.0.2"``)
129 |
130 | ## Install jar for JSON Parsing
131 | Next let's install JSON jar from [here](https://mvnrepository.com/artifact/org.json/json/20201115) or [here](https://repo1.maven.org/maven2/org/json/json/20201115/json-20201115.jar). Store it in any directory and go to **[File]** -> **[Project Structure…]** *or Ctr+Alt+Shift+S* -> **[Libraries]** -> **[New Project Library (+) sign]** -> **[Java]** -> **[Choose jar file]** -> **[Press okay]**
132 |
133 | ## Working with API
134 | you actually can do any other API if you like. I choose first [link](https://currencylayer.com/) in google. I won't explain how to set-up - it is easy. But once you logged in go to how to start and find the api link with your assigned token like i did http://data.fixer.io/api/latest?access_key=27ad197ccf98251c14ebc0b13134de39&symbols=USD
135 | Even if you click this link it will open JSON object in your browser, so we can now use it in our application.
136 |
137 | Before we need to build a class **getUrlContent** which will buffer data from the GET.
138 |
139 | ```Java
140 | private static String getUrlContent(String urlAddress) {
141 | StringBuffer content = new StringBuffer();
142 | try {
143 | URL url = new URL(urlAddress);
144 | URLConnection urlConn = url.openConnection();
145 |
146 | BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(urlConn.getInputStream()));
147 | String line;
148 | while ((line = bufferedReader.readLine()) != null) {
149 | content.append(line + "\n");
150 | }
151 | bufferedReader.close();
152 | } catch (Exception e) {
153 | System.out.println("Error in conversion");
154 | }
155 | return content.toString();
156 | }
157 | ```
158 |
159 | Now in the method **initialize()** we can build up our parser and output insted of sout function:
160 |
161 | ```Java
162 | @FXML
163 | void initialize() {
164 | applyBtn.setOnAction(event -> {
165 | String output = getUrlContent("http://data.fixer.io/api/latest?access_key=" +
166 | "27ad197ccf98251c14ebc0b13134de39&symbols="+ccyOne.getText().toUpperCase()+","+ccyTwo.getText().toUpperCase());
167 | if(!output.isEmpty()) {
168 | JSONObject obj = new JSONObject(output);
169 | dateStamp.setText(obj.getString("date"));
170 | double ccyTwoValue = obj.getJSONObject("rates").getDouble(ccyTwo.getText()) /
171 | obj.getJSONObject("rates").getDouble(ccyOne.getText());
172 | String ccyTwoStr = String.format("%.2f", ccyTwoValue) + " " + ccyTwo.getText();
173 |
174 | rateCcy.setText(ccyTwoStr);
175 |
176 | double amount = Double.parseDouble(amountField.getText());
177 | double total = ccyTwoValue * amount;
178 | String totalStr = String.format("%.2f", total) + " " + ccyTwo.getText();
179 | conversionTotal.setText(totalStr);
180 | }
181 | });
182 | }
183 | ```
184 |
185 | And finally if it works then you can convert any currency and amount you would like.
186 |
187 | ### !!! Be awere to use indexes for currncy like EUR - for euro or USD - for US Dollar.
188 |
189 | ## Build a self contained .exe file
190 | If everything works then we can finally build our application as .exe file to use it without IDE.
191 | 1. Go to **[File]** -> **[Project Structure…]** *or Ctr+Alt+Shift+S* -> **[Project]** -> set Project as 1.8 and project language level as 8 
192 | 2. Go to **[Artifacts]** -> **+ Add** or Alt+Insert -> **JavaFX Application** -> **From module..**.
193 | 3. In output tab move JSON jar and lib from JavaFX to the left into .jar of your project 
194 | 4. Go to tab **Java FX** in **Application class:** set your main class or click folder icon and choose, for example *sample.Main*, in **Native bundle:** choose all option and press ok.
195 | 5. Go to **[Build]** -> **[Build Artifacts..]**
196 |
197 | If there is no errors it should create in **out.artifacts** folder bundle. Go there and you will the last folder where is .exe file. You can move this folder out of directory and it can works without IDE.
198 | 
199 |
200 | 
201 |
--------------------------------------------------------------------------------
/screens/AcnhorPane.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ifarkhshatov/JavaFX-Tutorial/2eacb04075b5c8682f87968386045c334b984eaf/screens/AcnhorPane.PNG
--------------------------------------------------------------------------------
/screens/Artifacts.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ifarkhshatov/JavaFX-Tutorial/2eacb04075b5c8682f87968386045c334b984eaf/screens/Artifacts.PNG
--------------------------------------------------------------------------------
/screens/GridPane.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ifarkhshatov/JavaFX-Tutorial/2eacb04075b5c8682f87968386045c334b984eaf/screens/GridPane.png
--------------------------------------------------------------------------------
/screens/Properties.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ifarkhshatov/JavaFX-Tutorial/2eacb04075b5c8682f87968386045c334b984eaf/screens/Properties.PNG
--------------------------------------------------------------------------------
/screens/empty_text.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ifarkhshatov/JavaFX-Tutorial/2eacb04075b5c8682f87968386045c334b984eaf/screens/empty_text.PNG
--------------------------------------------------------------------------------
/screens/empty_window.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ifarkhshatov/JavaFX-Tutorial/2eacb04075b5c8682f87968386045c334b984eaf/screens/empty_window.PNG
--------------------------------------------------------------------------------
/screens/example.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ifarkhshatov/JavaFX-Tutorial/2eacb04075b5c8682f87968386045c334b984eaf/screens/example.PNG
--------------------------------------------------------------------------------
/screens/exe1.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ifarkhshatov/JavaFX-Tutorial/2eacb04075b5c8682f87968386045c334b984eaf/screens/exe1.PNG
--------------------------------------------------------------------------------
/screens/exe2.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ifarkhshatov/JavaFX-Tutorial/2eacb04075b5c8682f87968386045c334b984eaf/screens/exe2.PNG
--------------------------------------------------------------------------------
/screens/final_layout.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ifarkhshatov/JavaFX-Tutorial/2eacb04075b5c8682f87968386045c334b984eaf/screens/final_layout.PNG
--------------------------------------------------------------------------------
/screens/fx_id.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ifarkhshatov/JavaFX-Tutorial/2eacb04075b5c8682f87968386045c334b984eaf/screens/fx_id.PNG
--------------------------------------------------------------------------------
/screens/java8.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ifarkhshatov/JavaFX-Tutorial/2eacb04075b5c8682f87968386045c334b984eaf/screens/java8.PNG
--------------------------------------------------------------------------------
/screens/skeleton.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ifarkhshatov/JavaFX-Tutorial/2eacb04075b5c8682f87968386045c334b984eaf/screens/skeleton.PNG
--------------------------------------------------------------------------------
/screens/sout_itworks.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ifarkhshatov/JavaFX-Tutorial/2eacb04075b5c8682f87968386045c334b984eaf/screens/sout_itworks.PNG
--------------------------------------------------------------------------------
/src/META-INF/MANIFEST.MF:
--------------------------------------------------------------------------------
1 | Manifest-Version: 1.0
2 | Main-Class: sample.Main
3 |
4 |
--------------------------------------------------------------------------------
/src/sample/Controller.java:
--------------------------------------------------------------------------------
1 |
2 | package sample;
3 |
4 | import java.io.BufferedReader;
5 | import java.io.InputStreamReader;
6 | import java.net.URL;
7 | import java.net.URLConnection;
8 | import java.util.ResourceBundle;
9 | import javafx.fxml.FXML;
10 | import javafx.scene.control.Button;
11 | import javafx.scene.control.TextField;
12 | import javafx.scene.text.Text;
13 | import org.json.JSONObject;
14 |
15 | public class Controller {
16 |
17 | @FXML // ResourceBundle that was given to the FXMLLoader
18 | private ResourceBundle resources;
19 |
20 | @FXML // URL location of the FXML file that was given to the FXMLLoader
21 | private URL location;
22 |
23 | @FXML // fx:id="amountField"
24 | private TextField amountField; // Value injected by FXMLLoader
25 |
26 | @FXML // fx:id="ccyOne"
27 | private TextField ccyOne; // Value injected by FXMLLoader
28 |
29 | @FXML // fx:id="ccyTwo"
30 | private TextField ccyTwo; // Value injected by FXMLLoader
31 |
32 | @FXML // fx:id="applyBtn"
33 | private Button applyBtn; // Value injected by FXMLLoader
34 |
35 | @FXML // fx:id="rateCcy"
36 | private Text rateCcy; // Value injected by FXMLLoader
37 |
38 | @FXML // fx:id="conversionTotal"
39 | private Text conversionTotal; // Value injected by FXMLLoader
40 |
41 | @FXML // fx:id="dateStamp"
42 | private Text dateStamp; // Value injected by FXMLLoader
43 |
44 | @FXML // This method is called by the FXMLLoader when initialization is complete
45 | void initialize() {
46 | applyBtn.setOnAction(event -> {
47 | String output = getUrlContent("http://data.fixer.io/api/latest?access_key=" +
48 | "27ad197ccf98251c14ebc0b13134de39&symbols="+ccyOne.getText().toUpperCase()+","+ccyTwo.getText().toUpperCase());
49 | if(!output.isEmpty()) {
50 | JSONObject obj = new JSONObject(output);
51 | dateStamp.setText(obj.getString("date"));
52 | double ccyTwoValue = obj.getJSONObject("rates").getDouble(ccyTwo.getText()) /
53 | obj.getJSONObject("rates").getDouble(ccyOne.getText());
54 | String ccyTwoStr = String.format("%.2f", ccyTwoValue) + " " + ccyTwo.getText();
55 |
56 | rateCcy.setText(ccyTwoStr);
57 |
58 | double amount = Double.parseDouble(amountField.getText());
59 | double total = ccyTwoValue * amount;
60 | String totalStr = String.format("%.2f", total) + " " + ccyTwo.getText();
61 | conversionTotal.setText(totalStr);
62 | }
63 | });
64 | }
65 | private static String getUrlContent(String urlAddress) {
66 | StringBuffer content = new StringBuffer();
67 | try {
68 | URL url = new URL(urlAddress);
69 | URLConnection urlConn = url.openConnection();
70 |
71 | BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(urlConn.getInputStream()));
72 | String line;
73 | while ((line = bufferedReader.readLine()) != null) {
74 | content.append(line + "\n");
75 | }
76 | bufferedReader.close();
77 | } catch (Exception e) {
78 | System.out.println("Error in conversion");
79 | }
80 | return content.toString();
81 | }
82 |
83 | }
84 |
--------------------------------------------------------------------------------
/src/sample/Main.java:
--------------------------------------------------------------------------------
1 | package sample;
2 |
3 | import javafx.application.Application;
4 | import javafx.fxml.FXMLLoader;
5 | import javafx.scene.Parent;
6 | import javafx.scene.Scene;
7 | import javafx.stage.Stage;
8 |
9 | public class Main extends Application {
10 |
11 | @Override
12 | public void start(Stage primaryStage) throws Exception{
13 | Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
14 | primaryStage.setTitle("Currency Converter");
15 | primaryStage.setScene(new Scene(root, 363, 263));
16 | primaryStage.setResizable(false);
17 | primaryStage.show();
18 | }
19 |
20 |
21 | public static void main(String[] args) {
22 | launch(args);
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/sample/sample.fxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
--------------------------------------------------------------------------------