aboutsummaryrefslogtreecommitdiff
path: root/files/pl/learn/javascript/first_steps/what_went_wrong/index.html
blob: e4aa652ef7662229c497cd9551e5a8b0772c3c6d (plain)
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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
---
title: Co poszło nie tak? Rozwiązywanie problemów w JavaScript
slug: Learn/JavaScript/First_steps/What_went_wrong
translation_of: Learn/JavaScript/First_steps/What_went_wrong
original_slug: Learn/JavaScript/Pierwsze_kroki/Co_poszlo_nie_tak
---
<div>{{LearnSidebar}}</div>

<div>{{PreviousMenuNext("Learn/JavaScript/First_steps/A_first_splash", "Learn/JavaScript/First_steps/Variables", "Learn/JavaScript/First_steps")}}</div>

<p class="summary">Kiedy w poprzednim artykule budowałeś grę "Zgadnij numer", mogłeś stwierdzić, że ona po prostu nie działała. Nie martw się - ten artykuł ma na celu zatrzymanie cię przed wyrywaniem sobie włosów nad takimi problemami poprzez dostarczenie ci narzędzi do znajdowania i naprawienia błędów w programach napisanych w JavaScript.</p>

<table class="learn-box standard-table">
 <tbody>
  <tr>
   <th scope="row">Wymagania wstępne:</th>
   <td>Podstawowa znajomość obsługi komputera, podstawowe rozumenie HTML i CSS oraz wiedza, czym jest JavaScript.</td>
  </tr>
  <tr>
   <th scope="row">Cel:</th>
   <td>Zdobycie umiejętności i odwagi do rozwiązywania prostych problemów w twoim własnym kodzie.</td>
  </tr>
 </tbody>
</table>

<h2 id="Typy_błędów">Typy błędów</h2>

<p>Każdy błąd w kodzie można w ogólności podzielić na dwa typy:</p>

<ul>
 <li><strong>Błędy składniowe: </strong> Są to literówki w twoim kodzie, które albo sprawią, że program w ogóle się nie uruchomi, albo przestanie działać w pewnym momencie - zwykle zostaną podane także wiadomości o błędzie. Zwykle naprawa ich nie jest trudno, tak długo jak znasz odpowiednie narzędzia i wiesz, co znaczą poszczególne komunikaty błędów!</li>
 <li><strong>Błędy logiczne:</strong> Są to błedy, gdy składnia jest poprawna, ale kod zachowuje się inaczej niż chciałeś, aby to robił. Znaczy to, że program uruchamia się poprawnie, ale daje niepoprawne wyniki. Są to trudne błędy do naprawienia, gdyż często nie mają żadnej informacji, która by naprowadziła na źródło błędu.</li>
</ul>

<p>No dobra, nie jest to <em>tak</em> proste - istnieją także inne czynniki, które różnicują błędy. Powyższa klasyfikacja wystarczy jednak w tym początkowym etapie twojej kariery. W kolejnej częsci przyjrzymy się tym dwóm typom błędów.</p>

<h2 id="Błędogenny_przykład">Błędogenny przykład</h2>

<p>Zaczniemy od naszej poprzedniej gry "Zgadnij numer" - tylko że tym razem będziemy zajmować się wersją w której umyślnie ukryto trochę błędów. Odwiedź GitHub i wykonaj lokalną wersję  <a href="https://github.com/mdn/learning-area/blob/master/javascript/introduction-to-js-1/troubleshooting/number-game-errors.html">number-game-errors.html</a> (Zobacz live demo <a href="http://mdn.github.io/learning-area/javascript/introduction-to-js-1/troubleshooting/number-game-errors.html">tutaj</a>).</p>

<ol>
 <li>Aby zacząć, otwój lokalną wersję w swoim ulubionym edytorze tekstu i w twojej przeglądarce.</li>
 <li>Spróbuj zagrać w tę - odkryjesz że kiedy wciskasz przycisk "Zgadnij" - on po prostu nie działa!</li>
</ol>

