Issue #1

Gatherer advanced search wydaje się być zepsute, i to od jakiegoś czasu (zgaduję, że od co najmniej pół roku). Nie byłem w stanie znaleźć nikogo, kto by o tym mówił przez Google. To sprawiło, że zacząłem się zastanawiać, czy robię coś źle, czy nikt inny tego nie zauważył, czy po prostu nie szukałem wystarczająco dobrze, aby znaleźć wcześniejsze dyskusje. Postanowiłem przeprowadzić pewne dochodzenie.

Dwa główne problemy dla Gatherer jak ja je widzę.

  1. Zrób zaawansowane wyszukiwanie z dwoma lub więcej kryteriami (np. wyszukiwanie wszystkich kart Niebieskich, Czarnych i Czerwonych. Następnie kliknij przycisk x obok jednego z tych kryteriów na stronie z wynikami wyszukiwania. Kryterium zniknie z listy, ale wyniki kart nie zostaną zaktualizowane.
  2. Wykonaj wyszukiwanie zaawansowane z jednym lub więcej kryteriami (np. Kolor: zawiera Niebieski). Następnie kliknij „Uściślij wyszukiwanie”. Poprzednie parametry nie pojawiają się w polu kryteriów wyszukiwania zaawansowanego.

Wydaje mi się, że pamiętam, że w przeszłości trafiłem na trzeci problem, coś związanego z wypełnianiem kryteriów na stronie wyszukiwania zaawansowanego, ale teraz nie pamiętam, co to było.

Testowałem to w wielu przeglądarkach (Google Chrome, Mozilla Firefox i Microsoft Edge). Przetestowałem to na wielu urządzeniach (laptop, dwa komputery stacjonarne i telefon). Przetestowałem to na wielu systemach operacyjnych (Windows 10, Ubuntu 16.04, Ubuntu 18.04, Android 8, Android 9).

Jak najlepiej mogę powiedzieć, patrząc tylko na kod po stronie klienta, uważam, że jest to problem z tym, jak odpowiedź serwera jest obsługiwana przez Gatherer UI (i być może problem z tym, jak odpowiada serwer).

W AdvancedSearch.js::AdvSearchConditionCallback, linia 872, możemy zobaczyć jak Gatherer UI oczekuje obsługi odpowiedzi z serwera:

var results = eval("(" + transport.responseText + ")");

Odpowiedź, którą wysyła serwer to JSON. Na przykład, jeśli wykonam wyszukiwanie wszystkich kart Niebieskich, Czarnych i Czerwonych, a następnie kliknę x, aby usunąć kryterium „DOES contain Red”, odpowiedź, którą otrzymam to:

{ "LCID": 0, "CommandName": null, "CommandListPath": null, "SearchTerms": null, "SubQueryDelimiter": "Union", "SearchTermDelimiter": "And", "EscapeInputDelimiters": true, "EncodedSerializedQuery": "YwBvAGwAbwByAD0AKwBbAFUAXQArAFsAQgBdAA==", "ExtraSafeKeys": , "IncludeSpecial": false, "Filters": { "color": { "Identifier": "fe329dcb-d64a-4939-bfd6-7af5c102d655", "LogicalOperator": "Intersect", "From": "color", "Terms": ", "LogicalOperator": "And", "State": "+" }, { "Identifier": "b622e49d-87de-439f-ac75-0e99ffee07c6", "FormatString": "color", "Value": "", "LogicalOperator": "And", "State": "+" } ], "State": null, "FormatString": "color", "Value": ", "LogicalOperator": "And", "State": "+" }, { "Identifier": "b622e49d-87de-439f-ac75-0e99ffee07c6", "FormatString": "color", "Value": "", "LogicalOperator": "And", "State": "+" } ] } }, "State": "color=++"} 

Zauważ, że JSON nie wydaje się mieć żadnych informacji dotyczących tego, jakie karty powinny być wyświetlane, i nie widzę żadnego kodu w AdvancedSearch.js, który obsługiwałby listę kart.

