Issue #1

Gatherers avancerede søgning virker ødelagt, og det har den været i et stykke tid (jeg vil gætte på mindst seks måneder). Jeg var ikke i stand til at finde nogen, der taler om det gennem Google. Det fik mig til at tænke på, om jeg gjorde noget forkert, eller om ingen andre har bemærket det, eller om jeg bare ikke søgte godt nok til at finde tidligere diskussioner. Jeg besluttede mig for at foretage nogle undersøgelser.

De to hovedproblemer for Gatherer, som jeg ser dem:

  1. Gør en avanceret søgning med to eller flere kriterier (f.eks. en søgning efter alle blå, sorte og røde kort. Klik derefter på x ud for et af disse kriterier på siden med søgeresultater. Kriteriet forsvinder fra listen, men kortresultaterne opdateres ikke.
  2. Gør en avanceret søgning med et eller flere kriterier (f.eks. Farve: Indeholder Blå). Klik derefter på “Forfine søgning”. De tidligere parametre vises ikke i feltet for avancerede søgekriterier.

Jeg mener at kunne huske at have ramt et tredje problem tidligere, noget vedrørende udfyldning af kriterierne i den avancerede søgeside, men nu kan jeg ikke huske, hvad det var.

Jeg har testet dette i flere browsere (Google Chrome, Mozilla Firefox og Microsoft Edge). Jeg har testet dette på flere enheder (en bærbar computer, to stationære computere og en telefon). Jeg har testet dette på flere operativsystemer (Windows 10, Ubuntu 16.04, Ubuntu 18.04, Android 8, Android 9).

Så vidt jeg kan se ved kun at kigge på klientsidens kode, tror jeg, at dette er et problem med den måde, hvorpå serverresponset håndteres af Gatherer UI (og muligvis også et problem med den måde, hvorpå serveren reagerer).

I AdvancedSearch.js::AdvSearchConditionCallback, linje 872, kan vi se, hvordan Gatherer UI forventer at håndtere svaret fra serveren:

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

Svaret, som serveren sender her, er JSON. Hvis jeg f.eks. foretager en søgning efter alle blå, sorte og røde kort og derefter klikker på x for at fjerne kriteriet “DOES contain Red”, er det svar, jeg får:

{ "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=++"} 

Bemærk, at JSON’en ikke ser ud til at indeholde nogen oplysninger om, hvilke kort der skal vises, og jeg kan ikke se nogen kode i AdvancedSearch.js, der kan håndtere en liste over kort.

Så at kalde eval("(" + transport.responseText + ")") på dette virker fint og konverterer JSON-strengen til et JSON-objekt. AdvancedSearch.js bruger derefter dette JSON-objekt til at opdatere visningen af feltet med søgekriterier. Det opdaterer dog ikke den aktuelle URL (for at gå fra “?action=advanced&color=+++” til “?action=advanced&color=++”, f.eks.).

Nu er Prototype.js konfigureret på en sådan måde, at den også forsøger at evaluere svaret. Men Prototype.js er ikke skrevet til at antage, at svaret er JSON. Så i Prototype.js::respondToReadyState (linje 1498) forsøger Prototype.js at eval denne JSON-streng som JavaScript (i evalResponse (linje 1533)), og da JSON-strengen ikke er omsluttet af parenteser, mislykkes evalueringen:

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)

Det er dog værd at bemærke, at selv hvis du ændrer Prototype.js til at omslutte strengen i parenteser, konverterer den blot strengen til et JSON-objekt og gør derefter intet med den. Hvis du ændrer AdvancedSearch.js for at konfigurere Prototype.js til ikke at evaluere svaret, går den også bare videre, og slutresultatet er det samme.

Så vidt jeg kan se, er der to mulige problemer:

  1. Det er meningen, at Gatherer UI (og især AdvancedSearch.js) skal ændre den aktuelle URL og navigere til de nye søgeresultater.
  2. Det er meningen, at Gatherer UI skal forespørge den returnerede URL i baggrunden (eller måske er det meningen, at serveren skal inkludere alle kortene i svaret), og derefter dynamisk opdatere kortlisten.

Hvis #1 er tilfældet, så ville al logikken til dynamisk opdatering af listen over søgekriterier være meningsløs; så snart browseren navigerer til den nye URL, vil den indlæse en helt ny side, hvor listen over kriterier allerede er udfyldt og de nye kort vises. Dette ville også gøre den første forespørgsel (og svaret fra serveren) meningsløs, da brugergrænsefladen allerede har de oplysninger, som serveren returnerer, og bare kan navigere direkte til denne URL.