<div class="note">
<p><strong>Notatka:</strong> Równie dobrze możesz mieć swoją wersję gry, która ci nie działa. Wiemy, że może chciałbyś ją naprawić, ale chcemy, abyś wykorzystał naszą wersję, dzięki czemu nauczysz się technik, których tu uczymy. Następnie możesz  wykorzystać je do naprawienia własnego kodu.</p>
</div>

<p>W tym miejscu spójrzmy na narzędzia developerskie, dokładniej konsolę debugowania, aby sprawdzić, czy występują błędy składniowe, a jeżeli tak - naprawić je. Poniżej znajdują się instrukcje, jak tego dokonać.</p>

<h2 id="Naprawa_błędów_składniowych">Naprawa błędów składniowych</h2>

<p>Wcześniej pokazaliśmy kilka prostych komend JavaScript, które wpisałeś do <a href="/en-US/docs/Learn/Common_questions/What_are_browser_developer_tools">konsoli JavaScript w narzędziach deweloperskich </a> (jeżeli nie pamiętasz jak je otworzyć, kliknij w link, aby sobie przypomnieć). Jej bardzo przydatną funkcją jest wyświetlanie błędów w momencie gdy interpreter JavaScript przeglądarki napotka na błąd. Wyruszmy na poszukiwanie błędów!</p>

<ol>
 <li>Idź do karty z otwartą stroną <code>number-game-errors.html</code> i otwórz konsolę JavaScript. Znajdziesz tam błąd:<img alt="" src="https://mdn.mozillademos.org/files/13496/not-a-function.png" style="display: block; margin: 0 auto;"></li>
 <li>Jest to prosty błąd do wytropienia, a przeglądarka daje wiele przydatnych  wskazówek: (powyższy screen jest z Firefoxa, ale inne przeglądarki pokazują podobne informacje). Od lewej do prawej znajdują się:
  <ul>
   <li>Czerwony "x" dla pokazania, że jest to błąd.</li>
   <li>Wiadomość błędu dla pokazania, co poszło nie tak: "TypeError: guessSubmit.addeventListener is not a function"</li>
   <li>Link do "Learn More", który przekierowuje do strony na MDN, która wyjaśnia szczegółowo dany błąd.</li>
   <li>Nazwa pliku JavaScript, która po kliknięciu kieruje do karty debuggera. Jeżeli klikniesz go, pokaże ci się dokładna linia z danym błędem.</li>
   <li>Numer linii oraz znak, gdzie wystąpił błąd. W tym przypadku linia 86, znak 3.</li>
  </ul>
 </li>
 <li>Spoglądając na linię 86, zobaczymy następujący kod:
  <pre class="brush: js">guessSubmit.addeventListener('click', checkGuess);</pre>
 </li>
 <li>The error message says "guessSubmit.addeventListener is not a function", so we've probably spelled something wrong. If you are not sure of the correct spelling of a piece of syntax, it is often good to look up the feature on MDN. The best way to do this currently is to search for "mdn <em>name-of-feature</em>" on your favourite search engine. Here's a shortcut to save you some time in this instance: <code><a href="/en-US/docs/Web/API/EventTarget/addEventListener">addEventListener()</a></code>.</li>
 <li>So, looking at this page, the error appears to be that we've spelled the function name wrong! Remember that JavaScript is case sensitive, so any slight different in spelling or casing will cause an error. Changing <code>addeventListener</code> to <code>addEventListener</code> should fix this. Do this now.</li>
</ol>

<div class="note">
<p><strong>Note</strong>: See our <a href="/en-US/docs/Web/JavaScript/Reference/Errors/Not_a_function">TypeError: "x" is not a function</a> reference page for more details about this error.</p>
</div>

<h3 id="Błędy_składniowe_-_podejście_drugie">Błędy składniowe - podejście drugie</h3>