Więc, wywołanie eval("(" + transport.responseText + ")") na tym działa dobrze, konwertując ciąg JSON na obiekt JSON. AdvancedSearch.js następnie używa tego obiektu JSON do aktualizacji wyświetlania pola kryteriów wyszukiwania. Jednakże, nie aktualizuje on bieżącego adresu URL (aby przejść z „?action=advanced&color=+++” do „?action=advanced&color=++”, na przykład).

Teraz, Prototype.js jest skonfigurowany w taki sposób, że będzie również próbował ocenić odpowiedź. Ale, Prototype.js nie jest napisany tak, aby zakładać, że odpowiedź jest JSON. Tak więc, w Prototype.js::respondToReadyState (linia 1498), Prototype.js próbuje eval ten ciąg JSON jako JavaScript (w evalResponse (linia 1533)), a ponieważ ciąg JSON nie jest zamknięty w nawiasach, eval nie powiedzie się:

SyntaxError: Unexpected token : at klass.evalResponse (Prototype.js:1533) at klass.respondToReadyState (Prototype.js:1501) at klass.onStateChange (Prototype.js:1436) at XMLHttpRequest.<anonymous> (Prototype.js:291)

Warto jednak zauważyć, że nawet jeśli zmodyfikujesz Prototype.js, aby zamknąć łańcuch w nawiasach, to po prostu konwertuje on łańcuch na obiekt JSON, a następnie nic z nim nie robi. Również, jeśli zmodyfikujesz AdvancedSearch.js, aby skonfigurować Prototype.js tak, aby nie oceniał odpowiedzi, po prostu przejdzie dalej, a wynik końcowy jest taki sam.

Tak więc, o ile mogę powiedzieć, istnieją dwa możliwe problemy:

  1. The Gatherer UI (i AdvancedSearch.js w szczególności) ma zmienić bieżący adres URL, nawigując do nowych wyników wyszukiwania.
  2. The Gatherer UI ma odpytywać zwrócony adres URL w tle (lub być może serwer ma zawierać wszystkie karty w odpowiedzi), a następnie dynamicznie aktualizować listę kart.

Jeśli #1 jest w tym przypadku, to cała logika dynamicznego aktualizowania listy kryteriów wyszukiwania byłaby bezcelowa; jak tylko przeglądarka przejdzie do nowego adresu URL, załaduje całkowicie nową stronę, z już wypełnioną listą kryteriów i wyświetlonymi nowymi kartami. To również sprawiłoby, że pierwsze zapytanie (i odpowiedź z serwera) byłoby bezcelowe, ponieważ UI ma już informacje zwrócone przez serwer i może po prostu nawigować bezpośrednio do tego adresu URL.

Jeśli #2 jest w tym przypadku, uważam, że jest prawdopodobne, że serwer ma problem, w którym powinien zwrócić wynikową listę kart w pierwszej kolejności. Jeśli nie, ponownie, to pierwsze zapytanie wydaje się bezcelowe, ponieważ odpowiedź nie zawiera niczego, czego UI już nie miał. Tak czy inaczej, UI ma problem z tym, że nie posiada (najwyraźniej) żadnego kodu do dynamicznego aktualizowania listy kart.

Ten problem jest jeszcze bardziej zagmatwany, ponieważ nie zawsze się zdarza. Czasami, gdy klikniesz „Refine Search”, działa to poprawnie. Jednak przez większość czasu tak nie jest. Dla tych, którzy nie są w branży rozwoju oprogramowania, jest to prawdopodobnie to, co jest znane jako „Warunek wyścigu”: dwie rzeczy dzieją się jednocześnie, a wynik końcowy zależy od tego, w jakiej kolejności się kończą. (To trochę jak stos w Magii, gdzie kolejność dwóch efektów może mieć drastycznie różne wyniki).

Oto co widzę jako sedno problemu.

Użyjmy naszego powyższego przykładu ponownie, wyszukiwania wszystkich niebieskich, czarnych i czerwonych kart. Jeśli sprawdzisz przycisk „Rozszerz wyszukiwanie”, zobaczysz to:

<a href="/Pages/Advanced.aspx?action=advanced&amp;color=+++" class="refineSearchLink">Refine Search</a>