Hvis #2 er tilfældet, tror jeg, at det er sandsynligt, at serveren har et problem, hvor det er meningen, at den skal returnere den resulterende kortliste i første omgang. Hvis ikke, virker denne første forespørgsel igen meningsløs, da svaret ikke indeholder noget, som UI ikke allerede havde i forvejen. Uanset hvad, så har UI et problem, hvor den (tilsyneladende) alligevel ikke har nogen kode til dynamisk opdatering af kortlisten.

Dette her er endnu mere indviklet, fordi det ikke altid sker. Nogle gange, når man klikker på “Refine Search”, virker det korrekt. Men for det meste af tiden gør det ikke. For dem, der ikke er i softwareudviklingsbranchen, er dette sandsynligvis det, der er kendt som en “Race Condition”: to ting sker samtidig, og slutresultatet afhænger af, i hvilken rækkefølge de afsluttes. (Det er lidt ligesom stakken i Magic, hvor rækkefølgen af to effekter kan give drastisk forskellige resultater).

Her er, hvad jeg ser som problemets kerne.

Lad os bruge vores ovenstående eksempel igen, med en søgning efter alle blå, sorte og røde kort. Hvis du inspicerer knappen “Refine Search”, vil du se dette:

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

Det burde altså være ret ligetil. Naviger tilbage til den avancerede søgeside med de samme søgekriterier. Problemet er, hvad der rent faktisk sker med det. Hvis du går til den linkede side, uanset om du klikker på knappen “Refine Search” eller på dette link, der er tilføjet i dette indlæg, eller om du copy-paste URL’en i adresselinjen, virker dette (normalt) ikke; siden kommer tilbage uden at have udfyldt nogen søgekriterier. Det interessante for mig er, at det igen ser ud til at være et problem med enten serveren eller brugergrænsefladen (muligvis begge dele).

Hvis du går til fanen Netværk i din browsers udviklerværktøjer og navigerer til denne side, er den første post, der vises i aktivitetslisten, anmodningen til “Pages/Advanced.aspx?action=advanced&color=+++”. Hvis du klikker for at få vist svaret, vises hele HTML-koden for siden, som den så ud, da den blev sendt tilbage første gang. Hvor søgekriterierne skulle udfyldes, finder du

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

Under det er der et kald til et script, der tilsyneladende skal udfylde denne liste:

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

Hvis man kigger på RetrieveCurrentSearchConditions i AdvancedSearch.js, forsøger det at lave en Ajax-forespørgsel med følgende parametre (linje 406):

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

Problemet er, at hvis du foretager et breakpoint på denne linje (eller blot læser det rå HTML-svar fra serveren), har det element, som encodedSerializedParameters henviser til, ikke en værdi i dette svar. Så vi foretager derefter denne forespørgsel med encodedSerializedParameters: "", og vi får, som forventet, tomme søgefiltre tilbage. Så hvorfor sker dette? Tja, hvis du går tilbage til søgeresultaterne og ser kilden, vil du opdage, at encodedSerializedParameters har en værdi: YwBvAGwAbwByAD0AKwBbAFUAXQArAFsAQgBdACsAWwBSAF0A (base 64-kodning for “color=+++”).

Det ser altså ud til, at denne kode er skrevet i forventning om at blive kaldt, mens den stadig er på siden med søgeresultater, men så bliver den faktisk ikke kaldt, før browseren allerede har navigeret tilbage til siden med den avancerede søgning.

Det ser altså ud til, at det er meningen, at serveren skal svare med value-feltet i encodedSerializedParameters-elementet allerede udfyldt, men det er den ikke. Eller også er det meningen, at brugergrænsefladen på en eller anden måde skal indsamle denne værdi, inden den navigerer væk, og sende den gennem navigationen for at blive brugt på den næste side. Eller også skal brugergrænsefladen have en funktion til at hente disse søgeoplysninger fra URL’en (hvor forespørgselsoplysningerne er udfyldt) og tilføje dem til feltet med søgekriterier.

Jeg ved, at alt dette fungerede på et tidspunkt, så jeg har ingen anelse om, hvordan en så ødelagt kode blev frigivet, og hvordan den er forblevet ødelagt i så lang tid. Lige før jeg postede dette, besluttede jeg mig for at lave en søgning specifikt på r/magicTCG, og jeg fandt indlæg, som måske refererer til de samme problemer (f.eks. Gatherers “Advanced Search” søgekriterier nulstilles semi-randomly. Help?, Trouble with Gatherer Advanced Search), og jeg blev overrasket over at se, at de er så gamle (henholdsvis 1 år og 3 år for ovenstående eksempler), og jeg blev overrasket over at se, at de er så gamle (henholdsvis 1 år og 3 år for ovenstående eksempler). Så hvis de refererer til de samme problemer, har de været ude i lang tid.

Så spørgsmålet er, om Wizards of the Coast bare har opgivet Gatherer? (Eller i det mindste dens avancerede søgning?)

Leave a Reply