<ol>
 <li>Zapisz stronę i odśwież - zobaczysz, że błąd zniknął.</li>
 <li>Spróbuj teraz wpisać liczbę. Po kliknięciu na przycisk "Wyślij" zobaczysz... inny błąd!<img alt="" src="https://mdn.mozillademos.org/files/13498/variable-is-null.png" style="display: block; margin: 0 auto;"></li>
 <li>Tym razem błąd to "TypeError: lowOrHi is null", on line 78.
  <div class="note"><strong>Notatka</strong>: <code><a href="/en-US/docs/Glossary/Null">Null</a></code> jest specjalną wartością, która oznacza "nic" bądź "brak wartości" A więc <code>lowOrHi</code> został zadeklarowany i zainicjalizowany - ale wartością pustą.</div>

  <div class="note"><strong>Notatka</strong>: Ten błąd pojawił się jak tylko strona została załadowana, dlatego że błąd wystąpił podczas wykonywania funkcji (w środku bloku <code>checkGuess() { ... }</code>). Jak dowiesz się później bardziej szczegółowo, kod wewnątrz funkcji jest wykonywany w innej przestrzeni niż kod poza funkcją. W tym przypadku kod nie został uruchomiony, a błąd wyrzucony do momentu, aż funkcja <code>checkGuess()</code> nie dotarła do linijki 86.</div>
 </li>
 <li>Popatrz na linię 78. Zobaczysz tam następujący kod:
  <pre class="brush: js">lowOrHi.textContent = 'Last guess was too high!';</pre>
 </li>
 <li>W tej linii następuje próba ustawienia właściwości <code>textContent</code> zmiennej <code>lowOrHi</code> na tekst, ale to się nie powiodło ze względu na fakt, że <code>lowOrHi</code> nie jest tym, czego oczekujemy.  Trzeba się dowiedzieć, dlaczego tak jest - wyszukajmy inne wystąpienia <code>lowOrHi</code>. Najwcześniejsze wystąpienie znajdziemy w linii 48:
  <pre class="brush: js">var lowOrHi = document.querySelector('lowOrHi');</pre>
 </li>
 <li>W tej linii próbujemy przypisać zmiennej referencję do elementu w dokumencie HTML. Sprawdźmy, czy wartością tej zmiennej jest null po wykonaniu tej linijki. Aby to zrobić, dodaj ten kod w linii 49:
  <pre class="brush: js">console.log(lowOrHi);</pre>

  <div class="note">
  <p><strong>Notatka</strong>: <code><a href="/en-US/docs/Web/API/Console/log">console.log()</a></code> jest bardzo użyteczną funkcją do debugowania, której celem jest wypisanie wartości zmiennej do konsoli. W tym przypadku wypisze ona wartość<code>lowOrHi</code> do konsoli w takiej postaci, w jakiej została ona ustawiona w linii 48.</p>
  </div>
 </li>
 <li>Zapisz plik i odśwież stronę. Po przejściu do konsoli zobaczysz efekt wywołania <code>console.log()</code>.<img alt="" src="https://mdn.mozillademos.org/files/13494/console-log-output.png" style="display: block; margin: 0 auto;">Możemy być pewni - w tym momencie wartością <code>lowOrHi</code> jest <code>null</code>. Oznacza to, że błąd jest zdecydowanie związany z linią 48.</li>
 <li>Pomyślmy - co może być tutaj problemem? W linii 48 używamy <code><a href="/en-US/docs/Web/API/Document/querySelector">document.querySelector()</a></code> aby otrzymać referencję do elementu. Odbywa się to poprzez podanie selektora CSS jako parametr funkcji. W dalszej częsci pliku można znaleźć paragraf, którego referencji potrzebujemy:
  <pre class="brush: js">&lt;p class="lowOrHi"&gt;&lt;/p&gt;</pre>
 </li>
 <li>To oznacza, że potrzebujemy tu selektora klasy. Zaczyna się on kropką (.) - ale selektor, którego używamy w <code>querySelector()</code> (linia 48) nie ma kropki. To może być nasz błąd! Spróbuj zmienić <code>lowOrHi</code> na <code>.lowOrHi</code> w linii 48.</li>
 <li>Zapisz i odśwież stronę. Teraz <code>console.log()</code> powinien wyświetlić element  <code>&lt;p&gt;</code>, którego poszukiwaliśmy. Uff! Kolejny błąd naprawiony! Możesz już usunąć linię z <code>console.log()</code> (albo zostawić ją odniesienie na później - jak uważasz).</li>