Tak więc, to powinno być dość proste. Przejdź z powrotem do strony wyszukiwania zaawansowanego z tymi samymi kryteriami wyszukiwania. Problem polega na tym, co tak naprawdę się z tym dzieje. Jeśli przejdziesz do strony z odnośnikiem, czy klikniesz przycisk „Refine Search”, czy ten link dodany w tym poście, czy też wkleisz URL w pasku adresu, to (zazwyczaj) nie działa; strona wraca bez wypełnionych kryteriów wyszukiwania. Co jest dla mnie interesujące to fakt, że, ponownie, wydaje się, że jest to problem z serwerem lub UI (być może oba).

Jeśli przejdziesz do zakładki Sieć w narzędziach deweloperskich twojej przeglądarki i przejdziesz do tej strony, pierwszym wpisem, który pojawia się na liście aktywności jest żądanie „Pages/Advanced.aspx?action=advanced&color=+++”. Jeśli klikniesz, aby wyświetlić odpowiedź, zobaczysz pełny HTML strony, tak jak pojawiła się ona przy pierwszym zwróceniu. W miejscu, w którym powinny być wypełnione kryteria wyszukiwania, znajdujemy

<div class="filters" ><span style="display: none;">None yet.</span>

Pod tym znajduje się wywołanie skryptu, który wydaje się być przeznaczony do wypełnienia tej listy:

<script type="text/javascript">RetrieveCurrentSearchConditions();</script>

Patrząc na RetrieveCurrentSearchConditions w AdvancedSearch.js, próbuje on wykonać żądanie Ajax z następującymi parametrami (linia 406):

parameters: { cacheBust: new Date().getTime(), encodedSerializedPrevious: $(ClientIDs.encodedSerializedParameters).value }

Problem polega na tym, że jeśli przełamiesz punkt na tej linii (lub po prostu przeczytasz przez surową odpowiedź HTML z serwera), element, do którego odnosi się encodedSerializedParameters, nie ma wartości w tej odpowiedzi. Tak więc, następnie wykonujemy to żądanie z encodedSerializedParameters: "", i my, przewidywalnie, otrzymujemy z powrotem puste filtry wyszukiwania. Więc dlaczego tak się dzieje? Cóż, jeśli wrócisz do wyników wyszukiwania i przejrzysz źródło, przekonasz się, że encodedSerializedParameters ma wartość: YwBvAGwAbwByAD0AKwBbAFUAXQArAFsAQgBdACsAWwBSAF0A (kodowanie base 64 dla „color=+++”).

Więc, wygląda na to, że ten kod został napisany oczekując, że zostanie wywołany jeszcze na stronie z wynikami wyszukiwania, ale tak naprawdę nie jest wywoływany dopóki przeglądarka nie przejdzie z powrotem na stronę z zaawansowanym wyszukiwaniem.

Więc, wydaje się, że serwer powinien odpowiadać z polem value elementu encodedSerializedParameters już wypełnionym, ale tak nie jest. Lub, UI ma w jakiś sposób zebrać tę wartość przed nawigacją i przekazać ją przez nawigację do wykorzystania na następnej stronie. Lub, ewentualnie, UI ma mieć funkcję wyciągania informacji o wyszukiwaniu z adresu URL (gdzie informacje o zapytaniu są wypełnione) i dodawania ich do pola kryteriów wyszukiwania.

Wiem, że wszystko to działało w pewnym momencie, więc nie mam pojęcia, jak taki zepsuty kod został wydany i jak pozostał zepsuty przez tak długi czas. Tuż przed wysłaniem tego posta, postanowiłem przeszukać r/magicTCG i znalazłem posty, które mogą odnosić się do tych samych problemów (np. Gatherer’s 'Advanced Search’ search criteria resets semi-randomly. Help?, Trouble with Gatherer Advanced Search), i byłem zaskoczony, że są one tak stare (1 rok, 3 lata odpowiednio dla powyższych przykładów). Więc jeśli odnoszą się do tych samych problemów, to są one już od dłuższego czasu.

Więc pytanie brzmi, czy Wizards of the Coast po prostu porzuciło Gatherera? (A przynajmniej jego zaawansowane wyszukiwanie?)

.

Leave a Reply