Zapisywanie danych w UserJS
23 maja 2007

Jakiś czas temu pragnąc napisać kolejny skrypt użytkownika (uwielbiam modyfikować strony via UserCSS / UserJS) otworzyłem about:config, aby sprawdzić wartość jednej zmiennej.
Jakież było moje zdziwienie, gdy po kilkunastu sekundach Firefox oznajmił mi, że skrypt na tej stronie może nie odpowiadać. Wybrałem jednak kontynuowanie pracy i wreszcie po kolejnym oczekiwaniu pojawił się spis. Byłem ciekaw o co chodzi i sprawdziłem w profilu plik prefs.js przechowujący wpisy z about:config. Rozmiar przekraczał 15MB!
Okazało się, że wszystkie większe skrypty zapisywały w preferencjach dane pod postacią zmiennych. Rekordzistami okazały się skrypty parsujące XML-e z Last.fm.
Zapisywanie i odczytywanie zmiennych w Greasemonkey jest proste jak drut - służą do tego funkcje GM_getValue i GM_setValue. Wygląda na to, że z powodu tej prostoty są nadużywane. Podany wyżej przykład dobrze ilustruje czemu nie należy traktować pliku preferencji jako własnej bazy danych.
Alternatywa - globalStorage
Czego można używać w zamian? Wprowadzonej w Firefoksie 2 metody na przechowywanie danych globalStorage – zaczerpniętego ze szkicu HTML5 1.
GlobalStorage definiuje przestrzeń dla każdej domeny, w której możemy zapisywać dowolne dane - będą one zachowane do momentu skasowania, a dostęp do nich ma każdy skrypt działający w obrębie tej domeny.
var glStorage = globalStorage.namedItem(document.domain);
Taki kod stworzy nam obiekt, do którego możemy wkładać i wyjmować dane. W przypadku skryptów użytkownika, często działających symultanicznie na jednej stronie zaleca się dodawanie specyficznych prefiksów. Przykładowo:
var namespace = 'my.script.globalstorage.test' + '.';var glStorage = globalStorage.namedItem(namespace + document.domain);
Specjalnie zaznaczyłem obecność ostatniej kropki, ponieważ jej usunięcie zaskutkuje błędem 2. Do zapisania danych służy setItem(), do odczytania getItem(), a usunięcia removeItem().
glStorage.setItem('linki-nowe-okno', 'tak'); //klucz, wartośćglStorage.getItem('linki-nowe-okno', 'nie'); //klucz, wartość domyślnaglStorage.removeItem('linki-nowe-okno'); //klucz
Klucz to inaczej adres pod który się udajemy w tym dużym zbiorze danych, aby wykonać operacje na jednej z nich.
UserJS = Greasemonkey?
Mam już sposób na wygodne zapisywanie danych, ale zastanowiłem się czy to wszystko czego potrzebuję. Co będzie, gdy ktoś uruchomi mój skrypt na Firefoksie 1.5? Albo na Operze? Potrzeba większej elastyczności. Postanowiłem więc napisać uniwersalne funkcje korzystające progresywnie z trzech sposobów zapisu danych.
globalStorageGM_getValue,GM_setValuedocument.cookie
Zadeklarowany wcześniej namespace nam się przyda także do 2 i 3 punktu (nazwy na pewno nie będą się powtarzać). Nie będę wnikał w strukturę całego skryptu, zwłaszcza że jest dość przejrzysty – obejrzyjcie go sami:
Pobierz: globalstorage.user.js
Kod tworzy 3 funkcje - setValue(), getValue() i remValue() do wykorzystania w jakimkolwiek UserJS.
Zobaczyć, że działa (prosty zapis i odczytanie wartości) możecie na stronie testowej.
Dyskusyjne może być użycie funkcji Gmk dla Fx 1.5 i niższych - ale dodałem ten kod, bo zależy w sumie co przechowujemy i jak dużo tych danych jest. Gdyby okazało się, że potrzebujemy dla skryptu prawie bazy danych, to polecam zrezygnować z wspierania poprzednich wersji Firefoksa. No i tym bardziej ciasteczek. A Opera mogłaby udostępnić jakiś inny mechanizm zapisu danych (choćby userData MS).
Tak czy inaczej, uważam że preferencje przeglądarki nie powinny być zaśmiecane wpisami skryptów i twórcy Greasemonkey poszli na łatwiznę. Prośba, abyście Wy nie szli. :)