</ol>

<div class="note">
<p><strong>Note</strong>: See our <a href="/en-US/docs/Web/JavaScript/Reference/Errors/Unexpected_type">TypeError: "x" is (not) "y"</a> reference page for more details about this error.</p>
</div>

<h3 id="Błędy_składniowe_-_podejście_trzecie">Błędy składniowe - podejście trzecie</h3>

<ol>
 <li>Teraz gdy spróbujesz zagrać w grę, powinno Ci się więcej powieść - gra będzie grywalna, do momentu końca gry - nieważne, czy poprzez znalezienie właściwej liczby czy skończenie się żyć.</li>
 <li>W tym miejscu gra nie działa, a w konsoli pojawie się ten sam błąd, co na początku - "TypeError: resetButton.addeventListener is not a function"! Tym razem jednak jest wywoływany z linii 94.</li>
 <li>Spójrzmy na linię 94. Można łatwo zauważyć, że mamy do czynienia z tym samym błędem, co wcześniej - wystarczy zmienić <code>addeventListener</code> na <code>.addEventListener</code>. Zrób to teraz.</li>
</ol>

<h2 id="Błąd_logiczny">Błąd logiczny</h2>

<p>Na tym etapie gra powinna być grywalna, ale po kilku uruchomieniach można łatwo zauważyć, że "losowa" liczba to zawsze 0 bądź 1. Nie jest to to, czego można od takiej gry oczekiwać!</p>

<p>Musi to być jakiś problem z logiką aplikacji - gra nie zwraca błędu, jednak nie zachowuje się jak powinna.</p>

<ol>
 <li>Znajdźmy zmienną <code>randomNumber</code>  i linie, w których zmienna jest zadeklarowana i jej wartość ustalona. To miejsce znajduje się w okolicach linii 44:

  <pre class="brush: js">var randomNumber = Math.floor(Math.random()) + 1;</pre>
  A linia, która generuje losową liczbę przed każdą grą, to linia 113:

  <pre class="brush: js">randomNumber = Math.floor(Math.random()) + 1;</pre>
 </li>
 <li>Aby sprawdzić czy to z tymi liniami jest problem, użyjmy naszego starego przyjaciela - polecenia <code>console.log()</code>. Wstaw następujący kod bezpośrednio pod wcześniej wymienionymi dwiema liniami kodu:
  <pre class="brush: js">console.log(randomNumber);</pre>
 </li>
 <li>Zapisz i odśwież, następnie zagraj kilka razy - można zauważyć, że za każdym wywołaniem  <code>randomNumber</code> jest równe 1.</li>
</ol>

<h3 id="Praca_nad_logiką">Praca nad logiką</h3>

<p>W celu naprawy tego błędu, należy najpierw pomyśleć, jak działa ten kod. Na samym początku wywołujemy <code><a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random">Math.random()</a></code>, który generuje zmiennoprzecinkową liczbę pomiędzy 0 i 1, na przykład 0.5675493843.</p>

<pre class="brush: js">Math.random()</pre>

<p>Następnie uzyskaną liczbę podajemy jako parametr funkcji <code><a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/floor">Math.floor()</a></code>, której zadanie jest zaokrąglenie uzyskanej w parametrze liczby do największej liczby całkowitej równej bądź mniejszej od parametru. Następnie dodajemy 1 do wyniku:</p>

<pre>Math.floor(Math.random()) + 1</pre>

<p>Zaokrąglanie liczby zmiennoprzecinkowej w zakresie od 0 do 1 zawsze da 0 . Dodanie do niej 1 da więc wynik 1. Aby naprawić wynik zgodnie z wymaganiami, pomnóżmy naszą losową liczbę przez 100. Sprawi to, że dostaniemy losową liczbę od 0 do 99:</p>

<pre class="brush: js">Math.floor(Math.random()*100);</pre>

<p>Jeżeli dodamy 1, dostaniemy liczbę z przedziału od 1 do 100:</p>

