////////////////////////////////////////////////////////////////////////////
//
//
//
// ustawmy kurs na czółno przędz
//
// Dyskusja w glosach o programowaniu kreatywnym
// Nick Montfort i Stephanie Strickland
//
// Tłum. Monika Górska Olesińska, Mariusz Pisarski
// Translacja kodu: Jan Argasiński, Nick Montfort, Mariusz Pisarski
//
// Niniejsza refleksja (jej nowsza wersja) dotyczy dzieła opublikowanego na łamach
// Dear Navigator zimą 2010 roku, a mianowicie utworu Sea and Spar Between Nicka
// Montforta i Stephanie Strickland (Między Reją a Morzem). Jest to generator poezji
// rozrysowujący przestrzeń językową wypełnioną przez strofy, których liczba dorównuje
// ilości ryb zamieszkujących oceany (około 225 trylionów).
//
// Do odczytania Między Reją a Morzem potrzebny jest niniejszy plik (sea_spar_PL.js)
// oraz pliki następujące:
//
// index.html Główna strona internetowa, pełniąca funkcję interfejsu dla generatora
// reading.html Instrukcja czytania utworu
// style.css - arkusz stylów css dla obu powyższych stron www
// canvastext.js - znajdujący się w domenie publicznej plik Jima Studta zawierający
// reguły wyświetlania czcionek. W naszym przypadku dokonaliśmy w canvastext.js
// niewielkich zmian.
// [W wersji polskiej Jan Argasiński dodał definiowanie każdej z czcionek z
// ogonkami, przyp. red. pl]
// "ustawmy kurs na czółno przędz" zawiera nowe komentarze autorów do oryginalnego
// javascriptu. Pierwotnie, kod opatrzony był komentarzami, które ułatwić miały
// orientowanie się w jego obrębie tym wszystkim, którzy chcieliby spróbować jego
// ponownego użycia. W obecnej wersji rozwinięto te komentarze, kładąc nacisk na sam
// proces tworzenia generatora oraz na rolę komentarzy i przypisów, jakie towarzyszą
// kodowi. Niniejszy plik, obfitujący w uwagi zarówno praktyczne jak i metarefleksyjne,
// jest więc jednocześnie próbą stworzenia modelu krytycznego opracowania dzieła
// literackiego powstałego w efekcie zastosowania kodu komputerowego.
//
// Komentarz to podstawowa funkcja języków oprogramowania. Na przykład w języku BASIC
// zaczyna się go od "REM". Z kolei w Javascripcie, a także paru innych językach,
// komentarz umieszcza się w linijce rozpoczętej przez "//" lub obramowuje początkowym
// "/*", by móc zakończyć w dowolnym miejscu poprzez "*/".
//
// Dziełem prekursorskim, jeśli chodzi o umieszczanie komentarzy w literaturze jest
// wydanie Rymów o sędziwym marynarzuy Coleridge'a z 1817 roku, które oprócz motywów
// morza i rei zawiera glosy Coleridge'a o charakterze zarówno dydaktycznym jak i
// poetyckim. Ich celem było wprowadzenie autorskiego dodatku do wiersza oferującego
// nowe spojrzenie na oryginał wyrażone zmienionym głosem.
//
// Tytuł niniejszej wersji kodu z poszerzonymi komentarzami jest jedną z linijek
// Między Reją a Morzem wygenerowaną przez ten właśnie program.
//
// ustawmy kurs na czółno przędz
//
// Nasze komentarze umieszczone są pomiędzy linijkami działającego kodu, który pełni
// tu rolę "czółna przędz". Zamiast zatem pisać esej, "przybiliśmy" nasze glosy tak,
// by ustawiły się na kursie obranym przez nasz program
//
// Tak jak w przypadku wydania oryginalnego, także do niniejszego kodu załączamy notkę
// o prawach autorskich, w której zapewniamy, że każdemu wolno skopiować i zmodyfikować
// jakikolwiek fragment bądź całość naszej pracy, jeśli tylko wypełniona zostanie krótka
// lista warunków. Deklarując, iż oferujemy wolne oprogramowanie, zastrzegamy
// jednocześnie, że nie dajemy na nie gwarancji.
//
// Copyright (c) 2012, Nick Montfort i Stephanie Strickland
// Wszelkie prawa zastrzeżone
//
// Zezwala się na ponowne udostępnienie i użycie źródła i formatów binarnych,
// z lub bez modyfikacji, na następujących warunkach:
//
// * Redystrybucja kodu źródłowego musi zachować powyższą notę o prawach autorskich,
// niniejszą listę warunków oraz zawarte adnotacje.
// * Redystrybucja w formacie binarnym musi zawierać powyższą notę o prawach
// autorskich, niniejszą listę warunków oraz adnotacje zawarte w dokumentacji i/lub w
// materiałach dołączonych do dystrybucji
// * Nazwiska właścicieli praw, jak też i nazwiska każdego ze współpracowników nie
// mogą być używane w celach promocyjnych związanych z tym programem bez uprzedniej
// pisemnej zgody
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
// Choć na nasz projekt złożył się wysiłek programistyczny, dwa utwory o objętości
// książkowej oraz współpraca dwojga autorów, uświadamiamy sobie potencjał sieci, dzięki
// której powstają coraz to nowe formy i radykalne rodzaje kooperacji. Poprzez
// udostępnienie Między Reją a Morzem jako wolnego oprogramowania dajemy jasno do
// zrozumienia, że autorzy i programiści mogą czerpać zeń wszystko, co uznają za
// przydatne w ich własnych projektach, w podobny sposób, w jaki my zremiksowaliśmy Moby
// Dicka i wiersze Emily Dickinson.
//
// Niniejsza licencja, rzecz jasna, nie pełni tu roli komentarza, lecz odnosi się
// do statutu tego wydania.
//
// Skorzystaliśmy ze statycznego narzędzia o nazwie jslint przeszukującego kod w
// poszukiwaniu niektórych rodzajów błędów programistycznych. Komendy jslint same
// w sobie są wyrażane jako komentarze – – choć w tym przypadku odnoszą się one raczej
// do innego programu komputerowego niż do czytelnika.
//
// Poniższe cztery linijki mają na celu sprawdzenie kodu z jslint:
/*jslint browser: true*/
/*global window*/
/*global unescape*/
/*global CanvasTextFunctions*/
// Kod rozpoczyna się zadeklarowaniem zmiennych, które definiować będą ciągi numeryczne
// i znakowe. Definiując pierwszą deklarację upewniamy się, że program będzie wiedział,
// w jakiej pozycji znajduje się kursor myszy. Pozycja początkowa ustalona zostaje na
// 0.0:
//
//Zmienne liczbowe mouseX i mouseY:
var mouseX = 0, mouseY = 0;
// Nasz program wytwarza bezkresną siatkę kartograficzną strof. Za każdym razem, gdy
// program się uruchamia, czytelnik umieszczony zostaje w losowo wybranym położeniu.
// Następujące dwie linijki kodu ustanawiają te (losowe) współrzędne i są jedynymi
// fragmentami, w których mowa o losowości. Dla tych, którzy czytając poemat chcieliby
// znaleźć się w ściśle określonej pozycji, udostępniliśmy taką opcję i jest ona
// zdefiniowana w dalszej części kodu. Umożliwia ona wprowadzenie adresu url w formie
// ? x, y (np. 0,0 lub ?1234,22680099), dzięki czemu program rozrysuje siatkę
// współrzędnych tak, by wskazana w adresie pozycja znalazła się w jej środku.
// Koordynaty można też wprowadzić w okienko nawigacyjne umieszczone pod tekstem.
//
// Nazwy zmiennych baseI oraz baseJ biorą się stąd, iż wskazują one na określoną (I, J)
// pozycję w obrębie całej siatki współrzędnych, a nie na punkt (X, Y) w obrębie ekranu
// komputera, a zatem na element definiowany przez mouseX i mouseY:
var baseI = Math.round(Math.random() * 14992383);
var baseJ = Math.round(Math.random() * 14992383);
// Siatka współrzędnych rozciąga się między następującą liczbą jednostek: 14992383 x
// 14992383, których liczba jest konsekwencją pewnych wyborów natury retorycznej, które
// wyjaśnimy w dalszej częsci komentarza. Wybór losowej pozycji dokonuje się w oparciu
// o tę właśnie liczbę. Wprowadzone zaokrąglenie usuwa liczby po przecinku, w wyniku
// czego otrzymujemy wartość współrzędnej.
//
// Kierując się zasadą korespondencji między kompozycją a intencją, wrzucamy czytelnika
// nie tyle na "głęboką wodę", co na szerokie "morze", gdzie otacza go rozległy poemat,
// na który spogląda okiem żeglarza. Nie jest to jednak punkt widzenia w sensie nadanym
// przez Pounda, kiedy to żeglujący wpatruje się w ląd -- tutaj, teraz, w tym wierszu,
// ląd znika z horyzontu.
//
// Choć ilość strof jest skończona a ich układ nie jest losowy, to sama ich liczebność,
// w połączeniu z zawrotną prędkością, z jaką układają się jedna po drugiej, może wydać
// się "przerażająca", jak to określił pewien uznany autor i czytelnik cyfrowej
// literatury, lub "zapierająca dech", jak wyraził to inny czytelnik. Sięgając po takie
// terminy, jak sądzimy, chcieli oni zasygnalizować pewien rodzaj obfitości
// przekraczający zwykłą ludzką skalę, przyprawiającą o zawrót głowy, i przysparzającą
// znacznych trudności w orientacji.
//
// Następny szereg zmiennych odpowiada za typografię i aspekt wizualny.
//
// Wartość zmiennych canvas i context przypisywana jest przy starcie programu
// w funkcji setup(). Używa się ich, by definiowały element canvas i jeden z
// jego możliwych kontekstów graficznych: kontekst 2D
var canvas, context;
// Określenie wielkości czcionki fontSize:
var fontSize = 12;
// Pięć kolejnych zmiennych określa typografię strof. Ich wartości są przypisywane za
// każdym razem, gdy przywołana jest funkcja changeFontSize(),
// co zdarza się w odpowiedzi na określone działania z poziomu interfejsu.
// changeFontSize uaktywnia się także wraz
// ze starem programu, w ramach setup().
var lineHeight, column, stanzaHeight, spacingX, spacingY;
//
// DANE — słowa i frazy, które generator zestawia ze sobą definiujemy w następujących
// partiach kodu.
//
// Ta część zawiera dane literackie: słowa i zwroty, które generator układa w formy
// wierszowe, by przywołać lub wyjaskrawić elementy stylistyczne właściwe tekstom
// źródłowym, przynajmniej w naszej ich interpretacji. Owe teksty to wiersze Emily
// Dickinson (1830-1886) oraz całość powieści Moby Dick (1851) Hermana Melville'a
// (1819-1891).
//
// Jeśli ktoś chciałby nasze zestawy słowne zastąpić nowymi, należałoby zdefiniować
// generatora o podobnym wyglądzie i funkcjonowaniu, ale o innym zasobie słownictwa.
// Praktycznie możliwe jest stworzenie nowego generatora, który byłby jego remiksem
// lub zawłaszczeniem, poprzez wymianę danych tylko w niniejszej części kodu.
// Jeśli tak by się stało, a kod nie byłby zmieniany w innych miejscach, system
// złoży elementy językowe w identyczny jak tutaj sposób, ale w oparciu o inne zasoby
// językowe.
//
// W tym szczególnym systemie językowym, słowa i zwroty, które zamieszczamy
// stanowią dane.
// Inne programy literackie korzystają z innych modeli, wywiedzionych z odmiennych
// perspektyw, wykładnia naszego modelu znajduje się poniżej:
//
// Tablica danych shortPhrase zawiera krótkie zwroty, z których niemal
// całość pochodzi z Moby Dicka.
var shortPhrase = ['czymże jest czas?','i ty--też--','kołujże, kołuj','kręć się, kreć', 'miarowo', 'niechże ich', 'pętlij się, petlij', 'słuchajże teraz', 'wij się, wij', 'pędźże, pędź', 'umkniesz mi?','tocz się, tocz', 'wiosłujże, wiosłuj','wirujże, wiruj', 'wróćże się, wróć', 'zanurz się, zanurz','zwińże się, zwiń', 'ryba przytrzymana', 'ryba wolna'];
// Najczęściej, w niniejszej, jak i w kolejnych tablicach, organizowaliśmy dane językowe,
// kierując się zasadą kolejności alfabetycznej. Jednakże w shortPhrase postanowiliśmy
// umieścić "fast-fish" i "loose-fish" (termin Melville'a na określenie wieloryba
// przyciskanego do łodzi lub przytrzymywanego na otwartym morzu) obok siebie na końcu
// zbioru. Dzięki takiemu wyborowi upewniamy się, że zwroty te znajdą się niedaleko od
// siebie w wygenerowanym poemacie.
// [przyp. : W przypadku polskiej wersji, "ryba
// przytrzymana" i "ryba wolna" w sposób oczywisty i wynikający z szyku wyrazów
// tworzących te określenia znajdują się obok siebie, choć i tu – z uwagi na chęć
// zachowania specyfiki oryginału – także umieszczamy je na końcu zbioru]
//
// Tablica zmiennych dickinsonNoun zawiera popularne rzeczowniki spotykane w poezji
// Dickinson. Uznaliśmy je za popularne w oparciu o dokonaną przez nas analizę
// frekwencyjną słownictwa Dickinson. Tablica ta uzyskuje swoją własną strukturę (jest
// rodzajem tablicy tablic) za sprawą segregacji słów ze względu na ilość sylab:
var dickinsonNoun = [
['ból', 'druh', 'dym', 'ktoś', 'kunszt', 'kwiat', 'lęk', 'wdzięk', 'grób', 'świt', 'targ', 'wiatr', 'rok', 'raj','świat','śnieg', 'wzrok'],
['umysł', 'spektakl','poza', 'zachwyt', 'łatwy','kiedyś', 'ojciec', 'człowiek', 'ranek', 'dzisiaj','wieczność'],
['kolejny', 'spoczynek', 'jedyny', 'we mnie', 'w sobie'],
['nieśmiertelność'] ];
// Zbiór stworzony dla wersji polskiej pod wers OneLine z uwagi na zróżnicowanie na
// rodzaje gramatyczne. Jeśli nastąpi tu zmiana, trzeba ją także nanieść
// na dickinsonNoun powyżej:
var dickinsonOneNoun = [
['jeden ból', 'jeden druh', 'jeden dym', 'jeden grób', 'jeden ktoś', 'jeden kwiat', 'jeden łatwy', 'jeden lęk', 'jeden targ','jeden śnieg', 'jeden świt', 'jeden świat', 'jeden raj', 'jeden rok', 'jeden wdzięk', 'jeden wzrok', 'jeden wiatr'],
['jeden człowiek', 'jedno dzisiaj', 'jedno kiedyś', 'jeden ojciec', 'jedno poza','jeden ranek', 'jeden spektakl', 'jedna sztuka', 'jeden umysł',' jeden zachwyt', 'jedna wieczność'],
['jeden kolejny', 'jeden spoczynek', 'jeden we mnie', 'jeden jedyny', 'jeden w sobie'],
['jedna nieśmiertelność'] ];
// Tablica courseStart ustanawia trzy alternatywne początkiu do linijki
// compoundCourseLine:
var courseStart = ['ustawmy kurs na ', 'skrojony na ', 'jak przetrzymać '];
// Zamieszczone poniżej słowa [w wersji angielskiej wyłącznie jednosylabowe, przyp.
// red.pl], które są w częstym użyciu zarówno u Melville'a jak i Dickinson, są składane
// przez generator w zbitki wyrazowe (w kenningi). Najmniejszą jednostką języka, którą
// się tu zajmujemy jest zatem sylaba. Z uwagi na to, że nasze dwuwiersze mają przypominać
// (same w sobie ekscentryczne) strofy Dickinson a jednocześnie zawierać w sobie ślady
// stylu Melville'a, postanowiliśmy wprowadzić licznik sylab Nasza decyzja o generowaniu
// kenningów z uprzednio dobranych słów pochodzących z leksykonu obojga autorów także
// wymagała liczenia sylab, by wpasować wynik w linijkę wiersza.
//
// [Sytuacja polskiego tłumacza jest w tym przypadku
// nader skomplikowana. Wybrane przez nas do tablic dickinsonSyllable i
// melvilleSyllable słowa pochodzą w większości z
// istniejących polskich tłumaczeń dzieł Melville'a i Dickinson. Ani Barańczak ani
// Zieliński nie kierowali się oczywiście podczas prac translatorskich możliwą do
// wprowadzenia zasadą zachowania w przekładzie tej samej ilości sylab, jaką miały słowa
// oryginału. Tłumacz Między Reją a Morzem stoi zatem przed wyborem. Może : na siłę, i w
// całkiem odmiennym kontekście translatorskim, stworzyć jednosylabowce, czego się raczej
// nie praktykuje w translacjach z angielskiego, gdzie np. jednosylabowe rymy jambiczne,
// najczęściej zamienia się na rymy żeńskie, dwu lub trzysylabowe. Może też, tam gdzie
// istnieje taka możliwość, zachować istniejące polskie tłumaczenia wybranych przez
// Strickland i Montforta słów, tłumacząc jedynie te nieprzetłumaczone, i stworzyć zbitki
// wielosylabowe. My zdecydowaliśmy się na opcję drugą. Warto też pamiętać, że struktura
// kenningu w polszczyźnie, będąc strukturą zależną, zawsze wymagać będzie, aby drugi
// człon był był dwusylabowy, choć w mianowniku jest jednosylabowy,,np. "bard barda",
// "dok barda", "bard doku", przyp. red. pl]
//
// Tablice zmiennych dickinsonSyllable i melvilleSyllable:
// Sylaby, które łączone są w kenningi, czyli metafory dopełniaczowe (ang. compound words):
var dickinsonSyllableNominative = ['błonę', 'brzęczenie','dysk', 'kroplę', 'kukłę', 'łuskę','mgłę', 'opłatę', 'osłonę', 'pajęczynę', 'paproć', 'piach','plamę', 'plemię','przędzę', 'pszczołę','pukiel', 'różę', 'skrzynkę', 'sójkę','spisek', 'ule', 'werbel', 'wieszcza', 'zarodek'];
var dickinsonSyllableGenative = ['błon','brzęczenia', 'dysków','kropel','kukieł', 'łusek', 'mgieł', 'opłat', 'osłon', 'pajęczyn', 'paproci', 'piachu', 'plam', 'plemion', 'pszczół','przędz', 'pukli','różu', 'sójek', 'skrzynek', 'spisków', 'uli','werbli', 'wieszczów','zarodków'];
var melvilleSyllableNominative = ['barana','byka','czaty','dok', 'hak', 'koję', 'koguta', 'laskę','łachman', 'krawędź', 'płetwę', 'kozę', 'kopniak', 'krewniak', 'mleko', 'młodziana', 'mordę', 'czółno', 'obręcz', 'paszczę','popiół', 'reję', 'róg', 'sakwę', 'sieć', 'sokoła','sól', 'stożek', 'szpikulec', 'ślimaka','wiedźmę','wygę','wapień', 'waleta','wariata', 'wegorza','wycie', 'znój','żelazo'];
var melvilleSyllableGenative = ['byków','czatów','doków', 'haków', 'kój','kóz', 'koguta', 'kopniaka', 'krewniaka','lasek','łachmanów', 'mleka','młodziana', 'mord', 'narzedzi', 'krawędzi', 'obręczy', 'paszczy', 'płetw', 'popiołów', 'rogow','sakiew', 'sieci', 'szpikulca', 'ślimaków','soli','sokołów','stożków', 'rei', 'waleta','wapienia', 'wariata','wegorzy', 'wiedźm','wyg','wycia','znojów','żelaza'];
// Chcieliśmy, aby kod rozróżniał, który z dziewiętnastowiecznych autorów był autorem
// danego jednosylabowca. Może być on autorem całego kenningu, tylko pierwszego członu
// lub tylko członu drugiego. W trakcie generowania złożeń słowa generowane są z tablicy
// zbiorczej. Poniższy kod odpowiada za łączenie dwóch tablic i sortowanie alfabetyczne.
//
// Tablica var syllable przechowuje posortowane złożenia.
// [W Wersji polskiej należało stworzyć dodatkowe zmienne,
// które kierują szykiem i odmianą złożeń, zwracając w pierszym członie mianownik,
// w drugim dopełniacz: syllableNominative oraz var syllableGenative,
// przyp. red. pl]:
var syllableNominative = dickinsonSyllableNominative;
syllableNominative.concat(melvilleSyllableNominative);
syllableNominative.sort();
var syllableGenative = dickinsonSyllableGenative;
syllableGenative.concat(melvilleSyllableGenative);
syllableGenative.sort();
// W wierszach Dickinson często spotyka się intrygujące neologizmy w oryginale
// angielskim zakończone przyrostkiem "less". W polskim tłumaczeniu przybierają
// one formę przymiotników poprzedzonych przyrostkami "nie", "bez", "niedo" czego
// egzemplifikacją jest słowo bezkunsztny [ang. artless]. Melville także nie stroni
// od tego typu konstrukcji (np. masterless), ale u Dickinson użycie sufiksów
// wydaje się działać jako rodzaj kompresji języka oraz wycofania się na końcu słowa
// z obietnicy złożonej na jego początku. Poniżej znajdują się rdzenie wykorzystywane
// przez program do generowania takiego rodzaju słów.
//
// Tablica zmiennych dickinsonLessLess, podobnie jak dickinsonNoun, jest tablicą
// zawierającą inną tablicę, której zawartość sortowana jest według liczby sylab:
// Poniższy zbiór w wersji angielskiej zawierał same
// rdzenie takich wyrazów. Polszczyzna, ze względu na zróżnicowanie na "bez" i
// "nie" oraz na rodzaje gramatyczne, wymusza pozostawienie przedrostków i
// stworzenie trzech oddzielnych grup zamiast jednej: dla rodzaju żeńskiego,
// męskiego i nijakiego. Zbiory posegregowane są według ilości sylab.
var dickinsonMLessLess = [
['bezbarwny','bezcenny','niedbały', 'bezdźwięczny', 'bezdenny', 'bezgłośny', 'bezgwiezdny', 'niemodny', 'beznogi', 'niepomny','bezrdzenny', 'niespójny','niewinny','bezwstydny'],
['bezcelowy', 'bezcielesny', 'bezforemny', 'bezgustowny', 'nieistotny', 'bezkształtny', 'bezowocny', 'niechwiejny', 'niechwalony', 'niepojęty', 'niepotrzebny', 'nieprzerwany', 'nieruchomy','nieśmiertelny', 'nieobrzeżany', 'bezsmakowy', 'nieosłowiony', 'niewzburzony', 'bezpluski', 'bezzmienne', 'bezwspółrzędny', 'bezgłębny', 'niespełny'],
['niedoprawiony', 'niedostrzegalny', 'niehonorowy', 'nieokruszony', 'nieodgadniony','nieuchylny', 'nieograniczony', 'bezkalkulacyjny', 'nieodwracalny','nieodzyskany', 'nieosadzony', 'niedokryty', 'nieprzykuwalny', 'niepożądliwy', 'nieskazitelny',
'niestroskany', 'bezkrewny', 'nieodwlekły', 'nieuleczalny', 'nieunikniony', 'bezodnogi', 'nieustający', 'niewyszukany', 'niewidzialny', 'niewysłowiony', 'niepomniejszony','niezachwiany', 'niezakłamany', 'niestwarzony', 'niezamieszkany'],
['nieodzyskiwalny','nieprzemijajacy', 'nieukształtowany',
'niedoschły', 'nieprzywdziany'] ];
// dodatek wersji pl do zgodności rodzaju – nijaki:
var dickinsonButNLessLess = [
['bezbarwne','bezcenne','niedbałe', 'bezdźwięczne', 'bezdenne', 'bezgłośne', 'bezgwiezdne', 'niemodne', 'beznogie', 'niepomne','bezrdzenne', 'niespójne','niewinne','bezwstydne'],
['bezcelowe', 'bezcielesne', 'bezforemne', 'bezgustowne', 'nieistotne', 'bezkształtne', 'bezowocne', 'niechwiejne', 'niechwalone', 'niepojęte', 'niepotrzebne', 'nieprzerwane', 'nieruchome','nieśmiertelne', 'nieobrzeżane', 'bezsmakowe', 'nieosłowione', 'niewzburzone', 'bezpluskie', 'bezzmienne', 'bezwspółrzędne', 'bezgłębne', 'niespełne'],
['niedoprawione', 'niedostrzegalne', 'niehonorowe', 'nieokruszone', 'nieodgadnione','nieuchylne', 'nieograniczone', 'bezkalkulacyjne', 'nieodwracalne','nieodzyskane', 'nieosadzone', 'niedokryte', 'nieprzykuwalne', 'niepożądliwe', 'nieskazitelne',
'niestroskane', 'bezkrewny', 'nieodwlekłe', 'nieuleczalne', 'nieuniknione', 'bezodnogie', 'nieustające', 'niewyszukane', 'niewidzialne', 'niewysłowione', 'niepomniejszone','niezachwiane', 'niezakłamane', 'niestwarzone', 'niezamieszkane'],
['nieodzyskiwalne','nieprzemijajace', 'nieukształtowane',
'niedoschłe', 'nieprzywdziane'] ];
// dodatek wersji pl do zgodności rodzaju – żeński:
var dickinsonButFLessLess = [
['bezbarwna','bezcenna','niedbała', 'bezdźwięczna', 'bezdenna', 'bezgłośna', 'bezgwiezdna', 'niemodna', 'beznoga', 'niepomna','bezrdzenna', 'niespójna','niewinna','bezwstydna'],
['bezcelowa', 'bezcielesna', 'bezforemna', 'bezgustowna', 'nieistotna', 'bezkształtna', 'bezowocna', 'niechwiejna', 'niechwalona', 'niepojęta', 'niepotrzebna', 'nieprzerwana', 'nieruchoma','nieśmiertelna', 'nieobrzeżana', 'bezsmakowa', 'nieosłowione', 'niewzburzona', 'bezpluska', 'bezzmienna', 'bezwspółrzędna', 'bezgłębna', 'niespełna'],
['niedoprawiona', 'niedostrzegalna', 'niehonorowa', 'nieokruszona', 'nieodgadniona','nieuchylna', 'nieograniczona', 'bezkalkulacyjna', 'nieodwracalna','nieodzyskana', 'nieosadzona', 'niedokryta', 'nieprzykuwalna', 'niepożądliwa', 'nieskazitelna',
'niestroskana', 'bezkrewna', 'nieodwlekła', 'nieuleczalna', 'nieunikniona', 'nieusztywniona', 'nieustająca', 'niewyszukana', 'niewidzialna', 'niewysłowiona', 'niepomniejszona','niezachwiana', 'niezakłamana', 'niestwarzona', 'niezamieszkana'],
['nieodzyskiwalna','nieprzemijajaca', 'nieukształtowana','niedoschła',
'nieprzywdziana'] ];
// Poniższa tablica zmiennych dickinsonFlatLessLess powstała po to, by zawrzeć
// "spłaszczoną" [ang. flat] wersję dickinsonLessLess -- jest to jeden długi zbiór słów,
// które zostały posortowane, ale bez uwzględniania ich wewnętrznej struktury:
// Omawiany tu fragment kodu w oryginale angielskim tworzy długą listę wszystkich
// rdzeni wyrazowych z konstrukcji "less", a następnie generuje całe wyrazy,
// sortując je ze względu na liczbę sylab.
// W wersji polskiej kod ten posiada w gruncie rzeczy pusty przebieg
var dickinsonFlatLessLess = dickinsonMLessLess[0];
dickinsonFlatLessLess.concat(dickinsonMLessLess[1], dickinsonMLessLess[2]);
dickinsonFlatLessLess.sort();
var dickinsonFlatButFLessLess = dickinsonButFLessLess[0];
dickinsonFlatButFLessLess.concat(dickinsonButFLessLess[1], dickinsonButFLessLess[2]);
dickinsonFlatButFLessLess.sort();
var dickinsonFlatButNLessLess = dickinsonButNLessLess[0];
dickinsonFlatButFLessLess.concat(dickinsonButNLessLess[1], dickinsonButNLessLess[2]);
dickinsonFlatButNLessLess.sort();
// Tablica zmiennych upVerb zawiera słowa, które mogą sugerować pozytywny nastrój:
var upVerb = ['głos', 'krok', 'kurant', 'marsz', 'odwrót','ruch', 'śpiew', 'taniec', 'zryw','żar'];
// Tablice zmiennych butBeginning i butEnding zawierają słowa, które
// rozpoczynają lub kończą jeden typ wersu, określony przez nas jako butLine:
var butBeginning = ['gdyż','jakże', 'i tylko'];
var butNEnding = ['morze', 'niebo', 'słońce'];
var butFEnding = ['ziemia'];
// Tablica zmiennych threeToFiveSyllable zawiera dwu, trzy i czterosylabowe wyrazy
// pochodzące z dickinsonNoun oraz dwusylabowe wyrazy z tablicy dickinsonLessLess:
var threeToFiveSyllable = dickinsonNoun[2];
threeToFiveSyllable.concat(dickinsonNoun[3] + dickinsonNoun[4] + dickinsonMLessLess[2]);
// Tablica zmiennych twoSyllable przechowuje jednosylabowe wyrazy z obu list:
var twoSyllable = dickinsonNoun[1];
twoSyllable.concat(dickinsonMLessLess[1]);
// Tablica zmiennych nailedEnding zawiera słowa (obu autorów), które
// kończą jeden rodzaj wersu, nazwany przez nas nailedLine:
var nailedEnding = ['belki', 'deski', 'dziobu', 'kabiny', 'masztu', 'mostka','pokładu','poręczy','rei', 'sakwy', 'trumny'];
// Analizy tekstu – frekwencyjna oraz ilościowa – których się systematycznie
// podejmowaliśmy, były zadaniem względnie prostym. Warto jednak zastrzec,
// że miały one charakter poszukujący a ich celem było rozpoznanie nowych
// poetyckich możliwości i otwarcie literatury na nowe pytania, a nie
// na przykład określanie autorstwa konkretnego tekstu lub szeroko zakrojona
// analiza statystyczna. Pod tym względem nasz projekt dzieli
// pewne podobieństwa z ideami "distant reading" Franco Morettiego oraz
// "not-reading" Tany Clement i koresponduje z analizą The Making
// of Americans Gertrudy Stein przeprowadzoną przez wspomnianą badaczkę.
// Ponieważ naszym celem jest wypracowanie nowego ujęcia i
// sposobu rozumienia dwóch zaledwie tekstów, a nie olbrzymiej ilości książek,
// Między Reją a Morzem jest najsilniej spokrewnione z pracą Clement,
// która komputerową analizę tekstu wykorzystuje po to, by wzbogacić,
// a nie zastąpić, dotychczasowe odczytania.
//
// FUNKCJE odpowiedzialne za generowanie poszczególnych typów wersów
// i za rozmieszczanie ich na siatce współrzędnych w oknie
// przeglądarki, za zarządzanie działaniami użytkownika i za inne
// zdarzenia, zdefiniowane są w poniższej części kodu.
//
// Jeśli kod w tej części zostanie zmodyfikowany, a dane w partiach
// poprzedzających pozostaną bez zmian, to słowa i zwroty tam
// zawarte mają szansę być zaprezentowane w nowy sposób.
//
// Pisanie komentarzy w kodzie, podobnie jak dopisywanie glosów do
// tekstu, zobowiązuje do bycia precyzyjnym i do komentowania
// wybranych wersów.
//
// Jednak czasem potrzebna jest refleksja natury ogólnej.
// Przy okazji owego przejścia z mówienia o "danych" do komentowania
// "funkcji", przydatnym jest spojrzenie na procesy,
// w których dane mieszają się z instrukcjami, i na to, za co te
// instrukcje tak naprawdę są odpowiedzialne.
//
// Kod Między Reją a Morzem [w oryginalnej wersji,
// przyp. red. pl] bez komentarzy ma 11558 bajtów, czyli mniej
// niż 12 KB, i zawiera tylko około 1300 "słów",
// z czego 3295 bajtów (około 28%) odpowiada za określenie danych,
// zadeklarowanie i rozpisanie zmiennych.
// Program nie jest zatem zdominowany przez dane. Dla odmiany,
// program, który po prostu wyświetlałby długi, statyczny tekst zawierałby
// przede wszystkim dane (tekst, który się pokazuje na ekranie),
// natomiast ilość kodu, odpowiedzialnego za wyświetlanie tekstu,
// byłaby szczątkowa.
//
// Z kolei w Miedzy Reją a Morzem większość kodu odpowiada za zdarzenia
// interfejsowe i za rozrysowanie strof w obrębie elementu
// "canvas" w przeglądarce. Tylko 2609 bajtów, około 22% kodu, użytych
// zostało do łączenia segmentów tekstowych i
// układania strof. Pozostałe 5654 bajty (około 50%) to kod odpowiadający
// za wyświetlanie strof i interakcję.
//
// Stworzyliśmy siedem szablonów wersów, trzy dla pierwszej i
// cztery dla drugiej linijki dwuwiersza. Szablony te zaprojektowaliśmy tak,
// by każdy z nich przywoływał pewien retoryczny gest właściwy - przynajmniej
// naszym zdaniem - dla tekstu źródłowego, oraz by uwypuklić silną
// tendencję do figur negacji, charakterystyczną dla Dickinson.
//
// Poniższe funkcje wersu pierwszego są częścią głównego generatora
// poetyckiego, dzięki nim powstaje pierwsza linijka dwuwersowej stanzy.
// Funkcja shortLine() może na przykład wygenerować zwrot "umkniesz mi?":
function shortLine(n) {
return shortPhrase[n % shortPhrase.length];
// % jest operatorem typu mod "n % m", który zwraca resztę po podzieleniu
// n przez m. Dla przykładu wartość "n % 10" to najmniej 0 a najwięcej 9.
// W tym przypadku % zezwala każdej wartości n na wybranie elementu z
// listy shortPhrase.
}
// We wszystkich innych przypadkach zaprojektowaliśmy pierwsze linijki
// poprzez wybór słów, które w nich zostaną użyte oraz przez
// określenie składni.
//
// Funkcja oneNounLine() może wygenerować na przykład zwrot
// "jeden dym jeden śnieg jeden grób jeden świt"
// [zasób wyrazów w polskiej wersji oneNounLine()
// dość szczęśliwie dostroił się do wersji angielskiej, w której nie ma podziału
// na rodzaje. Wyrazy tu użyte okazały się w zdecydowanej większości wyrazami
// rodzaju męskiego, co sprzyjało zastosowaniu prostego
// szablonu "jeden + rzeczownik r.m". Jeśli jednak znacząca część słownictwa
// z tej tablicy byłaby także rodzaju żeńskiego lub nijakiego, to w
// wersji polskiej musielibyśmy
// rozdzielić listę na trzy podgrupy, tak jak zrobiliśmy to np. w DickinsonLessLess,
// przyp red. pl]
function oneNounLine(n)
{
var a, b, c, d = n % dickinsonOneNoun[0].length;
n = Math.floor(n / dickinsonOneNoun[0].length);
c = n % dickinsonOneNoun[0].length;
n = Math.floor(n / dickinsonOneNoun[0].length);
b = n % dickinsonOneNoun[0].length;
n = Math.floor(n / dickinsonOneNoun[0].length);
a = n % dickinsonOneNoun[0].length;
return dickinsonOneNoun[0][a] + ' ' + dickinsonOneNoun[0][b] + ' ' + dickinsonOneNoun[0][c] + ' ' + dickinsonOneNoun[0][d];
}
// Funkcja compoundCourseLine() może wygenerować np. "ustawmy kurs na czółno przędz":
function compoundCourseLine(n)
{
var a, b, c = n % syllableNominative.length;
n = Math.floor(n / syllableNominative.length);
b = n % syllableNominative.length;
n = Math.floor(n / syllableNominative.length);
a = n % courseStart.length;
return courseStart[a] + syllableNominative[b] + ' ' + syllableGenative[c];
}
// w pl wersji usunięto + 'course' //
// Funkcja firstLine() zwraca pierwszą linijkę dwuwiersza, która składać może się z
// trzech wyżej komentowanych wariantów.
function firstLine(n) {
var m = Math.floor(n / 4);
if (n % 4 < 2) {
return shortLine(m);
}
if (n % 4 === 2) {
return oneNounLine(m);
}
return compoundCourseLine(m);
}
//
// Przechodzimy teraz do wersu drugiego
// Funkcja riseAndGoLine może wygenerować na przykład zwrot "beznogi marsz i odwrót":
function riseAndGoLine(n)
{
var a, b, c = n % upVerb.length, dash = '';
n = Math.floor(n / upVerb.length);
b = n % upVerb.length;
n = Math.floor(n / upVerb.length);
a = n % dickinsonFlatLessLess.length;
if (dickinsonFlatLessLess[a] in dickinsonMLessLess[0])
{
dash = ' --';
}
return dickinsonFlatLessLess[a] + ' ' + upVerb[b] + ' i ' + upVerb[c] + dash;
// w wersji pl zmioniony szyk
}
// Jakkolwiek poprzednia funkcja generuje takie wersy, jak te właśnie zaprezentowane,
// to jednak efekt jej działania odbiega od początkowych założeń; jest inny, niż
// mogłaby to sugerować szybka lektura kodu. Ogląd kodu, którego fragment zamieszczono
// powyżej sugeruje, że program powinien wygenerować wers " beznogi marsz i odwrót
// --" (z myślnikiem na końcu). Tak się jednak nie dzieje, ponieważ sformułowany tu
// warunek nie może zostać spełniony, gdyż działając poprawnie w Pythonie, nie działa
// w ten sam sposób w języku programowania, jakim jest Java Script
//
// Błąd ten pojawił się ponieważ oryginalny generator napisany został w Pythonie.
// Konwersja do formatu JavaScript nastąpiła nieco później. Niemniej jednak program
// działa; efekt uzyskiwany na wyjściu – brak myślnika na końcu wersu – spodobał się nam.
// To, że pozostawiliśmy ów wers programu w niezmienionym kształcie, może jednakże
// sprawiać trudności tym, którzy chcą zrozumieć wszystkie szczegóły programu, by móc go
// modyfikować i w oparciu o jego kod tworzyć własny generator program. Równocześnie
// decyzja nasza o pozostawieniu myślnika w kodzie motywowana jest także w następujący
// sposób: pragnęliśmy uświadomić czytelnikowi kodu, że nawet stosunkowo krótkie programy
// zachowują w sobie ślady etapów ich tworzenia.
// Funkcja butLine może wygenerować m.in. "gdyż bezgłośne jest słońce":
function butLine(n)
{
var a, b, c = n % butNEnding.length;
n = Math.floor(n / dickinsonFlatButNLessLess.length);
b = n % dickinsonFlatLessLess.length;
n = Math.floor(n / dickinsonFlatButNLessLess.length);
a = n % butBeginning.length;
return butBeginning[a] + ' ' + dickinsonFlatButNLessLess[b] + ' jest ' + butNEnding[c];
}
// w wersji pl dodatkowa linijka do zgodności z r.ż "ziemia"
// z nowego zbioru dickinsonButFLessLess gdzie F to końcowki żeńskie:
function butFLine(n)
{
var a, b, c = n % butFEnding.length //n % dickinsonButFLessLess.length;
n = Math.floor(n / dickinsonButFLessLess.length);
b = n % dickinsonFlatButFLessLess.length;
n = Math.floor(n / butBeginning.length);
a = n % butBeginning.length;
return butBeginning[a] + ' ' + dickinsonFlatButFLessLess[b] + ' jest ' + butFEnding[c];
}
// Funkcja exclaimLine może wygenerować m.in. "w sobie! łatwy!":
function exclaimLine(n)
{
var a, b = n % twoSyllable.length;
n = Math.floor(n / twoSyllable.length);
a = n % threeToFiveSyllable.length;
return threeToFiveSyllable[a] + '! ' + twoSyllable[b] + '!';
}
// Fukcja nailedLine wytwarza linijkę wersu rozpoczynającą się od słów "przybity do ..."
//
// W powieści Moby Dick, Ahab przybija do masztu dublon (złotą hiszpańską monetę)
// mający być nagrodą dla tego marynarza, który pierwszy dostrzeże białego wieloryba.
// Szablon tej linii pomyślany został tak, by odzwierciedlać w warstwie
// semantycznej próby poszukiwania punktu odniesienia, które podejmuje czytelnik
// naszego poematu, a także te, które odnajdujemy w fabule powieści Melville'a,
// gdzie "bycie na morzu" pociąga za sobą
// konieczność jednoczesnego odnajdywania się w trudnych sytuacjach dzięki
// wewnętrznemu kompasowi moralnemu, śledzenia ofiary i podążania za nią podczas
// połowu, kontrolowania załogi statku metodą przekupstwa, i wreszcie używanie
// masztu jako wskaźnika, umożliwiającego XIX-wiecznym żeglarzom wytyczanie kursu
// względem gwiazd widocznych na nocnym niebie.
function nailedLine(n)
{
var a = n % nailedEnding.length;
if (shortPhrase[a] == 'ryba wolna' || shortPhrase[a] == 'ryba przytrzymana') {
return 'przybita do ' + nailedEnding[a];
} else {
return 'przybity do ' + nailedEnding[a];}
}
// Funkcja secondLine() zwraca jedną spośród czterech typów linijek
// służących za drugą linijkę dwuwiersza:
function secondLine(n) {
var m = Math.floor(n / 4);
if (n % 4 === 0) {
return riseAndGoLine(m);
}
if (n % 4 === 1) {
return butLine(m);
}
if (n % 4 === 2) {
return exclaimLine(m);
}
return nailedLine(m);
}
//
// Funkcje związane z rysowaniem tekstu i ze sterowaniem zdarzeniami
//
// W tym miejscu kończy się fragment kodu odpowiadający za generowanie
// poezji i rozpoczyna się ten, który rysuje słowa oraz warunkuje interakcję.
// Właściwie następna funkcja, "drawPair", jest również w pewnym sensie
// odpowiedzialna za generowanie poezji, ponieważ
// rozrysowując linie na ekranie, zestawia ze sobą pierwszą i
// drugą linijkę dwuwiersza.
// Funkcja drawPair rysuje kuplet – dwa wersy – na "płótnie", którym
// jest dwuwymiarowa przestrzeń Canvas. Rysowanie odbywa się poprzez
// wywołanie metody drawText (w canvastext.js), z użyciem graficznych
// koordynat x,y. Linie dwuwiersza są określane
// przez funkcje firstLine i secondLine, którym nadane zostają
// odpowiednie współrzędne.
// Między Reją a Morzem generuje strofy złożone z czterech wersów, układających
// się w dwa kuplety (dystychy, dwuwiersze); każdy z dwuwierszy powstaje w
// efekcie posłużenia się komendą drawPair i rozpoczyna się w jeden z trzech
// możliwych sposobów (shortLine, oneNounLine lub compoundCourseLine) oraz kończy
// wersem realizującym jeden z możliwych schematów (riseAndGoLine, butLine, exclaimLine,
// nailedLine). Każdy z dwuwierszy generowany jest w oparciu o swoiste i założone
// dla jego schematu reguły kombinatoryczne określające proces językowego asamblażu
// i umiejscowienie w określonym punkcie na siatce współrzędnych. Reguły te
// są proste; nie ma tu rozbudowanej
// architektury AI, nie ma też mowy o działaniu procesów sterowanych statystycznie.
//
// Tło dla naszego projektu stanowią tarcze kombinatoryczne Raymonda Llullusa,
// maszyna literacka Jonathana Swifta; w sposób bezpośredni odnosimy się też
// do wczesnych projektów komputerowych - do poematów permutacyjnych Briona Gysina
// i Iana Sommerville'a, także do House of Dust Alison Knowles i Jamesa Tenney'a
// oraz do wielu innych historycznych i współczesnych badań nad sposobami,
// w jakie można łączyć ze sobą fragmenty języka.
//
// Współrzędne Między Reją a Morzem są deterministyczne; do każdej z koordynat
// przypisana jest określona kombinacja słów i linii (wersów) tak by (teoretycznie)
// system ten mógł przedstawić wszystkie możliwe teksty. W tym sensie system nasz
// podobny jest do "I AM THAT I AM" Gysina i Sommerville'a – poematu, który zawiera
// wszystkie permutacje tytułowego zdania.
function drawPair(i, j, x, y) {
y += lineHeight;
context.drawText('sans', fontSize, x, y, firstLine(i + j + 1));
y += lineHeight;
context.drawText('sans', fontSize, x, y, ' ' +
secondLine(Math.abs(i - j) + 1));
}
// Strona WWW jest niedoskonałym, jakkolwiek rozwiniętym i rozbudowanym mechanizmem
// służącym także do wyświetlania tekstu. Istnieje możliwość prostego generowania
// elementów HTML i wyświetlania tekstu bezpośrednio na stronie dzięki apletowi Java
// Script i z użyciem dowolnej czcionki, którą obsługuje przeglądarka.
//
// Zamiast tego jednak Między Reją a Morzem wykorzystuje element canvas
// dostępny w HTML5 i obsługiwany przez większość współczesnych przeglądarek.
// Element canvas staje się w Między Reją a Morzem obszarem graficznej reprezentacji.
// Aby "wpisać" tekst w ten obszar, należy najpierw zdefiniować czcionkę
// (robi się to w osobnym pliku), cały zaś system rysowania elementów typograficznych
// musi zostać zbudowany od podstaw. Zaletą jaką niesie ze sobą takie rozwiązanie
// jest możliwość sprawowania pełniejszej i lepszej kontroli zarówno nad wizualnymi
// aspektami rysowanego tekstu, jak i nad sposobem w jaki użytkownik
// wchodzi nim w interakcję.
// Funkcja readCoords() analizuje koordynaty z okienka URL (jeśli takie wpisano) i
// ustawia je pośrodku siatki współrzędnych:
function readCoords() {
var params = window.location.search, a;
if (params.substring(0, 1) === '?') {
params = params.substring(1);
}
params = params.split(',');
for (a = 0; a < params.length; a += 1) {
params[a] = unescape(params[a]);
}
return params;
}
// Funkcja drawCoords() wyświetla współrzędne liczbowe centralnej strofy,
// które pojawiają się bezpośrednio nad nią:
function drawCoords(i, j, x, y) {
var stroke = context.strokeStyle;
context.strokeStyle = "rgba(255,255,255,1.2)";
context.drawText('sans', 12, x, y, i + ' : ' + j);
context.strokeStyle = stroke;
}
// Funkcja canonical() zamienia dane liczbowe na "kanoniczne" współrzędne
// (wartości nie mniejsze niż 0 i nie większe niż 14992383), aby uniknąć wyniku
// z liczbą ujemną lub z bardzo wysokimi liczbami. W efekcie morze staje się torusem,
// tworząc pętlę zarówno z lewej i prawej strony, jak też i z kierunków góra/dół.
// Wymiar 14992383 został zdeterminowany przez ogromną liczbę możliwych permutacji
// linijki "one _ one _ one _ one _"
function canonical(value) {
value = value % 14992384;
if (value < 0) {
value = value + 14992384;
}
return value;
}
// Funkcja drawLattice() jest główną funkcją programu. Rysuje ona całą
// możliwą porcję siatki współrzędnych widoczną w danym momencie
// w oknie przeglądarki:
function drawLattice(startI, startJ) {
var startX, startY, i, j, x, y;
// Rysowanie tła:
context.fillStyle = "rgba(199,220,254,1)";
context.fillRect(0, 0, canvas.width, canvas.height);
startX = (canvas.width - column) / 2; // X position of central stanza.
startY = (canvas.height - stanzaHeight) / 2; // Y position.
// Rysowanie koordynatów dwuwiersza:
drawCoords(canonical(baseI + startI), canonical(baseJ + startJ), startX,
startY);
// W tym miejscu startX and startY wskazują na punkt, w którym
// narysowana zostanie centralna strofa.
// Jeśli okno jest wystarczająco dużych rozmiarów, a rozmiar czcionki
// odpowiednio mały, parametry te zostają
// zmienione tak, by pomieścić inne strofy
while (startX > 0) { // Dopóki nie znajdziemy się w pozycji 0 lub poza stroną na lewo,
startX -= spacingX; // cofnij się o jedną miejsce ...
startI -= 1; // tak byśmy mogli narysować poprzednią (i-1) strofę
} // z lewej strony.
while (startY > 0) { // Dopóki nie będziemy w pozycji 0 lub poza stroną u góry
startY -= spacingY; // ustaw się o jedną strofę wyżej...
startJ -= 1; // tak by narysować poprzednią stanzę
} // parę linijek (j-2) oraz (j-1) powyżej.
i = canonical(baseI + startI);
// i znajduje się teraz we właściwej pozycji pierwszego koordynatu dla górnej strofy
// po lewej
for (x = startX; x <= canvas.width; x += spacingX) {
j = canonical((baseJ + startJ) * 2);
// Pomnożenie przez dwa powoduje, że siatka współrzędnych porusza się do góry i
// w dół tylko o jedną strofę (dwa kuplety). Jeśli nie zostałoby to określone,
// przerwy między strofami nie byłyby kontrolowane.
for (y = startY; y <= canvas.height; y += spacingY - lineHeight * 3) {
// Rysowanie pojedynczej storfy, najpierw pierwszy dystych, potem drugi.
drawPair(i, j, x, y, lineHeight);
j = canonical(j + 1);
y += lineHeight * 3;
drawPair(i, j, x, y, lineHeight);
j = canonical(j + 1);
}
i = canonical(i + 1);
}
}
// Funkcja changeFontSize() poprzez dodanie wartości delta do aktualnie używanej
// wielkości czcionki określa na nowo przestrzeń między wierszami i na nowo wytycza
// siatkę współrzędnych:
function changeFontSize(delta) {
fontSize += delta;
fontSize = Math.max(4, fontSize);
lineHeight = context.fontAscent('sans', fontSize) +
context.fontDescent('sans', fontSize);
column = fontSize * 22;
stanzaHeight = lineHeight * 5;
spacingX = fontSize * 38;
spacingY = stanzaHeight * 2;
drawLattice(parseInt(mouseX / 3, 10), parseInt(mouseY / 3, <10));
}
// Funkcja updateWheel() dokonuje zmian zgodnie z ruchem kółka myszy. Jest to
// jedno z kilku ustawień w kodzie odpowiedzialnych za zdarzenia w obrębie
// interfejsu inicjowane przez użytkownika:
function updateWheel(e) {
var evt, wheel;
evt = window.event || e; // Select available event object.
wheel = evt.detail ? evt.detail * (-120) : evt.wheelDelta;
if (wheel > 0) {
changeFontSize(1);
} else {
changeFontSize(-1);
}
}
// Funkcja markStanza() wyznacza współrzędne w okienku nawigacyjnym:
function markStanza() {
var textInput = document.getElementById("coords");
textInput.value = canonical(baseI + parseInt(mouseX / 3, 10)) + ",";
textInput.value += canonical(baseJ + parseInt(mouseY / 3, 10));
}
// Funkcja keyDown() reaguje na naciśnięcia klawiszy a, A, z, Z, SPACJA oraz klawiszy
// ze strzałkami. A i z zbliżają i oddalają tekst, klawisz spacji wyznacza strofę,
// natomiast klawisze strzałek pozwalają na poruszanie się [przemieszczanie się] o
// odległość całego ekranu [w oryg. "screenful", przyp. red. pl]
// w dowolnym kierunku.
// Wyznaczenie tych klawiszy stanowi samo w sobie dość skomplikowany proces.
// Inną metodę stosuje się przy programowaniu klawiszy a, A, z, Z, oraz przy
// programowaniu zachowań po naciśnięciu spacji klawiszy ze strzałkami. Choć dzięki
// internetowi z takiego programu jak ten stworzony przez nas może korzystać niemal
// każdy, to różne przeglądarki działają w nieco odmienny sposób, dlatego trzeba ze
// szczególną uwagą programować zdarzenia, które wywołuje się za pomocą klawiszy
// tak, by działały one w każdej przeglądarce:
function keyDown(e) {
var key = String.fromCharCode(e.keyCode);
if (key === "a" || key === "A") {
changeFontSize(1);
} else if (key === "z" || key === "Z") {
changeFontSize(-1);
} else if (key === ' ') {
markStanza();
// Pozostała część instrukcji steruje klawiszami strzałek
} else if (e.keyCode === 37) {
baseI = canonical(baseI - 1);
drawLattice(parseInt(mouseX / 3, 10), parseInt(mouseY / 3, 10));
} else if (e.keyCode === 38) {
baseJ = canonical(baseJ - 1);
drawLattice(parseInt(mouseX / 3, 10), parseInt(mouseY / 3, 10));
} else if (e.keyCode === 39) {
baseI = canonical(baseI + 1);
drawLattice(parseInt(mouseX / 3, 10), parseInt(mouseY / 3, 10));
} else if (e.keyCode === 40) {
baseJ = canonical(baseJ + 1);
drawLattice(parseInt(mouseX / 3, 10), parseInt(mouseY / 3, 10));
}
}
// Funkcja mouseMove() odpowiada za ruchy myszy.
// Gdy mysz porusza się o co najmniej trzy piksele w dowolnym kierunku,
// program rozrysowuje inną część siatki współrzędnych. To ten kawałek
// kodu odpowiada za dygotanie obrazu morza, nieba lub elementu canvas,
// wskazujące na szybkie odświeżanie danych na ekranie. Zwyczajowe zachowanie myszy,
// która służy do sterowania ruchami kursora w obszarze ekranu zostaje tu zatem
// zmodyfikowane tak, by ruch myszy podmieniał
// tekst na ekranie, w zaskakujący dla czytelnika, a zarazem trudny
// do kontrolowania sposób:
function mouseMove(e) {
mouseX = e.clientX;
mouseY = e.clientY;
drawLattice(parseInt(mouseX / 3, 10), parseInt(mouseY / 3, 10));
return false;
}
// Funkcja mouseClick() odpowiada za przemieszczanie się w nowy region tekstu
// jeśli kliknie się w pobliżu krawędzi okna przeglądarki. Na nowo rozrysowuje
// ona wtedy siatkę współrzędnych. Pozwalamy przemierzać czytelnikowi
// tę przestrzeń na trzy główne sposoby: albo poprzez pojedyncze ruchy myszy,
// a zatem małymi krokami w ramach funkcji mouseMove omówionej powyżej;
// bądź też krokami (kliknięciami) olbrzyma na krawędzi ekranu, które
// zabierają go daleko od pierwotnej lokacji (odpowiada za to mouseClick
// zdefiniowany poniżej); albo także za pomocą precyzyjnych kroków
// umożliwionych dzięki okienku nawigacyjnemu i wpisywaniu adresu URL,
// które pozwalają na dotarcie do ściśle określonej lokacji.
function mouseClick(e) {
if (mouseX > canvas.width * 2 / 3) {
baseI += parseInt(canvas.width / 3, 10);
} else if (mouseX < canvas.width / 3) {
baseI -= parseInt(canvas.width / 3, 10);
}
if (mouseY > canvas.height * 2 / 3) {
baseJ += parseInt(canvas.height / 3, 10);
} else if (mouseY < canvas.height / 3) {
baseJ -= parseInt(canvas.height / 3, 10);
}
drawLattice(parseInt(mouseX / 3, 10), parseInt(mouseY / 3, 10));
return false;
}
// Funkcja resizeCanvas() pozwala na zmianę rozmiaru elementu canvas wraz ze zmianą
// rozmiaru okna przeglądarki (na przykład, gdy użytkownik maksymalizuje okno
// przeglądarki):
function resizeCanvas(e) {
var div = document.getElementsByTagName('div')[0];
canvas.width = div.scrollWidth;
canvas.height = div.scrollHeight;
context.strokeStyle = "rgba(0,0,128,0.75)";
drawLattice(0, 0);
}
// Funkcja setBase() ustanawia podstawowe koordynaty na siatce współrzędnych,
// gdy takich się jej dostarczy. Innymi słowy wartość zmiennych baseI i baseJ
// pozostaje bez zmian.
function setBase(coords) {
var newI, newJ;
newI = parseInt(coords[0], 10);
newJ = parseInt(coords[1], 10);
if (!isNaN(newI) && !isNaN(newJ)) {
baseI = newI;
baseJ = newJ;
}
}
// Funkcja setup() uruchamia się, gdy strona ładuje się w przeglądarce.
// Inicjalizuje ona element canvas, operatory zdarzeń i inne zadania, które
// uaktywnia się raz, przy starcie programu:
function setup() {
var mouseWheelEvent, params = readCoords();
// Jeśli w okienku URL znajdą się dwie pary koordynatów
// (odczytywane przez readCoords()), aby określić aktualną pozycję
// na siatce program użyje następujących parametrów:
if (params.length === 2) {
setBase(params);
}
canvas = document.getElementsByTagName('canvas')[0];
if (!canvas.getContext) {
return;
}
// Dodanie operatorów zdarzeń dla ruchów myszy, dla kliknięć i dla klawiszy:
canvas.onmousemove = mouseMove;
canvas.onclick = mouseClick;
document.onkeydown = keyDown;
mouseWheelEvent = (/Firefox/i.test(navigator.userAgent)) ?
"DOMMouseScroll" : "mousewheel";
// DLa IE (i Opery w zależności od ustawień czytelnika).
if (document.attachEvent) {
document.attachEvent("on" + mouseWheelEvent, updateWheel);
// Dla przeglądarek WC3.
} else if (document.addEventListener) {
document.addEventListener(mouseWheelEvent, updateWheel, false);
}
// Dodanie funkcji tekstowych do funkcji context:
context = canvas.getContext('2d');
CanvasTextFunctions.enable(context);
changeFontSize(0);
window.onresize = resizeCanvas;
resizeCanvas(null);
markStanza();
}
// Funkcja go() przywoływana jest po naciśnięciu klawisza "enter", po
// czym interakcja kierowana jest w okienko nawigacyjne,
// a koordynaty ulegają odświeżeniu.
function go() {
var textInput, coordPair;
textInput = document.getElementById("coords");
coordPair = textInput.value;
coordPair = coordPair.split(' ').join('');
coordPair = coordPair.split(':').join(',');
setBase(coordPair.split(','));
drawLattice(0, 0);
}
// Oczywistym jest, że literaturę elektroniczną bada się poprzez operowanie nią.
// Badaniem objąć należy nie tylko dane pojawiające się na wyjściu
// ale i sam interfejs.
// Pisząc o Między Reją a Morzem i umieszczając nasze refleksje w kodzie głównym,
// chcieliśmy zachęcić krytyków, by zajrzeli pod powierzchnię interfejsu
// i wzięli pod uwagę poziom kodu. Dzięki temu Ci, którzy zainteresowani
// są estetycznymi i poetyckimi aspektami programowania poznają szczegóły
// literackich i technicznych decyzji, które podejmowaliśmy odnośnie wyglądu
// dzieła, jego interfejsu i ukrytych w nim funkcji.
//
// Choć uważamy, że wiele typów kodów o funkcjach poetyckich, estetycznych i
// humanistycznych zasługuje na uwagę, to chcielibyśmy podkreślić, że praca jaką
// wykonaliśmy nad Między Reją a Morzem, cechując się pewnym podobieństwem
// do projektów spod znaku cyfrowej humanistyki, różni się od nich.
// Pracujemy nad tym, by rozwinąć poetykę komputacyjną. Gdy tworzyliśmy Między
// a Morzem, w większym stopniu zaprzątała naszą uwagą poiesis, w mniejszym zaś
// analiza samego tekstu. W niniejszej
// edycji -"ustawmy kurs na czółno przędz" – rozbudowaliśmy projekt,
// próbując pokazać, w jaki sposób można włączać dyskurs krytyczny w warstwę kodu.
// W tym konkretnym przypadku są to glosa przygotowane przez autorów;
// w przyszłości jednak uwagi i komentarze mogą
// być również dopisywane przez krytyków, wydawców i kuratorów.
//
// Zmierzając do konkluzji: najbardziej użytecznym rodzajem krytyki jest taka,
// która zakłada nowy układ w obrębie zastanych elementów.
// Z jednej strony cel ten można osiągnąć poddając rekonfiguracji plik z kodem
// źródłowym dzieła, dodając doń uwagi i objaśnienia, których autorami mogą być
// zarówno sami twórcy jak i krytycy. Natomiast z drugiej,
// i najpewniej całkiem świeżej, ów nowy układ elementów wyłania się
// właśnie z poetyki komputacyjnej i z kodu, który jest jej wynikiem.
// Dzięki nim nowopowstały układ
// ma jednocześnie szansę na szerszą niż zazwyczaj dystrybucję.