<pre class="brush: js">Math.floor(Math.random()*100) + 1;</pre>

<p>Zmień obie linie zgodnie z tym wzorem, zapisz i odśwież stronę - gra powinna zachowywać się tak jak od niej tego oczekujemy!</p>

<h2 id="Inne_popularne_błędy">Inne popularne błędy</h2>

<p>Istnieją inne popularne błędy, na które natkniesz się w swoim kodzie. Ta sekcja zawiera listę najpopularniejszych z nich.</p>

<h3 id="SyntaxError_missing_before_statement">SyntaxError: missing ; before statement</h3>

<p>Ten błąd oznacza, że zapomniałeś o średniku na końcu linii. Czasem może jednak być bardziej enigmatyczny. Przykładem może być zmiana tej linii:</p>

<pre class="brush: js">var userGuess = Number(guessField.value);</pre>

<p>na</p>

<pre class="brush: js">var userGuess === Number(guessField.value);</pre>

<p>Ten kod wyrzuca błąd, gdyż myśli, że chcesz zrobić coś innego. Musisz być pewny, że nie mieszasz znaku przypisania (<code>=</code>) — zapisuje on wartość w zmiennej, z operatorem dokładnego porównania, który testuje czy jedna wartość jest dokładnie równa drugiej - zwraca ona wynik w postaci zmiennej logicznej <code>true</code>/<code>false</code>.</p>

<div class="note">
<p><strong>Notatka</strong>: Aby dowiedzieć się więcej o tym błędzie, odwiedź naszą stronę <a href="/en-US/docs/Web/JavaScript/Reference/Errors/Missing_semicolon_before_statement">SyntaxError: missing ; before statement</a>.</p>
</div>

<h3 id="Program_zawsze_twierdzi_że_wygrałeś_niezależnie_od_wprowadzonej_liczby">Program zawsze twierdzi, że wygrałeś, niezależnie od wprowadzonej liczby</h3>

<p>Może to być objawem pomieszania operatorów przypisania i dokładnego porównania. Przykładowo jeżeli byśmy zmienili tę linię w funkcji <code>checkGuess()</code>:</p>

<pre class="brush: js">if (userGuess === randomNumber) {</pre>

<p>na</p>

<pre class="brush: js">if (userGuess = randomNumber) {</pre>

<p>ten test zawsze zwróciłby <code>true</code> (prawdę), co sprawiłoby, że program za każdym razem twierdziłby, że gra została przez Ciebie wygrana. Uważaj na błędy!</p>

<h3 id="SyntaxError_missing_)_after_argument_list">SyntaxError: missing ) after argument list</h3>

<p>Ten błąd jest prosty — oznacza po prostu, że zapomniałeś dodać nawias zamykający na końcu funkcji/wywołania metody.</p>

<div class="note">
<p><strong>Notatka</strong>: Zobacz naszą  stronę referencyjną: <a href="/en-US/docs/Web/JavaScript/Reference/Errors/Missing_parenthesis_after_argument_list">SyntaxError: missing ) after argument list</a>, aby dowiedzieć się więcej o tym błędzie.</p>
</div>

<h3 id="SyntaxError_missing_after_property_id">SyntaxError: missing : after property id</h3>

<p>Ten błąd zwykle jest związany z niepoprawnie napisanym obiektem JavaScript. Tym razem jednak został spowodowany zmianą</p>

<pre class="brush: js">function checkGuess() {</pre>

<p>na</p>

<pre class="brush: js">function checkGuess( {</pre>

<p>Ten błąd spowodował, że przeglądarka zinterpretowała ten kod jako próbę podania wnętrza funkcji jako parametr funkcji. Uważaj na nawiasy!</p>

<h3 id="SyntaxError_missing_after_function_body">SyntaxError: missing } after function body</h3>

<p>This is easy — it generally means that you've missed one of your curly braces from a function or conditional structure. We got this error by deleting one of the closing curly braces near the bottom of the <code>checkGuess()</code> function.</p>

<h3 id="SyntaxError_expected_expression_got_'string'_or_SyntaxError_unterminated_string_literal">SyntaxError: expected expression, got '<em>string</em>' or SyntaxError: unterminated string literal</h3>

<p>These errors generally mean that you've missed off a string value's opening or closing quote mark. In the first error above, <em>string</em> would be replaced with the unexpected character(s) that the browser found instead of a quote mark at the start of a string. The second error means that the string has not been ended with a quote mark.</p>

<p>For all of these errors, think about how we tackled the examples we looked at in the walkthrough. When an error arises, look at the line number you are given, go to that line and see if you can spot what's wrong. Bear in mind that the error is not necessarily going to be on that line, and also that the error might not be caused by the exact same problem we cited above!</p>

<div class="note">
<p><strong>Note</strong>: See our <a href="/en-US/docs/Web/JavaScript/Reference/Errors/Unexpected_token">SyntaxError: Unexpected token</a> and <a href="/en-US/docs/Web/JavaScript/Reference/Errors/Unterminated_string_literal">SyntaxError: unterminated string literal</a> reference pages for more details about these errors.</p>
</div>

<h2 id="Podsumowanie">Podsumowanie</h2>

<p>A wieć to jest to - podstawy szukania błędów w prostych programach w JS. Nie zawsze znalezienie błędu jest tak proste , ale przynajmniej ten artykuł może ci pomóc w zaoszczędzeniu kilku godzin snu i pozwolić na szybsze postępy w nauce.</p>

<h2 id="Zobacz_także">Zobacz także</h2>

<div>
<ul>
 <li>Istnieje wiele innych typów błędów, które nie zostały tu przedstawione - tworzymy artykuł o tym, co one dokładnie oznaczają -zobacz <a href="/en-US/docs/Web/JavaScript/Reference/Errors">JavaScript error reference</a>.</li>
 <li>Jeżeli napotkasz błędy w swoim kodzie, co do których nie jesteś pewny, jak je rozwiązać - nawet po lekturze tego artykułu - możesz uzyskać pomoc. Zapytaj na  <a class="external external-icon" href="https://discourse.mozilla-community.org/t/learning-web-development-marking-guides-and-questions/16294">Learning Area Discourse thread</a>, lub na kanale IRC  <a href="irc://irc.mozilla.org/mdn">#mdn</a> na <a class="external external-icon" href="https://wiki.mozilla.org/IRC">Mozilla IRC</a>. Opisz swój błąd, a my spróbujemy Ci pomóc. Przedstawienie Twojego kodu będzie równie pomocne w owocnym rozwiązaniu problemu.</li>
</ul>
</div>

<p>{{PreviousMenuNext("Learn/JavaScript/First_steps/A_first_splash", "Learn/JavaScript/First_steps/Variables", "Learn/JavaScript/First_steps")}}</p>

<p> </p>

<h2 id="In_this_module">In this module</h2>

<ul>
 <li><a href="/en-US/docs/Learn/JavaScript/First_steps/What_is_JavaScript">What is JavaScript?</a></li>
 <li><a href="/en-US/docs/Learn/JavaScript/First_steps/A_first_splash">A first splash into JavaScript</a></li>
 <li><a href="/en-US/docs/Learn/JavaScript/First_steps/What_went_wrong">What went wrong? Troubleshooting JavaScript</a></li>
 <li><a href="/en-US/docs/Learn/JavaScript/First_steps/Variables">Storing the information you need — Variables</a></li>
 <li><a href="/en-US/docs/Learn/JavaScript/First_steps/Math">Basic math in JavaScript — numbers and operators</a></li>
 <li><a href="/en-US/docs/Learn/JavaScript/First_steps/Strings">Handling text — strings in JavaScript</a></li>
 <li><a href="/en-US/docs/Learn/JavaScript/First_steps/Useful_string_methods">Useful string methods</a></li>
 <li><a href="/en-US/docs/Learn/JavaScript/First_steps/Arrays">Arrays</a></li>
 <li><a href="/en-US/docs/Learn/JavaScript/First_steps/Silly_story_generator">Assessment: Silly story generator</a></li>
</ul>

<p> </p>