summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--ChangeLog34
-rw-r--r--README11
-rw-r--r--data/docs/10.cs49
-rw-r--r--data/docs/2.cs184
-rw-r--r--data/docs/318
-rw-r--r--data/docs/3.cs138
-rw-r--r--data/docs/435
-rw-r--r--data/docs/4.cs36
-rw-r--r--data/docs/5.cs82
-rw-r--r--data/docs/616
-rw-r--r--data/docs/6.cs89
-rw-r--r--data/docs/763
-rw-r--r--data/docs/7.cs92
-rw-r--r--data/docs/9.cs74
-rw-r--r--data/docs/index.cs14
-rw-r--r--data/docs/notfinished.cs4
-rw-r--r--data/global.pl93
-rw-r--r--data/lang.txt2477
-rw-r--r--data/script.js1902
-rw-r--r--data/style.css111
-rw-r--r--lib/LangFile.pm145
-rw-r--r--lib/Multi/Anime.pm13
-rw-r--r--lib/Multi/IRC.pm8
-rw-r--r--lib/Multi/Maintenance.pm35
-rw-r--r--lib/Multi/RG.pm296
-rw-r--r--lib/VNDB/DB/Discussions.pm12
-rw-r--r--lib/VNDB/DB/Misc.pm40
-rw-r--r--lib/VNDB/DB/Producers.pm37
-rw-r--r--lib/VNDB/DB/Releases.pm14
-rw-r--r--lib/VNDB/DB/Users.pm28
-rw-r--r--lib/VNDB/DB/VN.pm13
-rw-r--r--lib/VNDB/Func.pm4
-rw-r--r--lib/VNDB/Handler/Discussions.pm7
-rw-r--r--lib/VNDB/Handler/Misc.pm13
-rw-r--r--lib/VNDB/Handler/Producers.pm160
-rw-r--r--lib/VNDB/Handler/Releases.pm96
-rw-r--r--lib/VNDB/Handler/Tags.pm45
-rw-r--r--lib/VNDB/Handler/ULists.pm20
-rw-r--r--lib/VNDB/Handler/Users.pm6
-rw-r--r--lib/VNDB/Handler/VNBrowse.pm6
-rw-r--r--lib/VNDB/Handler/VNEdit.pm45
-rw-r--r--lib/VNDB/Handler/VNPage.pm71
-rw-r--r--lib/VNDB/L10N.pm115
-rw-r--r--lib/VNDB/Plugin/TransAdmin.pm349
-rw-r--r--lib/VNDB/Util/Auth.pm4
-rw-r--r--lib/VNDB/Util/CommonHTML.pm66
-rw-r--r--lib/VNDB/Util/LayoutHTML.pm20
-rw-r--r--static/f/forms.js955
-rw-r--r--static/f/icons.pngbin9403 -> 9474 bytes
-rw-r--r--static/f/script.js635
-rwxr-xr-xutil/dbgraph.pl8
-rw-r--r--util/dump.sql120
-rwxr-xr-xutil/init.pl2
-rwxr-xr-xutil/jsgen.pl97
-rwxr-xr-xutil/lang.pl167
-rwxr-xr-xutil/skingen.pl18
-rw-r--r--util/updates/update_2.8.sql213
-rwxr-xr-xutil/vndb.pl19
59 files changed, 7201 insertions, 2225 deletions
diff --git a/.gitignore b/.gitignore
index 59f221ff..e2a730fd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,9 +1,9 @@
/data/config.pl
/data/log/
+/static/f/script.js
/static/s/*/style.css
/static/s/*/boxbg.png
/static/cv/
-/static/rg/
/static/sf/
/static/st/
/static/robots.txt
diff --git a/ChangeLog b/ChangeLog
index 2a269f21..299ade41 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,37 @@
+2.8 - 2009-10-24
+ - Converted relation graphs to use inline SVG
+ - Relation graphs now use the color scheme of selected skin
+ - VN relations are translatable in both the interface and the graphs
+ - Full date is displayed in graphs instead of only month/year
+ - Converted to ENUM data type:
+ - vn_relations.relation
+ - anime.type
+ - changes.type
+ - releases_rev.type
+ - releases_media.medium
+ - New language: Hungarian
+ - Complete rewrite of the Javascript code:
+ - Intended to be less error prone, more maintainable, and easier to make
+ 'XHTML compliant' in the future (currently still has some issues here).
+ - Improved spoiler selection on /v+/tagmod
+ - Everything merged into one file.
+ - Optionally minified (using JavaScript::Minifier::XS)
+ - Language strings are translatable
+ - Information is automatically synchronised with data/global.pl
+ - Changed language selector into a Javascript dropdown
+ - Added producer role (developer/publisher) to releases
+ - Display number of unread posts in "My messages" (instead of total threads)
+ - Optimized dbUserGet (mostly for the user list)
+ - All languages are listed on /r and /v/all instead of only those in use
+ - Copy over search query when switching search type (htmlSearchBox)
+ - Fixed obscure sorting bug on user VN list
+ - Fixed calculation of tags_vn_bayesian.spoiler
+ - Fixed bug with unhiding a producer entry
+ - Set 'no spoilers' as default spoiler level for tags
+ - Added Czech and Hungarian interface translation
+ - Producer relations
+ - Increased tag dropdown search results to 15
+
2.7 - 2009-09-24
- Improved styling of the threeboxes layout
- Blacklist a users' votes from the VN vote statistics
diff --git a/README b/README
index 5177b6e2..e62f0e06 100644
--- a/README
+++ b/README
@@ -12,7 +12,7 @@ Requirements
global requirements:
Linux, or an OS that resembles Linux. Chances are VNDB won't run on Windows.
- PostgreSQL 8.3 recommended, 8.1 may also work
+ PostgreSQL 8.3+
perl 5.10 recommended, 5.8 may also work
A webserver that works with YAWF (lighttpd and Apache are known to work)
@@ -41,11 +41,20 @@ Requirements
Maintenance:
PerlIO::gzip
RG:
+ XML::Parser
+ XML::Writer
graphviz (/usr/bin/dot is used by default)
Sitemap:
XML::Writer
PerlIO::gzip
+ util/skingen.pl
+ Image::Magick
+ CSS::Minifier::XS (optional, minimizes CSS output)
+
+ util/jsgen.pl
+ JavaScript::Minifier::XS (optional, minimizes JS output)
+
Contact
diff --git a/data/docs/10.cs b/data/docs/10.cs
new file mode 100644
index 00000000..d8eebb52
--- /dev/null
+++ b/data/docs/10.cs
@@ -0,0 +1,49 @@
+:TITLE:Tagovací systém
+:INC:notfinished
+:INC:index
+
+
+:SUB:Úvod
+<i>TODO</i>
+
+
+:SUB:Hodnocení tagů
+<p>
+ I když celkem vzato může kdokoliv uvést jakýkoliv tag k jakékoliv vizuální novele, pamatujte prosím na následující věci,
+ pokud tagy hodnotíte:
+</p>
+<ul>
+ <li>Před přidáním tagu k nějaké vizuální novele si přečtěte jeho popis, některé tagy nemusí znamenat to,
+ co si myslíte, že znamenají.</li>
+ <li>Buďte tak přesní, jak jen můžete být. Kupříkladu, pokud má hra dramatické prvky, zkuste spíše najít
+ více specifické odvozené tagy, které popisují jaký typ dramatických událostí může hráč očekávat,
+ než přidat tag "Drama".</li>
+ <li>Tagu může být přiděleno hodnocení v rozsahu -3 až 3. Negativní hodnocení znamená, že s tagem
+ nesouhlasíte, kde -1 bude "Nemyslím si, že tento tag je opravdu ve hře" a -3 bude znamenat, že plně
+ nesouhlasíte a tag by neměl být použit vůbec. Pozitivní hodnocení tagu funguje naopak a znamená, že
+ s tagem pro danou vizuální novelu souhlasíte.</li>
+ <li>Některé tagy se mohou chovat jako spoiler co se děje VN týká, to může být označeno ohodnocením spoiler
+ statusu. Je vysoce doporučeno nenechávat tento jako "Neutral", a opravdu se snažit poskytnout nějaké vodítko
+ ohledně toho, zda tasg vůbec není spoiler nebo zda vizuální novelu malinko prozrazuje.</li>
+</ul>
+
+
+:SUB:Doporučení k tagům
+<p>
+ Nové tagy si může vyžádat kdokoliv, ale než se dají procházet musí být schváleny moderátorem.
+ Platí následující pravidla:
+</p>
+<ul>
+ <li>Jména tagů musí mluvit za sebe. I když význam tagu může být zřejmý v okamžiku, kdy se podíváte
+ na jeho umístění ve stromu tagů, v okamžiku, kdy je zobrazen na stránce VN, je vidět pouze jeho jméno
+ a ne mateřské tagy. Jméno by tedy mělo plně popisovat co tag znamená a pro co by měl být použit.
+ Například: Tag k popisu mužského hlavního hrdiny by měl být pojmenován plně "Male protagonist" (Muž protagonista),
+ i když jeho mateřský tag je "Characters &gt; Protagonist" (Postavy &gt; Protagonista).</li>
+ <li>Tagy by měly být užívány k popisování objektivních informací o VN. Subjektivní tagy jako
+ "Awesome protagonist" (Úžasný protagonista), které by pro každého znamenaly něco jiného, nejsou povoleny.</li>
+ <li>Tagy by měly popisovat pouze informace, které VNDB ještě nezahrnuje. Tudíž tagy ohledně informací o vydání či
+ o délce VN nejsou povoleny.</li>
+ <li>Výše uvedené pravidlo platí i pro jakékoliv další informace o VN, které ještě nejsou na VNDB zahrnuty,
+ jako lidé v produkčním týmu nebo stav licence v jakékoliv zemi.</li>
+</ul>
+
diff --git a/data/docs/2.cs b/data/docs/2.cs
new file mode 100644
index 00000000..b7d3c7eb
--- /dev/null
+++ b/data/docs/2.cs
@@ -0,0 +1,184 @@
+:TITLE:Přidání/editace vizuální novely
+:INC:index
+
+:SUB:Kdy přidat vizuální novelu
+<p>
+ Aby hra byla zahrnuta v této databázi, musí pro vyprávění příběhu konzistentně
+ používat vyprávěcí styl. Příklady zahrnují popisy <a
+ href="http://s.vndb.org/sf/58/258.jpg">vizuální</a>, <a
+ href="http://s.vndb.org/sf/63/2663.jpg">událostí</a>, <a
+ href="http://s.vndb.org/sf/74/274.jpg">akcí postav</a> nebo <a
+ href="http://s.vndb.org/sf/49/449.jpg">myšlenek</a>. Toto vyprávění musí být
+ jednou ze známých metod prezentace vizuálních novel, jako jsou <a
+ href="http://s.vndb.org/sf/40/3440.jpg">ADV</a>, <a
+ href="http://s.vndb.org/sf/52/3152.jpg">NVL</a> a jejich <a
+ href="http://s.vndb.org/sf/39/339.jpg">obdoby</a>.<br />
+ <br />
+ Hra, která 'působí' jako VN, i když plní jinou funkci než striktně vyprávění
+ příběhu (jako například vysoce interaktivní simulace) se může jako VN kvalifikovat
+ pokud je buď celá vyprávěna v ADV/NVL stylu nebo jsou její další herní prvky
+ jasně podřízeny prvkům textovým (prezentovaným v ADV/NVL stylu) (např. akční scény
+ v 3D hrách nemohou být podřízeny jejich textovým prvkům, jelikož vizuální prvky
+ jsou jasně 'účelem' hry; 'podzemí' ve hrách Princess Maker, která mají jednoduchou
+ grafiku a dají se všechna přeskočit, by podřízena byla).
+</p>
+
+:SUB:Obecné informace
+<dl>
+ <dt>Název (romaji)</dt><dd>
+ Název by měl korespondovat s názvem původního vydání.<br />
+ Pokud název užívá latinky použijte našich <a href="/d5.2">doporučení ke kapitalizaci</a>.<br />
+ Jinak <a href="/d5.1">romanizujte</a> podle našich doporučení.
+ </dd><dt>Originální název</dt><dd>
+ Pokud je název oficiálně odlišný od názvu v romaji (obvykle kvůli odlišným znakovým sadám),
+ umístěte sem originální název.
+ </dd><dt>Aliasy</dt><dd>
+ Vizuální novely mohou být známy pod různými názvy, použijte tedy toto pole pro přidání jakýchkoliv
+ aliasů a akronymů užívaných na internetu. Oficiální názvy vydání by se sem přidávat neměla, protože
+ jsou již zapsána ve vydáních.
+ </dd><dt>Popis</dt><dd>
+ Krátký popis hlavního příběhu.
+ </dd><dt>Délka</dt><dd>
+ Velmi hrubý odhad času potřebného k dohrání všech konců vizuální novely.
+ K určení délky hry je často lepší ignorovat délku branou v čase jako takovém a místo toho
+ ji porovnat s jinými hrami, které jste hráli. Všechno je to přeci jen relativní.
+ </dd><dt>Externí odkazy</dt><dd>
+ Odkazy k externím zdrojům o této vizuální novele. Abyste získali URL, navštivte tyto stránky
+ (<a href="http://en.wikipedia.org/">Wikipedia</a>, <a href="http://novelnews.net/">
+ Novelnews.net</a>, <a href="http://renai.us/">Renai.us</a> a
+ <a href="http://visual-novels.net/">Visual-novels.net</a>),
+ najděte stránku o hře a určete ID nebo jméno této stránky, které je třeba vyplnit
+ do textových polí. Nepište celá URL, potřebujeme jen jejich malou část!
+ </dd><dt>Vztahy k anime</dt><dd>
+ Některé vizuální novely (jako <a href="/v4">Clannad</a> a <a href="/v3">Utawarerumono</a>)
+ mají anime adaptace a některé vizuální novely byly adaptovány z anime sérií. Použijte
+ toto pole pro upřesnění těchto anime se vztahy k vizuálním novelám.<br />
+ Anime by mělo být specifikováno za užití <a href="http://anidb.net/">AniDB</a> ID.
+ Pro přidání anime ho najděte na AniDB a vložte číselné ID anime (dá se najít v
+ <i>aid=xxx</i> části URL) do editovatelného pole. Několik ID by mělo být odděleno mezerou.<br />
+ Pokud má vizuální novela vztah k jiné vizuální novele a obě hry mají anime adaptaci,
+ to samé anime nemusí být přidáno k oběma hrám. Například <a href="/v264">Da Capo</a> má dvě
+ anime adaptace (<a href="http://anidb.net/a837">837</a>, <a href="http://anidb.net/a2832">2832</a>),
+ stejně jako <a href="/v266">Da Capo II</a>. (<a href="http://anidb.net/a5419">5419</a>,
+ <a href="http://anidb.net/a5652">5652</a>) Ale vztahy pro Da Capo II se nemusí přidávat
+ k Da Capu a vice versa - o to se postarají vnitřní vztahy vizuálních novel.<br />
+ VNDB po potvrzení nového AniDB ID automaticky zanese informace o anime a bude je prezentovat
+ na stránce. Tato akce zabere několik minut až - přinejhorším - několik hodin. Po tento čas
+ uvidíte ID anime, ale ne jeho název a odkazy na AnimeNFO a Anime News Network. Není možné
+ tyto informace přidat ručně, budou zaneseny automaticky!
+ </dd>
+</dl>
+
+
+:SUB:Kategorie
+<i>
+ Systém kategorií byl nahrazen tagy. Tato sekce je zde kvůli místu, aby se zachovala posloupnost
+ číslování.
+</i>
+
+
+:SUB:Obrázek
+<p>
+ Každá vizuální novela by měla mít obrázek, preferován je oficiální art z obalu
+ některého vydání. V některých případech, hlavně u doujin her, pokud není žádný
+ oficiální art z obalu dostupný, může být použitý obecný obrázek ze stránek hry
+ nebo screenshot ze hry samotné.<br />
+ Obrázky mohou být nahrány v JPEG nebo PNG formátu a neměly by být větší 500kB.
+ Všechny obrázky větší 256x400 pixelů budou automaticky zmenšeny, aby na stránku
+ pasovaly. Pamatujte, že zmenšení se děje odděleně, takže obrázek neuvidíte
+ hned po nahrání. Tento proces obvykle zabere jen pár vteřin a obrázek by měl být
+ dostupný krátce po potvrzení formuláře.
+ <br /><br />
+ NSFW varování by mělo být použito v případě, kdy obrázek z obalu není vhodný pro
+ prostředí přátelských pracovišť. K určení toho, co je "safe for work" a co ne
+ použijte následující doporučení:
+</p><ul>
+ <li>Nahota je "safe" pouze pokud jsou sexuální znaky zakryty nebo schovány</li>
+ <li>Bikini, pantsu aa ostatní spodní prádlo jsou "safe" jen pokud neobsahují žádné
+ implicitní naznačení pohlavní anatomie</li>
+ <li>Pokud je póza postavy přehnaně sexuálně provokativní, pak je to NSFW</li>
+ <li>Pokud je zobrazeno několik postav a jsou zobrazeny ve fyzickém kontaktu, který
+ by se dal považovat za sexuální kontakt (či předehru k němu), pak je obrázek také NSFW.
+ </li>
+</ul><p>
+ I s těmito doporučeními nemusí být občas jednoduché určit zda je obrázek "safe" nebo ne.
+ Pokud jste v pochybách, je často nejlepší zvolit NSFW.
+</p>
+
+
+:SUB:Vztahy
+<p>
+ Vztahy mezi vizuálními novelami se dají použít k určení toho, jak jsou mezi sebou hry
+ provázány.<br />
+ Když přidáváte vztah, k druhé vizuální novele bude automaticky přidán vztah opačný.
+ Např. pokud označíte hru x jako pokračování ke hře y, pak hra y bude automaticky
+ označena jako prequel ke hře x. Nemusíte editovat obě hry.<br />
+ Specifikujte pouze <b>přímé</b> vztahy. Pokud má hra 1 zapsán nějaký vztah ke hře 2
+ a hra 2 má vztah ke hře 3, pak hra 3 nemusí nutně mít vztah ke hře 1. Toto se může
+ na první pohled zdát matoucí, ale dá se to pochopit z grafu vztahů. Když editujete
+ vydání, vždy se snažte myslet na vztahy mezi všemi hrami, které je mají - jak je
+ ukázáno v grafu - místo toho, abyste se dívali jen na tu jedinou vizuální novelu,
+ kterou editujete.
+ <br />
+ Máme pevný seznam vztahů, ze kterých se dá vybrat (jak je popsán níže). Jako s mnoha
+ vztahy, i ty mezi hrami mohou být ve skutečnosti mnohem složitější, než jak je tyto
+ možnosti mohou popsat. Pokud si nejste jisti, kterou možnost vybrat, pak jedoduše
+ vyberte tu, o které si myslíte, že je nejblíže vyjádření skutečného vztahu.
+</p>
+<dl>
+ <dt>Pokračování</dt><dd>
+ Pokračování příběhu. &lt;=&gt;<i>Prequel</i>.
+ </dd><dt>Prequel</dt><dd>
+ Příběh se odehrává před příběhem originálním.&lt;=&gt;<i>Pokračování</i>.
+ </dd><dt>Stejný setting</dt><dd>
+ Stejný vesmír, svět, realita a časová linie, ale kompletně odlišné postavy.
+ Definici "settingu" není vždy jednoduché definovat, ale obvykle to znamená, že
+ pokud místa či předměty, neexistující v reálném světě, popsané v jedné hře,
+ existují také ve hře druhé, pak se dá použít tento vztah.
+ </dd><dt>Alternativní verze</dt><dd>
+ Stejný setting, stejné postavy, ale příběh je odvyprávěn odlišně.
+ </dd><dt>Sdílí postavy</dt><dd>
+ Odlišný příběh, který ale sdílí některé postavy.
+ </dd><dt>Vedlejší příběh</dt><dd>
+ Příběh se odehrává někdy během příběhu hlavního. &lt;=&gt;<i>Parent story</i>
+ </dd><dt>Hlavní příběh</dt><dd>
+ Opak <i>vedlejšího příběhu</i>.
+ </dd><dt>Stejná série</dt><dd>
+ Hry jsou součástí jedné herní série.
+ </dd><dt>Fandisk</dt><dd>
+ <a href="http://en.wikipedia.org/wiki/Fan_disc">Fandisk</a>.
+ </dd><dt>Originální hra</dt><dd>
+ Opak fandisku.
+ </dd>
+</dl>
+
+
+:SUB:Screenshoty
+Každá jedna vizuální novela může mít až 10 screenshotů. Screenshoty mohou být nahrány v JPEG a PNG
+formátu a měly by vždy být v souladu s těmito doporučeními:
+<ul>
+ <li>Všechny obrázky musí být v nejvyšším původním rozlišení VN. Původní rozlišení je takové rozlišení,
+ pro které jsou dělány bitmapové obrázky, což je 640x480 nebo 800x600 pro většinu vizuálních novel.
+ Jakékoliv zmenšování nebo zvětšování, ať už provedené enginem hry samotné nebo ručně za použití
+ editoru obrázků, <b>není</b> povoleno!</li>
+ <li>Všechny obrázky musí být screenshoty <b>hry samotné</b>, to znamená aby všechny okraje oken byly
+ odstraněny před nahráním. Nezajímají nás vaše těžce modifikované okraje oken ani vaše úžasné rozložení
+ AGTH.</li>
+ <li>Alespoň polovina screenshotů by měly být screenshot přímo ze hry jako takové. Což znamená, že by měly
+ obsahovat sprity postav nebo nějakou formu dialogu nebo uživatelské rozhraní. Jen CG obrázky událostí
+ bez jakéhokoliv textu nebo rozhraní jsou povoleny pro demonstraci stylu krslení, ale neměly by mezi
+ ostatními screenshoty převažovat.</li>
+ <li>Nahrávání screenshotů s erotickým obsahem je povoleno tak dlouho, dokud je poměr erotických a neerotických
+ screenshotů jakžtakž v souladu s poměrem erotického a neerotického obsahu ve VN samotné. Například pokud má
+ desetihodinová VN jen dvě desetiminutové H-scény, pak by asi nebylo patřičné nahrát více jak 2 až tři erotické
+ obrázky. Na druhou stranu, pokud je VN převážně o sexu, pak nebude dávat smysl nahrát erotický screenshot
+ jen jeden. Je doporučeno - i pro hry pouze o sexu - mít alespoň jeden neerotický screenshot.</li>
+ <li>Snažte se o co nejméně spoilerů. Je nemožné nahrát screenshoty, ktré jsou kompletně bez spoilerů, ale
+ je možné snížit množství spoilerů na minimum. Tedy jsou preferovány screenshoty ue začátku VN a záběry
+ z pozdějších částí hry jsou v pořádku pokud neodhalují žádné důležité informace z příběhu.</li>
+ <li>Obrázky, které jsou NSFW by tak měly být označeny, vizte doporučení k <a href="#4">obrázkům</a> výše.</li>
+ <li>Screenshoty musí být neoznačené: neměly by obsahovat copyright, internetové adresy nebo jiný text, který není
+ přítomný v původním screenshotu.</li>
+ <li>Screenshoty by ideálně měly být z anglického nebo z originálního (japonského) vydání. Ostatní jazyky
+ jsou pvoloeny, ale množství těchto obrázků by mělo být zachování na minimu.</li>
+</ul>
diff --git a/data/docs/3 b/data/docs/3
index f3f5fbdf..4ac8ded5 100644
--- a/data/docs/3
+++ b/data/docs/3
@@ -132,8 +132,22 @@
:SUB:Producers
<p>
- The companies/groups/individuals involved in the creation, development or translation
- of this release. Does not include distributors.
+ The companies/groups/individuals involved in the development or publishing of
+ this release. Does not include distributors. The following roles can be selected:
+ <dl>
+ <dt>Developer</dt><dd>
+ The producer involved in the creation of the game itself, not necessarily of
+ this specific release. Keep in mind that producers that have made modifications
+ to a game but have not made the game itself should NOT be listed as developers.
+ </dd><dt>Publisher</dt><dd>
+ The producer responsible for publishing this specific release. The publisher may have
+ made modifications to the game (e.g. translating all text or porting to a different
+ platform), but was not involved in the creation process.
+ </dd><dt>Both</dt><dd>
+ When the release is developed and published by the same producer. This is often
+ true for doujin games and the first releases of commercial games.
+ </dd>
+ </dl>
</p>
diff --git a/data/docs/3.cs b/data/docs/3.cs
new file mode 100644
index 00000000..2e4b5369
--- /dev/null
+++ b/data/docs/3.cs
@@ -0,0 +1,138 @@
+:TITLE:Přidání/editace vydání
+:INC:index
+
+
+:SUB:Kdy přidat vydání
+<p>
+ 'Vydání' je produkt - buď fyzický nebo digitální - obsahující vizuální novelu (i část).
+ To vylučuje soundtracky, drama CD, fandisky a ostatní produkty, které neobsahují
+ vizuální novelu samotnou.<br />
+ Všechna vydání by měla být přidána samostatně. Například limitovaná a běžná edice
+ by neměly být kombinovány do jednoho vydání, i když sdílí datum vydání a obsah.
+ Pro komerční hry se dají jednotlivá vydání rozlišit podle jejich kódu JAN/UPC/EAN.
+</p>
+
+
+:SUB:Obecné informace
+<dl>
+ <dt>Typ</dt><dd>
+ Je vydání kompletní, částečné nebo trial verze?
+ Kompletní vydání obsahuje vše.
+ Částečná vydání obsahují něco ze hry, ale stále jsou věci, které čekají na vydání.
+ Trial verze jsou výrazně zkrácená volná vydání, aby si člověk mohl hru vyzkoušet
+ před její koupí. Občas jsou trial verze sestříhány pro stažení na webu a nepředstavují
+ tak úplně finální produkt.<br />
+ V případě patche pro překlad by měl typ značit zda patch slouží k překladu celé hry
+ (kompletní, complete) nebo pouze jejích částí (částečný, partial).
+ </dd><dt>Patch</dt><dd>
+ Použijte toto zaškrtnutí k označení toho, že vydání je (překladatelským) patchem,
+ použitým k opatchování jiného vydání.
+ </dd><dt>Freeware</dt><dd>
+ Toto zaškrtněte, pokud je hra volně ke stažení (či jinak volně dostupná) za nulovou cenu.
+ </dd><dt>Doujin</dt><dd>
+ Publikováno doujin kruhem, amatérskou skupinou nebo jedincem, v protikladu k právnické osobě,
+ jakou je například firma. Toto pole je ignorováno, pokud je typ vydání patch.
+ </dd><dt>Název (romaji)</dt><dd>
+ Název vydání latinkou (za užití romanizace nebo překladu)
+ </dd><dt>Originální název</dt><dd>
+ Pokud je oficiální název odlišný (obvykle kvůli jiným znakovým sadám), napište sem originální název.
+ </dd><dt>Jazyk</dt><dd>
+ V jakém jazyce je toto vydání? Uveďte jazyk, ve kterém je napsána většina hry.
+ </dd><dt>JAN/UPC/EAN</dt><dd>
+ <a href="http://en.wikipedia.org/wiki/Global_Trade_Item_Number">GTIN</a> kód produktu.
+ Často zvaný "JAN" pro japonská vydání, "UPC" v USA a Kanadě a "EAN" v Evropě. Systém automaticky
+ pozná typ podle kódu a na stránce vydání uvede příslušný termín.
+ </dd><dt>Číslo v katalogu</dt><dd>
+ Číslo v katalogu jak je mu přiřazeno producentem. Často se používá pro identifikaci vydání
+ na webshopech a obvykle se dá najít někde na obalu produktu.
+ </dd><dt>Oficiální internetová stránka</dt><dd>
+ URL oficiálních stránek pro tento produkt.
+ </dd><dt>Datum vydání</dt><dd>
+ Pro komerční hry datum prodeje.
+ Pro vše ostatní datum, kdy bylo vydání poprvé dostupné.
+ Pokud byla hra uvedena na internetové stránce, pak datum příspěvku, který ji oznámil.
+ </dd><dt>Věková přístupnost</dt><dd>
+ Minimální věková přístupnost pro vydání. Pro většinu vydání je toto specifikováno na obalu
+ nebo na internetových stránkách produktuT.
+ </dd><dt>Poznámky</dt><dd>
+ Další různé užitečné věci.
+ Obecně vzato sem patří extra (ale ne předobjednávkové bonusy) a informace o vývoji/překladu.
+ </dd>
+</dl>
+
+
+:SUB:Formát
+<dl>
+ <dt>Rozlišení</dt><dd>
+ Primární/původní rozlišení obrazovky pro hru.
+ </dd><dt>Hlas</dt><dd>
+ Značí, zda toto vydání obsahuje nadabování ve VN/ADV částech hry.
+ <i>Plně s hlasem</i> znamená, že všechny postavy (obvykle až na hlavního hrdinu
+ a některé vedlejší postavy) jsou nadabovány ve všech scénách. <i>S hlasem pouze v ero scénách</i>
+ mluví samo za sebe, a <i>Částečně s hlasem</i> by mělo být použito, pokud je ve hře
+ nějaký dabing, ale pouze pro hlavní postavu nebo pouze v některých scénách.
+ </dd><dt>Animace</dt><dd>
+ To, zda má hra animace se dá upřesnit ve dvou částech: v jedné pro normální příběhovou část
+ a v jedné pro ero scény, pokud je hra má. <i>Jednoduchými animacemi</i> odkazujeme na
+ (obvykle opakující se) efekty jako padající lístky nebo sníh v pozadí nebo animované
+ obličejové výrazy jako mrkání očí a pohyby úst.
+ <i>Plně animovanými scénami</i> pak myslíme neopakující se scény v anime stylu. Některé hry
+ jsou v tomto stylu celé, jiné mají plně animovaných jen několik scén. Efekty jako pohyb
+ postav po obrazovce, základní zoomování a třesení obrázkem v pozadí nebereme jako "animace".
+ Minihry a jiné herní prvky jsou také vyloučeny, uvažujeme zde pouze příběhové a ero části (v ADV/VN stylu).
+ </dd>
+</dl>
+<p>
+ <b>POZNÁMKA</b>: Pole rozlišení, hlasu a animace nemají žádný význam pro patche a pro tato vydání
+ by měla zůstat prázdná.
+ <br /><br />
+ <b>Platforma</b><br />
+ Platformy, pro které byl produkt vydán.Nezahrnuje emulované platformy (jako Playstation 2 hry na Playstationu 3)
+ nebo WINE. DVD Přehrávačem se myslí hry hratelné jako běžné DVD video (DVDPG) a toto by nemělo být zaměňováno
+s DVD jako s médiem.
+ <br /><br />
+ <b>Média</b>
+</p>
+<dl>
+ <dt>Blu-ray</dt><dd>
+ Blu-ray Disk, obvykle 30-60GB+. Vyžaduje Blu-ray přehrávač/jednotku. Playstation 3 hry
+ jsou obvykle Blu-ray.
+ </dd><dt>CD</dt><dd>
+ CD-ROM, obvykle 700MB.
+ </dd><dt>DVD</dt><dd>
+ DVD5, obvykle 4.5GB, nebo DVD9, obvykle 9GB. DVDPG hry jsou na DVD.
+ </dd><dt>Disketa</dt><dd>
+ 5 1/4" nebo 3 3/4", ne větší než 1.44MB.
+ </dd><dt>GD</dt><dd>
+ Hry na Dreamcast jsou obvykle na GD discích.
+ </dd><dt>Stažení z internetu</dt><dd>
+ Cokoliv bez fyzického obalu, tedy získáno stažením přes síť.
+ </dd><dt>Paměťová karta</dt><dd>
+ Jakákoliv varianta SD (Secure Digital) karty, včetně MMC varianty, Compact Flash disku nebo "USB Sticku".
+ Hlavní rozdíl mezi těmito a Cartridgí (níže) je v tom, že paměťová karta je přepisovatelná (RW).
+ </dd><dt>Cartridge</dt><dd>
+ Srovnejte s paměťovou kartou (výše). Pouze pro čtení. Famicom (NES), Super Nintendo (SNES),
+ Game Boy Advanced (GBA) a Nintendo DS používají cartridge.
+ </dd><dt>Nintendo Optický Disk</dt><dd>
+ Optické disky, které nejsou CD ani DVD, a které používají některé konzole Nintendo.
+ </dd><dt>Ostatní</dt><dd>
+ Jakékoliv médium, které se nedá zahrnout pod jakýkoliv typ z výše uvedených by měl použít tuto volbu.
+ Tato by ale neměla být používána jen tak a je možné, že si její užití budete muset obhájit.
+ </dd><dt>UMD</dt><dd>
+ Universal Media Disk, obvykle 2.2GB. Tento formát používá Playstation Portable.
+ </dd>
+</dl>
+
+
+:SUB:Producenti
+<p>
+ Firmy/skupiny/jedinci, kteří se podíleli na tvorbě, vývoji nebo překladu tohoto vydání.
+ Nezahrnuje distributory.
+</p>
+
+
+:SUB:Vztahy k vizuálním novelám
+<p>
+ Vizuální novely, které toto vydání (ať už částečně nebo plně) zahrnuje.
+</p>
+
diff --git a/data/docs/4 b/data/docs/4
index 927b7d2d..45acb927 100644
--- a/data/docs/4
+++ b/data/docs/4
@@ -35,3 +35,38 @@
</dd>
</dl>
+
+:SUB:Relations
+These relations provide information about which producers are related to each
+other, and how they are related. Choosing the correct relation may be a bit
+confusing, check the relation graph of the producer entry in case of doubt.
+The following relations are defined:
+<dl>
+ <dt>Formerly</dt><dd>
+ The current producer was earlier known as the selected producer. This can be
+ because of a name change, or when the earlier producer disbanded and the same
+ people started again under a different name.
+ </dd><dt>Succeeded by</dt><dd>
+ Reverse of <i>Formerly</i>.
+ </dd><dt>Subsidiary</dt><dd>
+ Selected producer is a subsidiary of the current producer. A subsidiary is
+ still "part" of the parent producer, but consist of a different group and
+ publishes under a different name.
+ </dd><dt>Parent producer</dt><dd>
+ Reverse of <i>Subsidiary</i>.
+ </dd><dt>Imprint</dt><dd>
+ Selected producer is an imprint of the current producer. Simply put, an
+ "imprint" is a different name for the same group of people, used when
+ publishing games. See <a href="http://en.wikipedia.org/wiki/Imprint">Wikipedia</a> for more info.
+ </dd><dt>Parent brand</dt><dd>
+ Reverse of <i>Imprint</i>.
+ </dd><dt>Spawned</dt><dd>
+ The selected producer was formed by former members of the current producers.
+ The difference with the <i>Formerly</i> relation is that the producer where the
+ members came from is still alive.
+ </dd><dt>Originated from</dt><dd>
+ Reverse of <i>Spawned</i>.
+ </dd>
+</dl>
+
+
diff --git a/data/docs/4.cs b/data/docs/4.cs
new file mode 100644
index 00000000..2c4644ac
--- /dev/null
+++ b/data/docs/4.cs
@@ -0,0 +1,36 @@
+:TITLE:Přidání/editace producenta
+:INC:index
+
+
+:SUB:Kdy přidat producenta
+<p>
+ Záznam o producentovi by měl být vytvořen, pokud přidáváte vydání vizuální novely vytvořené
+ producentem, který ještě není zapsán v databázi. Producenti, kteří nemají v databázi zapsána
+ žádná vydání mohou být po čase vymazáni, takže záznam vytvořte pouze pokud ho provážete
+ s vydáním. Aby byl producent na seznamu, potřebuje alespoň jeden dokončený produkt. Což
+ znamená, že co se překladatelských projektů týká, přidávejte skupinu jako producenta až poté,
+ co opravdu dokončila projekt.
+</p>
+
+
+:SUB:Obecné informace
+<dl>
+ <dt>Typ</dt><dd>
+ Typ producenta.
+ </dd><dt>Jméno (romaji)</dt><dd>
+ Jméno producenta v latince, za užití <a href="/d5.1">romanizace</a>
+ v případě, že originální jméno již nebylo latinkou. <a href="/d5.2">Kapitalizace</a>
+ je pro toto pole také důležitá.
+ </dd><dt>Originální jméno</dt><dd>
+ Pokud Jméno (viz výše) bylo romanizováno, nezapomeňte zde zapsat jméno originální (pravděpodobně japonské).
+ </dd><dt>Aliasy</dt><dd>
+ Ostatní jména, pod kterými je producent znám. Více aliasů by mělo být odděleno čárkou.
+ </dd><dt>Primární jazyk</dt><dd>
+ Jazyk, ve kterém producent ponejvíce pracuje. Standardně je to japonsky.
+ </dd><dt>Internetová stránka</dt><dd>
+ Oficiální webová stránka producenta.
+ </dd><dt>Popis</dt><dd>
+ Historie producenta nebo popis toho, jaké hry tvoří.
+ </dd>
+</dl>
+
diff --git a/data/docs/5.cs b/data/docs/5.cs
new file mode 100644
index 00000000..f30cb323
--- /dev/null
+++ b/data/docs/5.cs
@@ -0,0 +1,82 @@
+:TITLE:Doporučení k editacím
+:INC:index
+
+
+:SUB:Romanizace
+<p>
+ Většina čtenářů VNDB jsou anglicky mluvící fanoušci vizuálních novel. Při psaní
+ databáze předpokládáme, že čtenáři, na které se zaměřujeme, neumí číst či
+ nerozpozná cokoliv, co není romanizováno. Proto je romanizace užita na mnoha
+ místech databáze: Hlavní názvy vizuálních novel, vydání a producentů by všechny
+ měly být příslušně romanizovány, pokud už v takovém stavu nebyly předtím.<br />
+ Abychom udrželi databázi v jednotném stylu, rozhodli jsme se pro
+ <a href="http://en.wikipedia.org/wiki/Hepburn_romanization">Hepburnovu romanizaci</a>
+ na všech místech. Toto je také konzistentní s
+ <a href="http://wiki.anidb.net/w/Romanisation">romanizací AniDB</a>.
+ <br />
+ Pro anglická (nebo jiná cizí) slova v neromanizovaném textu by měla původní slova
+ být použita pokud tak bylo původně zamýšleno. Pokud jsou tato slova v neromanizovaném
+ skriptu, měla by být romanizována vhodnou romanizační technikou.
+</p>
+
+
+:SUB:Kapitalizace
+<p>
+ Občas se stává, že oficiální názvy nebo jména jsou kompletně v malých nebo
+ velkých písmenech. Pokud pro tuto volbu není žádný pořádný důvod (např. to, že
+ název je akronym), tyto názvy a jména by měly být příslušně převedeny do
+ normální anglické kapitalizace (jak je popsáno
+ <a href="http://en.wikipedia.org/wiki/Capitalization">na Wikipedii</a>) před
+ zapsáním do databáze.<br />
+ Toto se týká jen romanizovaných názvů a hlavních názvů vizuálních novel, pole
+ 'Originální název' u producentů a jednotlivých vydání by stále mělo používat
+ oficiální kapitalizaci.
+</p>
+
+
+:SUB:Pořadí jmen
+<p>
+ Ve většině anglicky mluvících zemí se jména píší v pořadí "křestní jméno, příjmení".
+ Toto nazýváme "západním psaním".
+ V angličtině je obvyklé na někoho volat jeho křestním jménem, pokud ho znáte dobře, jinak se použije příslušný titul a příjmení.
+ <br />
+ V japonštině (a i v dalších jazycích) se jména píší v pořadí "příjmení, křestní jméno".
+ Toto nazýváme "japonským psaním".
+ V japonštině na někoho voláme jedním z jeho jmen s použitím příslušného sufixu.
+ <br />
+ Aby pomohly anglicky mluvícím, některé zdroje (např. Wikipedia) užívají japonských jmen v západním psaní.
+ Zde na vndb.org preferujeme užívání původního psaní, které používala hra.
+ <br />
+ Tedy, pokud byla hra původně japonská s japonskými jmény, použije se japonské psaní.
+ <br />
+ Pokud byla hra původně anglická nebo postava měla kompletně cizí jméno (John Smith), použije se západního psaní.
+</p>
+
+
+:SUB:Shrnutí editací
+<p>
+ Editování jakékoliv stránky zahrnuje volitelné pole "Shrnutí editace".
+ <br />
+ To vám umožňuje vysvětlit o čem je vaše editace bez zbytečného zahlcování hlavní stránky.
+ <br />
+ Můžete se zmínit o tom, proč jste pozměnili kategorii, změnili odkaz, nebo ukázat, odkud jsou vaše informace (aby je další přispěvatelé mohli ověřit).
+ <br />
+ Možná chcete přidat vizuální novelu do databáze, ale nejste schopni pro ni najít další informace.
+ Pokud to zde napíšete, snad je jiný přispěvatel jednou najde a stránku dokončí.
+</p>
+
+
+:SUB:Popisy z vnějších zdrojů
+<p>
+ Je možné přidat popis nebo poznámku k vizuální novele, producentovi a jednotlivým vydáním
+ v databázi. Přestože jsou preferovány původní popisy, užití citovaných popisů z vnějších zdrojů
+ je povoleno, pokud je na původní zdroj patřičně odkázáno. Preferovaná forma odkázání
+ na vnější zdroj je přidáním následujícího vzorového kódu pod popis:<br />
+ [From [url=<b>URL</b>]<b>title or author</b>[/url]]<br />
+ Což je aplikováno jako, například, '[From <a href="http://en.wikipedia.org/Wiki/Ever17">Wikipedia</a>]'
+ pro popisy převzaté z Wikipedie. Pokud popis není převzta doslova, ale má nějaké změny,
+ pak by 'From' mělo být nahrazeno něčím podoným a naznačujícím, že popis byl pozměněn, např. 'Edited from', 'Based on'.<br />
+ Jelikož není povoleno odkazovat na stránky poskytující nebo vybízející k ilegálnímu stahování,
+ měla by adresa (URL) pro takovéto popisy zůstat vynechána.
+</p>
+
diff --git a/data/docs/6 b/data/docs/6
index 8bbf20a5..c9ab4375 100644
--- a/data/docs/6
+++ b/data/docs/6
@@ -72,3 +72,19 @@
the illegal spreading of visual novels.
</p>
+
+:SUB:Can I link to VNDB?
+<p>
+ Of course you can! We even have some icons you could use to link to and promote
+ VNDB. Direct linking is, unlike with all other images on this site, allowed.<br />
+ <img src="http://static.yorhel.nl/2008/vndb_88x31.gif" style="margin: 5px">
+ <img src="http://static.yorhel.nl/2009/vndb1.gif" style="margin: 5px">
+ <img src="http://static.yorhel.nl/2009/vndb2.jpg" style="margin: 5px">
+ <img src="http://static.yorhel.nl/2009/vndb3.jpg" style="margin: 5px">
+ <img src="http://static.yorhel.nl/2009/vndb4.jpg" style="margin: 5px">
+ <img src="http://static.yorhel.nl/2009/vndb5.jpg" style="margin: 5px">
+ <img src="http://static.yorhel.nl/2009/vndb6.jpg" style="margin: 5px"><br />
+ If you feel these banners don't suit your needs, please don't hesitate to make
+ one yourself.
+<p>
+
diff --git a/data/docs/6.cs b/data/docs/6.cs
new file mode 100644
index 00000000..6e9226f9
--- /dev/null
+++ b/data/docs/6.cs
@@ -0,0 +1,89 @@
+:TITLE:Frequently Asked Questions
+:INC:index
+
+
+:SUB:Co je to vizuální novela?
+<p>
+ Na vizuální novelu se dá nahlížet jako na kombinaci novely a počítačové hry:
+ jsou to počítačové hry s dějovou linií obsahující spoustu textu a jen málo
+ interakce od hráče. Typická vizuální novela obsahuje text přes obrázek
+ v anime stylu v pozadí a je doprovázena hudbou na pozadí. V průběhu hry
+ obvykle hráč musí odpovědět na několik otázek, které budou mít dopad na
+ příběh, tudíž hraní vizuální novely podruhé s jinými odpověďmi může poskytnout
+ úplně jiný příběh.<br />
+ <br />
+ Pro více informací je tu <a href="http://en.wikipedia.org/wiki/Visual_Novel">
+ článek o vizuálních novelách na Wikipedii</a> nebo popis na stránce
+ <a href="http://visual-novels.net/vn/index.php?option=com_content&amp;task=view&amp;id=259&amp;Itemid=47">Visual-Novels.net</a>.
+ Pro obecný dojem z žánru doporučujeme jednu z volných krátkých vizuálních novel z
+ <a href="http://altogether.insani.org/">projektu al|together</a>.
+</p>
+
+
+:SUB:Co takové eroge, H-hry a dating simy?
+<p>
+ Eroge nebo H-hra je vlastně jakákoliv japonská hra, která zahrnuje sexuální
+ obsah. Hodně vizuálních novel jsou eroge a hodně eroge jsou vizuální novely,
+ ale není to pravidlo. Definice dating simu je trochu vágnější, ale obvykle
+ je to to samé jako vizuální novela s tím, že herní systém dating simu staví
+ na statistikách.<br />
+ <br />
+ Neexistují žádné pevné hranice definice "vizuálních novel", většina
+ eroge a dating simů obsahuje prvky vizuálních novel, ale nemusí -
+ striktně řečeno - být vizuálními novelami jako takovými. Jelikož se VNDB
+ snaží o srozumitelnost, jednoduše přijímáme jakoukoliv hru, která obsahuje
+ prvky vizuální novely a je produkována japonskou nebo k japonsku vztaženou
+ společností nebo doujin kruhem, pro více informací se podívejte na
+ <a href="/d2">doporučení</a>.
+</p>
+
+
+:SUB:Proč databáze vizuálních novel?
+<p>
+ Internet je veliký, hodně veliký, ale množství anglických zdrojů o vizuálních
+ novelách je jen velmi malé. VNDB se snaží posbírat a zveřejnit co možná nejvíce
+ informací, které by jinak byly jen velmi těžko k nalezení anglicky mluvícími
+ hráči. Touto cestou mohou mít fanoušci jednoduchý přehled o nových vydáních
+ a lokalizacích jejich oblíbené hry a nemusí přitom procházet množstvím
+ sobě si podobných japonských stránek.
+</p>
+
+
+:SUB:Jak mohu VNDB pomoct?
+<p>
+ Je mnoho cest jak na VNDB přispět. První věc, kterou můžete udělat, je
+ volná editace jakýchkoliv informací, které lze nalézt na těchto stránkách,
+ takže pokud najdete chybu, stačí kliknout na odkaz "editovat" umístěný
+ na stránce vpravo nahoře. Můžete také přidávat nové informace (vizuální
+ novely, producenty, vydání) do databáze, i když před takovým přidáním,
+ prosíme, databázi nejdříve prohledejte, abychom se vyhnuli zdvojování
+ stránek.<br />
+ <br />
+ K diskusi o nových funkcích nebo pro pomoc s vývojem stránek samotných se můžete
+ kdykoliv účastnit diskuse na <a href="/t">diskusních boardech</a>
+ nebo se k nám přidat na IRC adrese <a href="irc://irc.synirc.net/vndb">#vndb @ irc.synirc.net</a>.
+ I pokud nepoužíváte IRC nebo jste jen líní nainstalovat si IRC klienta, můžete se stále přidat
+ k povídání za použití <a href="http://cgiirc.synirc.net/?chan=%23vndb">Webchatu</a>.
+ Stačí si vybrat přezdívku a stisknout tlačítko Login!
+</p>
+
+
+:SUB:Kde můžu stáhnout vizuální novely?
+<p>
+ Tady ne. Neposkytujeme stahování ani odkazy na zdroje, které podporují ilegální šíření
+ vizuálních novel.
+</p>
+
+:SUB:Mohu na VNDB odkázat?
+<p>
+ Samozřejmě můžete! Máme dkonce několik ikonek, které můžete k odkazování a podpoře
+ VNDB použít. Přímé linkování je, na rozdíl od všech ostatních obrázků na těchto stránkách, povoleno.<br />
+ <img src="http://static.yorhel.nl/2008/vndb_88x31.gif" style="margin: 5px">
+ <img src="http://static.yorhel.nl/2009/vndb1.gif" style="margin: 5px">
+ <img src="http://static.yorhel.nl/2009/vndb2.jpg" style="margin: 5px">
+ <img src="http://static.yorhel.nl/2009/vndb3.jpg" style="margin: 5px">
+ <img src="http://static.yorhel.nl/2009/vndb4.jpg" style="margin: 5px">
+ <img src="http://static.yorhel.nl/2009/vndb5.jpg" style="margin: 5px">
+ <img src="http://static.yorhel.nl/2009/vndb6.jpg" style="margin: 5px"><br />
+ Pokud cítíte, že ani jeden z těchto bannerů nevyhovuje tomu, co hledáte, neváhejte udělat
+ jeden sami.
diff --git a/data/docs/7 b/data/docs/7
index b20b8e7c..9590cbd0 100644
--- a/data/docs/7
+++ b/data/docs/7
@@ -47,25 +47,50 @@
:SUB:Credits
<p>
- Due to the sites nature as a wiki, all information in the database is added and
- kept up-to-date by active users. VNDB would not have been as large and comprehensive
- as it is now without the support of the many contributers. For a complete list of
- active contributors, check out the <a href="/u/all?o=d;s=changes">user list</a>.
-</p>
+ <b>Code development</b><br />
+ <dl>
+ <dt>Yorhel</dt><dd>Main developer.</dd>
+ <dt>3dB</dt><dd>Wrote the current user authentication code and implemented post throttling on the discussion board.</dd>
+ </dl>
+ <br />
+ <b>Translations</b><br />
+ <dl>
+ <dt>Czech</dt><dd><a href="mailto:nya.chan.production@gmail.com">Nya-chan Production</a></dd>
+ <dt>Hungarian</dt><dd><a href="mailto:bikmate@gmail.com">Bikfalvi Máté</a></dd>
+ <dt>Russian</dt><dd><a href="mailto:winkillerstudio@gmail.com">Dmitri Poguliayev</a></dd>
+ </dl>
-:SUB:Link to us
-<p>
- Due to popular demand, here are some icons you could use to link to and promote
- VNDB. Direct linking is, unlike with all other images on this site, allowed.<br />
- <img src="http://static.yorhel.nl/2008/vndb_88x31.gif" style="margin: 5px">
- <img src="http://static.yorhel.nl/2009/vndb1.gif" style="margin: 5px">
- <img src="http://static.yorhel.nl/2009/vndb2.jpg" style="margin: 5px">
- <img src="http://static.yorhel.nl/2009/vndb3.jpg" style="margin: 5px">
- <img src="http://static.yorhel.nl/2009/vndb4.jpg" style="margin: 5px">
- <img src="http://static.yorhel.nl/2009/vndb5.jpg" style="margin: 5px">
- <img src="http://static.yorhel.nl/2009/vndb6.jpg" style="margin: 5px"><br />
- If you feel these banners don't suit your needs, please don't hesitate to make
- one yourself.
-<p>
+ <br />
+ <b>Skins</b>
+ <dl>
+ <dt>applehq</dt><dd>
+ <a href="?skin=lb">Little Busters! (pink)</a>,
+ <a href="?skin=term">Neon (black)</a>
+ </dd><dt>EchoMateria</dt><dd>
+ <a href="?skin=aselia_01">Aselia (sunset)</a>,
+ <a href="?skin=carnevale">Gekkou no Carnevale (black)</a>,
+ <a href="?skin=eiel">EIeL (sand)</a>,
+ <a href="?skin=ever17_01">Ever17 (light blue)</a>,
+ <a href="?skin=fate_01">Fate/stay night (rust)</a>,
+ <a href="?skin=fate_02">Fate/stay night (red)</a>,
+ <a href="?skin=higu">Higurashi no Naku Koro ni (orange)</a>,
+ <a href="?skin=lb_02">Little Busters! (sand)</a>,
+ <a href="?skin=haze">Primitive Link (haze)</a>,
+ <a href="?skin=saya">Saya no Uta (maroon)</a>,
+ <a href="?skin=seinarukana">Seinarukana (white)</a>,
+ <a href="?skin=taka">Sora no Iro, Mizu no Iro (turquoise)</a>,
+ <a href="?skin=tsukihime">Tsukihime (purple)</a>,
+ <a href="?skin=tsukihime_02">Tsukihime (black)</a>
+ </dd><dt>Yorhel</dt><dd>
+ <a href="?skin=angel">Angelic Serenade (dark blue)</a>,
+ <a href="?skin=grey">Touhou (grey)</a>
+ </dd>
+ </dl>
+ <br />
+
+ <b>Top 5 Contributors</b>
+:TOP5CONTRIB:
+ ...and <a href="/u/all?o=d;s=changes">many more</a>.
+</p>
diff --git a/data/docs/7.cs b/data/docs/7.cs
new file mode 100644
index 00000000..0b0ae6bc
--- /dev/null
+++ b/data/docs/7.cs
@@ -0,0 +1,92 @@
+:TITLE:O nás
+:INC:index
+
+
+:SUB:Cíle
+<p>
+ Naším hlavním cílem je vybudovat velkou, srozumitelnou a aktuální databázi pro
+ informace o všech existujících vizuálních novelách. VNDB se snaží být hlavním
+ místem, kde lze vyhledat obecné informace jak o vizuálních novelách samotných,
+ tak praktické informace okolo nich, jako jsou dostupná vydání a producenti.<br />
+ Naším vedlejším cílem je propagovat toto úžasné médium zvané vizuální novely
+ širší veřejnosti, veřejnosti neomezené jen na lidi, kdo rozumí japonsky, ale
+ komukolibv, kdo se zajímá o literaturu, mangu, anime či hry, bez ohledu na
+ jejich místopisné zařazení či kulturní rozdíly.
+</p>
+
+
+:SUB:Stvoření
+<p>
+ Vše to začalo poté, co <a href="/u2">yorhel</a> dočetl a dohrál
+ <a href="/v17">Ever17</a>. Pod mohutným dojmem z tohoto mistrovského díla ho napadlo
+ několik otázek: Po zjištění, že vizuální novely existují, proč stále trvalo několik
+ měsíců tuhle najít? Proč o ní nikdy předtím neslyšel? Jak může vizuální novela takové
+ kvality unikat pozornosti takřka všech online anime a hráčských komunit? A nejdůležitěji:
+ Je tam venku více takových vizuálních novel?<br />
+ <br />
+ VNDB se zrodila, aby odpověděla na poslední otázku. Kompletní absence jakéhokoliv
+ centrálního zdroje nebo alespoň jednoduchého seznamu vizuálních novel způsobila, že
+ bylo velmi obtížné najít nové hry nebo získat dobrý přehled toho, co bylo dostupné.
+ Problém by vyřešilo, kdyby existovalo nějaké centrální a dobře organizované místo,
+ kde by všichni mohli sdílet své informace a znalosti o vizuálních novelách.<br />
+ <br />
+ Po krátkých třech týdnech tvrdé dřiny spatřila v září 2007 první verze VNDB světlo
+ světa. Zmatenost a malá základna fanoušků okolo vizuálních novel, spolu s holým
+ minimem funkčnosti a neohebný systém přispívání, který tehdy fungoval, zapříčinily,
+ že databáze rostla jen pomalým tempem. Ale časem se o VNDB dozvědělo více lidí,
+ byly představeny nové, pokročilejší funkce a v odpověď na tyto věci začalo více lidí
+ přispívat informacemi. Představení vylepšeného a otevřeného systému přispívání
+ v únoru 2008 motivovala ještě více uživatelů k přidání dalších informací a v září 2008
+ - rok po původní verzi VNDB - databáze zahrnuje přes 1000 vizuálních novel a 2000
+ vydání, a i dnes stále pokračuje v růstu jak do velikosti, tak do kvality.
+</p>
+
+
+:SUB:Autoři
+<p>
+ <b>Vývoj kódu</b><br />
+ <dl>
+ <dt>Yorhel</dt><dd>Hlavní vývojářr.</dd>
+ <dt>3dB</dt><dd>Napsal současný kód ověřující autorizaci uživatelů a na diskusních boardech implementoval limitaci příspěvků za určitý čas.</dd>
+ </dl>
+ <br />
+
+ <b>Překlady</b><br />
+ <dl>
+ <dt>Český</dt><dd><a href="mailto:nya.chan.production@gmail.com">Nya-chan Production</a></dd>
+ <dt>Maďarský</dt><dd><a href="mailto:bikmate@gmail.com">Bikfalvi Máté</a></dd>
+ <dt>Ruský</dt><dd><a href="mailto:winkillerstudio@gmail.com">Dmitri Poguliayev</a></dd>
+ </dl>
+
+ <br />
+ <b>Skiny</b>
+ <dl>
+ <dt>applehq</dt><dd>
+ <a href="?skin=lb">Little Busters! (pink)</a>,
+ <a href="?skin=term">Neon (black)</a>
+ </dd><dt>EchoMateria</dt><dd>
+ <a href="?skin=aselia_01">Aselia (sunset)</a>,
+ <a href="?skin=carnevale">Gekkou no Carnevale (black)</a>,
+ <a href="?skin=eiel">EIeL (sand)</a>,
+ <a href="?skin=ever17_01">Ever17 (light blue)</a>,
+ <a href="?skin=fate_01">Fate/stay night (rust)</a>,
+ <a href="?skin=fate_02">Fate/stay night (red)</a>,
+ <a href="?skin=higu">Higurashi no Naku Koro ni (orange)</a>,
+ <a href="?skin=lb_02">Little Busters! (sand)</a>,
+ <a href="?skin=haze">Primitive Link (haze)</a>,
+ <a href="?skin=saya">Saya no Uta (maroon)</a>,
+ <a href="?skin=seinarukana">Seinarukana (white)</a>,
+ <a href="?skin=taka">Sora no Iro, Mizu no Iro (turquoise)</a>,
+ <a href="?skin=tsukihime">Tsukihime (purple)</a>,
+ <a href="?skin=tsukihime_02">Tsukihime (black)</a>
+ </dd><dt>Yorhel</dt><dd>
+ <a href="?skin=angel">Angelic Serenade (dark blue)</a>,
+ <a href="?skin=grey">Touhou (grey)</a>
+ </dd>
+ </dl>
+ <br />
+
+ <b>5 nejaktivnějších přispívajících</b>
+:TOP5CONTRIB:
+ ...a <a href="/u/all?o=d;s=changes">mnoho dalších</a>.
+</p>
diff --git a/data/docs/9.cs b/data/docs/9.cs
new file mode 100644
index 00000000..f5ceef38
--- /dev/null
+++ b/data/docs/9.cs
@@ -0,0 +1,74 @@
+:TITLE:Diskusní boardy
+:INC:index
+
+
+:SUB:Představení
+<p>
+ VNDB obsahuje pěkně integrované diskusní boardy, které se dají používat k, no,
+ diskusím. Protože nepoužíváme žádný populární nebo veřejně dostupný software
+ na fóra, ale místo toho jsme něco napsali sami, tyto diskusní boardy mají několik
+ odlišností oproti populárním boardům, na které můžete být zvyklí.
+</p>
+
+
+:SUB:Boardy
+<p>
+ Aby se zabezpečilo, že hledající lidé najdou váš příspěvek, všechny thready patří
+ k jednomu nebo více 'boardům', které určují, o čem je daná diskuse. Podobá se to
+ boardům na ostatních fórche, ale zde má každá položka v databázi svůj vlastní board
+ a je možné odkázat thread do více než jednoho boardu. Použít se dají následující
+ boardy:
+</p>
+<dl>
+ <dt>db</dt><dd>
+ Diskuse o VNDB. Toto je obecný board pro thready, které nejsou o žádné určité položce
+ v databázi.
+ </dd><dt>v#</dt><dd>
+ Pro diskuse o určité vizuální novele. Board <i>v17</i>, například, se používá
+ pro všechny thready ohledně vizuální novely <a href="/v17">v17</a>.
+ </dd><dt>p#</dt><dd>
+ Stejně jako <i>v#</i>, ale pro producenty.
+ </dd><dt>u#</dt><dd>
+ <i>u#</i> board se dá použít pro upozornění některého uživatele této stránky ohledně
+ něčeho co by on/a měl/a vidět nebo k diskusi ohledně jeho/jejích editací. Podobá se to
+ klasické funkci 'soukromých zpráv' na mnoha stránkách, až na to, že to není 'soukromé'...
+ </dd><dt>an</dt><dd>
+ Požíváno pro oznámení ohledně stránek. Pouze pro moderátory.
+ </dd>
+</dl>
+
+
+:SUB:Formátování
+<p>
+ Pro formátování vašich příspěvků můžete použít následující kódy:
+</p>
+<dl>
+ <dt>X# or X#.#</dt><dd>
+ 'VNDBID', jak jim říkáme. Jsou to písmena (d, p, r, u or v), za kterými následuje číslo a můžou být následovány
+ tečkou a druhým číslem. VNDBID budou automaticky převedeny na odkazy na příslušnou stránku v databázi.
+ Například pokud napíšete 'v4.4', pak dostanete '<a href="/v4.4">v4.4</a>'.
+ </dd><dt>URL</dt><dd>
+ Jakákoliv URL (bez použití [url] tagu, viz níže) bude převedena na odkaz, podobně jako VNDBID.
+ Příklad: 'http://vndb.org/' bude naformátováno jako '<a href="http://vndb.org/">link</a>'.
+ </dd><dt>[url]</dt><dd>
+ Klasický BBCode [url] tag. Dá se použít poze v podobě <i>[url=link]název odkazu[/url]</i>.<br />
+ Např. '[url=/v]Seznam vizuálních novel[/url] a [url=http://blicky.net/]nějaká externí stránka[/url]'
+ bude zobrazeno jako '<a href="/v">Seznam vizuálních novel</a> a <a href="http://blicky.net/">nějaká externí stránka</a>'
+ </dd><dt>[spoiler]</dt><dd>
+ Tag [spoiler] by měl být použit pro skrytí informací, které by mohly pokazit potěšení z hraní vizuální novely
+ lidem, kteří ji ještě nehráli.
+ </dd><dt>[quote]</dt><dd>
+ Pokud se odkazujete na jiné lidi, umístěte citovaný příspěvek do [quote] .. [/quote] bloku. Prosíme, povšimněte si,
+ že populární syntaxe [quote=source] na VNDB nefunguje. (yet)
+ </dd><dt>[raw]</dt><dd>
+ Předveďte své dovednosti ve formátovacím kódu umístěním čehokoliv, co nechcete aby bylo naformátováno do [raw]
+ tagu. Jakýkoliv formátovací kód do teď zmíněný bude v [raw] .. [/raw] bloku ignorován.
+ </dd>
+</dl>
+<p>
+ Nemáme žádný tag [img] a pravděpodobně zde nikdy žádný nebude. Pokud chcete přidat
+ do příspěvku screenshoty nebo jiné obrázky, pak je, prosíme, nahrajte na externí
+ hostingovou službu (např. <a href="http://tinypic.com/" rel="nofollow">TinyPic</a>) a odkažte na ně ve svém příspěvku.
+</p>
+
+
diff --git a/data/docs/index.cs b/data/docs/index.cs
new file mode 100644
index 00000000..08c8b500
--- /dev/null
+++ b/data/docs/index.cs
@@ -0,0 +1,14 @@
+<ul class="index">
+ <li><b>Doporučení</b></li>
+ <li><a href="/d5">Doporučení k editacím</a></li>
+ <li><a href="/d2">Vizuální novely</a></li>
+ <li><a href="/d3">Vydání</a></li>
+ <li><a href="/d4">Producenti</a></li>
+ <li><a href="/d10">Tagy</a></li>
+ <li><b>O VNDB</b></li>
+ <li><a href="/d9">Diskusní boardy</a></li>
+ <li><a href="/d6">FAQ</a></li>
+ <li><a href="/d7">O nás</a></li>
+ <li><a href="/d8">Vývoj</a></li>
+</ul>
+
diff --git a/data/docs/notfinished.cs b/data/docs/notfinished.cs
new file mode 100644
index 00000000..ad45d31b
--- /dev/null
+++ b/data/docs/notfinished.cs
@@ -0,0 +1,4 @@
+<div class="warning">
+ Tato stránka ještě není dokončena!<br />
+ Pokud byste nám chtěli pomoct s jejím dokončováním, pak se k nám, prosíme, připojte na <a href="irc://irc.synirc.net/vndb">IRC</a>.
+</div>
diff --git a/data/global.pl b/data/global.pl
index 4ab04d5b..6dae908f 100644
--- a/data/global.pl
+++ b/data/global.pl
@@ -31,34 +31,34 @@ our %S = (%S,
[qw| hist board boardmod edit tag mod lock del tagmod |], # 4
[qw| hist board boardmod edit tag mod lock del tagmod usermod |], # 5
],
- languages => [qw|cs da de en es fi fr it ja ko nl no pl pt ru sv tr vi zh|],
+ languages => [qw|cs da de en es fi fr hu it ja ko nl no pl pt ru sv tr vi zh|],
producer_types => [qw|co in ng|],
discussion_boards => [qw|an db v p u|],
vn_lengths => [ 0..5 ],
- anime_types => [
- # AniDB anime type starts counting at 1, 0 = unknown
- # we start counting at 0, with NULL being unknown
- 'TV Series',
- 'OVA',
- 'Movie',
- 'Other',
- 'Web',
- 'TV Special',
- 'Music Video',
- ],
- vn_relations => [
- # Name, Reverse--
- [ 'Sequel', 0 ],
- [ 'Prequel', 1 ],
- [ 'Same setting', 0 ],
- [ 'Alternative version', 0 ],
- [ 'Shares characters', 0 ],
- [ 'Side story', 0 ],
- [ 'Parent story', 1 ],
- [ 'Same series', 0 ],
- [ 'Fandisc', 0 ],
- [ 'Original game', 1 ],
- ],
+ anime_types => [qw|tv ova mov oth web spe mv|],
+ vn_relations => {
+ # id => [ order, reverse ]
+ seq => [ 0, 'preq' ],
+ preq => [ 1, 'seq' ],
+ set => [ 2, 'set' ],
+ alt => [ 3, 'alt' ],
+ char => [ 4, 'char' ],
+ side => [ 5, 'par' ],
+ par => [ 6, 'side' ],
+ ser => [ 7, 'ser' ],
+ fan => [ 8, 'orig' ],
+ orig => [ 9, 'fan' ],
+ },
+ prod_relations => {
+ 'old' => [ 0, 'new' ],
+ 'new' => [ 1, 'old' ],
+ 'sub' => [ 2, 'par' ],
+ 'par' => [ 3, 'sub' ],
+ 'imp' => [ 4, 'ipa' ],
+ 'ipa' => [ 5, 'imp' ],
+ 'spa' => [ 6, 'ori' ],
+ 'ori' => [ 7, 'spa' ],
+ },
age_ratings => {
-1 => [ 'Unknown' ],
0 => [ 'All ages' ,'CERO A' ],
@@ -76,21 +76,21 @@ our %S = (%S,
17 => [ '17+', 'CERO D' ],
18 => [ '18+', 'CERO Z' ],
},
- release_types => [0..2],
+ release_types => [qw|complete partial trial|],
platforms => [qw|win dos lin mac dvd gba msx nds nes p98 psp ps1 ps2 ps3 drc sat sfc wii xb3 oth|],
media => {
- #DB display qty
- cd => [ 'CD', 1 ],
- dvd => [ 'DVD', 1 ],
- gdr => [ 'GD', 1 ],
- blr => [ 'Blu-ray', 1 ],
- flp => [ 'Floppy', 1 ],
- mrt => [ 'Cartridge', 1 ],
- mem => [ 'Memory card', 1 ],
- umd => [ 'UMD', 1 ],
- nod => [ 'Nintendo Optical Disk', 1 ],
- in => [ 'Internet download', 0 ],
- otc => [ 'Other', 0 ],
+ #DB qty?
+ cd => 1,
+ dvd => 1,
+ gdr => 1,
+ blr => 1,
+ flp => 1,
+ mrt => 1,
+ mem => 1,
+ umd => 1,
+ nod => 1,
+ in => 0,
+ otc => 0
},
resolutions => [
[ 'Unknown / console / handheld', '' ],
@@ -108,21 +108,8 @@ our %S = (%S,
voiced => [ 0..4 ],
animated => [ 0..4 ],
wishlist_status => [ 0..3 ],
- # note: keep these synchronised in script.js
- vn_rstat => [
- 'Unknown',
- 'Pending',
- 'Obtained', # hardcoded
- 'On loan',
- 'Deleted',
- ],
- vn_vstat => [
- 'Unknown',
- 'Playing',
- 'Finished', # hardcoded
- 'Stalled',
- 'Dropped',
- ],
+ rlst_rstat => [ 0..4 ], # 2 = hardcoded 'OK', < 2 = hardcoded 'NOK'
+ rlst_vstat => [ 0..4 ], # 2 = hardcoded 'OK', 0 || 4 = hardcoded 'NOK'
);
diff --git a/data/lang.txt b/data/lang.txt
index d4463379..bdea0d85 100644
--- a/data/lang.txt
+++ b/data/lang.txt
@@ -78,6 +78,10 @@ ends with ']'. The following options are supported:
Same as the english [quant] as above, but has three forms for a word
instead of two.
+ [quant,{num},{singular},{normative},{genetive}] (Czech)
+ Same as the english [quant] as above, but has three forms for a word
+ instead of two.
+
[url,{url},{title}]
Formats a link to another page, where {url} is the location of the page
and {title} the link title. {url} is usually an argument, e.g.:
@@ -100,31 +104,88 @@ ends with ']'. The following options are supported:
# data/global.pl - used in many places
+# Age display
+
+:_age_years
+en : [_1] [quant,_1,year,years] ago
+ru : [_1] [quant,_1,год,года,лет] назад
+cs : před [_1] [quant,_1,rokem,roky,roky]
+hu : [_1] [quant,_1,éve,éve]
+
+:_age_months
+en : [_1] [quant,_1,month,months] ago
+ru : [_1] [quant,_1,месяц,месяца,месяцев] назад
+cs : před [_1] [quant,_1,měsícem,měsíci,měsíci]
+hu : [_1] [quant,_1,hónapja,hónapja]
+
+:_age_weeks
+en : [_1] [quant,_1,week,weeks] ago
+ru : [_1] [quant,_1,неделя,недели,недель] назад
+cs : před [_1] [quant,_1,týdnem,týdny,týdny]
+hu : [_1] [quant,_1,hete,hete]
+
+:_age_days
+en : [_1] [quant,_1,day,days] ago
+ru : [_1] [quant,_1,день,дня,дней] назад
+cs : před [_1] [quant,_1,dnem,dny,dny]
+hu : [_1] [quant,_1,napja,napja]
+
+:_age_hours
+en : [_1] [quant,_1,hour,hours] ago
+ru : [_1] [quant,_1,час,часа,часов] назад
+cs : před [_1] [quant,_1,hodinou,hodinami,hodinami]
+hu : [_1] [quant,_1,órája,órája]
+
+:_age_min
+en : [_1] min ago
+ru : [_1] [quant,_1,минута,минуты,минут] назад
+cs : před [_1] min
+hu : [_1] perce
+
+:_age_sec
+en : [_1] sec ago
+ru : [_1] [quant,_1,секунда,секунды,секунд] назад
+cs : před [_1] s
+hu : [_1] másodperce
+
+
# user ranks
:_urank_0
en : visitor
ru : посетитель
+cs : návštěvník
+hu : vendég
:_urank_1
en : banned
ru : забанен
+cs : zabanovaný
+hu : kicsapva
:_urank_2
en : loser
ru : лузер
+cs : loser
+hu : vesztes
:_urank_3
en : user
ru : пользователь
+cs : uživatel
+hu : felhasználó
:_urank_4
en : mod
ru : модератор
+cs : moderátor
+hu : mod
:_urank_5
en : admin
ru : администратор
+cs : administrátor
+hu : admin
# languages
@@ -132,78 +193,122 @@ ru : администратор
:_lang_cs
en : Czech
ru : Чешский
+cs : Česky
+hu : Cseh
:_lang_da
en : Danish
ru : Датский
+cs : Dánsky
+hu : Dán
:_lang_de
en : German
ru : Немецкий
+cs : Německy
+hu : Német
:_lang_en
en : English
ru : Английский
+cs : Anglicky
+hu : Angol
:_lang_es
en : Spanish
ru : Испанский
+cs : Španělsky
+hu : Spanyol
:_lang_fi
en : Finnish
ru : Финский
+cs : Finsky
+hu : Finn
:_lang_fr
en : French
ru : Французский
+cs : Francouzsky
+hu : Francia
+
+:_lang_hu
+en : Hungarian
+ru : Венгерский
+cs : Maďarsky
+hu : Magyar
:_lang_it
en : Italian
ru : Итальянский
+cs : Italsky
+hu : Olasz
:_lang_ja
en : Japanese
ru : Японский
+cs : Japonsky
+hu : Japán
:_lang_ko
en : Korean
ru : Корейский
+cs : Korejsky
+hu : Koreai
:_lang_nl
en : Dutch
ru : Голландский
+cs : Nizozemsky
+hu : Holland
:_lang_no
en : Norwegian
ru : Норвежский
+cs : Norsky
+hu : Norvég
:_lang_pl
en : Polish
ru : Польский
+cs : Polsky
+hu : Lengyel
:_lang_pt
en : Portuguese
ru : Португальский
+cs : Portugalsky
+hu : Portugál
:_lang_ru
en : Russian
ru : Русский
+cs : Rusky
+hu : Orosz
:_lang_sv
en : Swedish
ru : Шведский
+cs : Švédsky
+hu : Svéd
:_lang_tr
en : Turkish
ru : Турецкий
+cs : Turecky
+hu : Török
:_lang_vi
en : Vietnamese
ru : Вьетнамский
+cs : Vietnamsky
+hu : Vietnámi
:_lang_zh
en : Chinese
ru : Китайский
+cs : Čínsky
+hu : Kínai
# platforms
@@ -213,82 +318,254 @@ ru : Китайский
:_plat_win
en : Windows
ru : Windows
+cs : Windows
+hu :
:_plat_dos
en : DOS
ru : DOS
+cs : DOS
+hu :
:_plat_lin
en : Linux
ru : Linux
+cs : Linux
+hu :
:_plat_mac
en : Mac OS
ru : Mac OS
+cs : Mac OS
+hu :
:_plat_dvd
en : DVD Player
ru : DVD Плеер
+cs : DVD Přehrávač
+hu : DVD Lejátszó
:_plat_gba
en : Game Boy Advance
ru : Game Boy Advance
+cs : Game Boy Advance
+hu :
:_plat_msx
en : MSX
ru : MSX
+cs : MSX
+hu :
:_plat_nds
en : Nintendo DS
ru : Nintendo DS
+cs : Nintendo DS
+hu :
:_plat_nes
en : Famicom
ru : Famicom (Dendy)
+cs : Famicom
+hu :
:_plat_p98
en : PC-98
ru : PC-98
+cs : PC-98
+hu :
:_plat_psp
en : Playstation Portable
ru : Playstation Portable
+cs : Playstation Portable
+hu :
:_plat_ps1
en : Playstation 1
ru : Playstation 1
+cs : Playstation 1
+hu :
:_plat_ps2
en : Playstation 2
ru : Playstation 2
+cs : Playstation 2
+hu :
:_plat_ps3
en : Playstation 3
ru : Playstation 3
+cs : Playstation 3
+hu :
:_plat_drc
en : Dreamcast
ru : Dreamcast
+cs : Dreamcast
+hu :
:_plat_sat
en : Sega Saturn
ru : Sega Saturn
+cs : Sega Saturn
+hu :
:_plat_sfc
en : Super Nintendo
ru : Super Nintendo
+cs : Super Nintendo
+hu :
:_plat_wii
en : Nintendo Wii
ru : Nintendo Wii
+cs : Nintendo Wii
+hu :
:_plat_xb3
en : Xbox 360
ru : Xbox 360
+cs : Xbox 360
+hu :
:_plat_oth
en : Other
ru : Другая
+cs : Ostatní
+hu : Egyéb
+
+
+# Release media
+
+:_med_cd
+en : [quant,_1,CD,CDs]
+ru : [quant,_1,CD,CD,CD]
+cs : [quant,_1,CD,CD,CD]
+hu : [quant,_1,CD,CD]
+
+:_med_dvd
+en : [quant,_1,DVD,DVDs]
+ru : [quant,_1,DVD,DVD,DVD]
+cs : [quant,_1,DVD,DVD,DVD]
+hu : [quant,_1,DVD,DVD]
+
+:_med_gdr
+en : [quant,_1,GD-ROM,GD-ROMs]
+ru : [quant,_1,GD-ROM,GD-ROM,GD-ROM]
+cs : [quant,_1,GD-ROM,GD-ROMy,GD-ROMů]
+hu : [quant,_1,GD-ROM,GD-ROM]
+
+:_med_blr
+en : [quant,_1,Blu-ray disc,Blu-ray discs]
+ru : [quant,_1,BD-ROM,BD-ROM,BD-ROM]
+cs : [quant,_1,disk Blu-ray,disky Blu-ray,disků Blu-ray]
+hu : [quant,_1,Blu-ray lemez,Blu-ray lemez]
+
+:_med_flp
+en : [quant,_1,Floppy,Floppies]
+ru : [quant,_1,Дискета,Дискеты,Дискетт]
+cs : [quant,_1,disketa,diskety,disket]
+hu : [quant,_1,Floppy,Floppy]
+
+:_med_mrt
+en : [quant,_1,Cartridge,Cartridges]
+ru : [quant,_1,Картридж,Картриджа,Картриджей]
+cs : [quant,_1,Cartridge,Cartridge,Cartridgí]
+hu : [quant,_1,Kazetta,Kazetta]
+
+:_med_mem
+en : [quant,_1,Memory card,Memory cards]
+ru : [quant,_1,Карта памяти,Карты памяти,Карт памяти]
+cs : [quant,_1,Paměťová karta,Paměťové karty,Paměťových karet]
+hu : [quant,_1,Memória kártya,Memória kártya]
+
+:_med_umd
+en : [quant,_1,UMD,UMDs]
+ru : [quant,_1,UMD,UMD,UMD]
+cs : [quant,_1,UMD,UMD,UMD]
+hu : [quant,_1,UMD,UMD]
+
+:_med_nod
+en : Nintendo Optical [quant,_1,Disk,Disks]
+ru : [quant,_1,Оптический диск,Оптические диски,Оптических дисков] Nintendo
+cs : [quant,_1,Nintendo Optical Disk,Nintendo Optical Disky,Nintendo Optical Disků]
+hu : Nintendo Optical [quant,_1,Lemez,Lemezek]
+
+:_med_in
+en : Internet download
+ru : Распространение через Интернет
+cs : Stažení z internetu
+hu : Internetes letöltés
+
+:_med_otc
+en : Other
+ru : Другое
+cs : Ostatní
+hu : Egyéb
+
+
+# VN relations
+
+:_vnrel_seq
+en : Sequel
+ru : Продолжение
+cs : Pokračování
+hu : Folytatás
+
+:_vnrel_preq
+en : Prequel
+ru : Предыстория
+cs : Prequel
+hu : Előzmény
+
+:_vnrel_set
+en : Same setting
+ru : Тот же мир
+cs : Stejné zasazení
+hu : Egyforma környezet
+
+:_vnrel_alt
+en : Alternative version
+ru : Альтернативная версия
+cs : Alternativní verze
+hu : Alternatív változat
+
+:_vnrel_char
+en : Shares characters
+ru : Те же персонажи
+cs : Sdílí postavy
+hu : Megosztott szereplők
+
+:_vnrel_side
+en : Side story
+ru : Побочный сюжет
+cs : Vedlejší příběh
+hu : Mellék történet
+
+:_vnrel_par
+en : Parent story
+ru : Исходный сюжет
+cs : Mateřský příběh
+hu : Szüllő történet
+
+:_vnrel_ser
+en : Same series
+ru : Из той же серии
+cs : Stejná série
+hu : Ugyanaz a sorozat
+
+:_vnrel_fan
+en : Fandisc
+ru : Фандиск
+cs : Fandisk
+hu : Rajongó lemez
+
+:_vnrel_orig
+en : Original game
+ru : Оригинальная игра
+cs : Originální hra
+hu : Eredeti játék
# producer types
@@ -296,29 +573,137 @@ ru : Другая
:_ptype_co
en : Company
ru : Компания
+cs : Společnost
+hu : Cég
:_ptype_in
en : Individual
ru : Частное лицо
+cs : Jednotlivec
+hu : Magánszemély
:_ptype_ng
en : Amateur group
ru : Любительская группа
+cs : Amatérská skupina
+hu : Amatőr csoport
+
+
+# producer relations
+
+:_prodrel_old
+en : Formerly
+ru : Изначально
+cs : Dříve
+hu : Előzőleg
+
+:_prodrel_new
+en : Succeeded by
+ru : Унаследована
+cs : Nahrazeno
+hu : Utódja
+
+:_prodrel_sub
+en : Subsidiary
+ru : Второстепенная
+cs : Vedlejší
+hu : Alválalat
+
+:_prodrel_par
+en : Parent producer
+ru : Исходная компания
+cs : Mateřský producent
+hu : Szüllő készítő
+
+:_prodrel_imp
+en : Imprint
+ru : Марка
+cs : Otisk
+hu : Megjelenési adatok
+
+:_prodrel_ipa
+en : Parent brand
+ru : Исходный бренд
+cs : mateřská značka
+hu : Szüllő márka
+
+:_prodrel_spa
+en : Spawned
+ru : Порождена
+cs*:
+hu*:
+
+:_prodrel_ori
+en : Originated from
+ru : Вышла из
+cs*:
+hu*:
# release types
-:_rtype_0
+:_rtype_complete
en : Complete
ru : Полный
+cs : Kompletní
+hu : Teljes
-:_rtype_1
+:_rtype_partial
en : Partial
ru : Частичный
+cs : Částečné
+hu : Részleges
-:_rtype_2
+:_rtype_trial
en : Trial
ru : Триальный
+cs : Trial
+hu : Próba
+
+
+# Anime types
+
+:_animetype_tv
+en : TV Series
+ru : ТВ Сериал
+cs : TV série
+hu : TV Sorozat
+
+:_animetype_ova
+en : OVA
+ru : ОВА
+cs : OVA
+hu :
+
+:_animetype_mov
+en : Movie
+ru : Фильм
+cs : Film
+hu : Film
+
+:_animetype_oth
+en : Other
+ru : Другое
+cs : Ostatní
+hu : Egyéb
+
+:_animetype_web
+en : Web
+ru : Веб-трансляция
+cs : Internet
+hu :
+
+:_animetype_spe
+en : TV Special
+ru : ТВ-спешл
+cs : TV Speciál
+hu : TV Különkiadás
+
+:_animetype_mv
+en : Music Video
+ru : Музыкальное видео
+cs : Hudební video
+hu : Videoklip
# Discussion board types
@@ -326,22 +711,32 @@ ru : Триальный
:_dboard_an
en : Announcements
ru : Объявления
+cs : Oznámení
+hu : Hirdetések
:_dboard_db
en : VNDB Discussions
ru : Форум VNDB
+cs : Diskuse o VNDB
+hu : VNDB Eszmecsere
:_dboard_v
en : Visual novels
ru : Новеллы
+cs : Vizuální novely
+hu : Visual novellek
:_dboard_p
en : Producers
ru : Компании
+cs : Producenti
+hu : Készítők
:_dboard_u
en : Users
ru : Пользователи
+cs : Uživatelé
+hu : Felhasználók
# Wishlist statuses
@@ -349,18 +744,26 @@ ru : Пользователи
:_wish_0
en : high
ru : высокий
+cs : vysoká
+hu : magas
:_wish_1
en : medium
ru : средний
+cs : střední
+hu : közepes
:_wish_2
en : low
ru : низкий
+cs : nízká
+hu : alacsony
:_wish_3
en : blacklist
ru : в чёрном списке
+cs : blacklist
+hu : fekete lista
# 'Voiced' information for releases
@@ -368,22 +771,32 @@ ru : в чёрном списке
:_voiced_0
en : Unknown
ru : Неизвестно
+cs : Není známo
+hu : Ismeretlen
:_voiced_1
en : Not voiced
ru : Нет озвучки
+cs : Bez hlasu
+hu : Néma
:_voiced_2
en : Only ero scenes voiced
ru : Озвучены лишь эросцены
+cs : S hlasem pouze v ero scénách
+hu : Csak ero jelenetek szinkronosak
:_voiced_3
en : Partially voiced
ru : Частичная озвучка
+cs : Částečně s hlasem
+hu : Részlegesen szinkronos
:_voiced_4
en : Fully voiced
ru : Озвучено целиком
+cs : Plně s hlasem
+hu : Szinkronizált
# 'Animated' information for releases
@@ -391,22 +804,32 @@ ru : Озвучено целиком
:_animated_0
en : Unknown
ru : Неизвестно
+cs : Není známo
+hu : Ismeretlen
:_animated_1
en : No animations
ru : Без анимации
+cs : Bez animací
+hu : Nem animált
:_animated_2
en : Simple animations
ru : Простая анимация
+cs : Jednoduché animace
+hu : Egyszerű animációk
:_animated_3
en : Some fully animated scenes
ru : Некоторые сцены анимированы целиком
+cs : Některé plně animované scény
+hu : Pár teljesen animált jelenet
:_animated_4
en : All scenes fully animated
ru : Все сцены полностью анимированы
+cs : Všechny scény plně animovány
+hu : Minden jelenet animált
# Rating indications
@@ -414,42 +837,62 @@ ru : Все сцены полностью анимированы
:_vote_1
en : worst ever
ru : хуже некуда
+cs : absolutně nejhorší
+hu : iszonyú
:_vote_2
en : awful
ru : ужасно
+cs : hrozná
+hu : szörnyű
:_vote_3
en : bad
ru : плохо
+cs : špatná
+hu : rossz
:_vote_4
en : weak
ru : слабо
+cs : slabá
+hu : gyenge
:_vote_5
en : so-so
ru : так себе
+cs : taktak
+hu : elmegy
:_vote_6
en : decent
ru : неплохо
+cs : slušná
+hu : átlagos
:_vote_7
en : good
ru : хорошо
+cs : dobrá
+hu : jó
:_vote_8
en : very good
ru : здорово
+cs : velmi dobrá
+hu : nagyon jó
:_vote_9
en : excellent
ru : отлично
+cs : skvělá
+hu : nagyszerű
:_vote_10
en : masterpiece
ru : шедевр
+cs : mistrovský kus
+hu : mestermű
# VN lengths
@@ -457,26 +900,101 @@ ru : шедевр
:_vnlength_0
en : Unknown
ru : Неизвестно
+cs : Není známo
+hu : Ismeretlen
:_vnlength_1
en : Very short[index,_1,, (< 2 hours), (OMGWTHOTL~, A Dream of Summer)]
-ru : Очень короткая[index,_1,, (< 2 часов), (OMGWTFOTL~, A Dream Of Summer)]
+ru : Крошечная[index,_1,, (< 2 часов), (OMGWTFOTL~, A Dream Of Summer)]
+cs : Velmi krátká[index,_1,, (< 2 hours), (OMGWTHOTL~, A Dream of Summer)]
+hu : Nagyon rövid[index,_1,, (< 2 óra), (OMGWTHOTL~, A Dream of Summer)]
:_vnlength_2
en : Short[index,_1,, (2 - 10 hours), (Narcissu~, Planetarian)]
-ru : Короткая[index,_1,, (2 - 10 часов), (Narcissu~, Planetarian)]
+ru : Малая[index,_1,, (2 - 10 часов), (Narcissu~, Planetarian)]
+cs : Krátká[index,_1,, (2 - 10 hours), (Narcissu~, Planetarian)]
+hu : Rövid[index,_1,, (2 - 10 óra), (Narcissu~, Planetarian)]
:_vnlength_3
en : Medium[index,_1,, (10 - 30 hours), (Kana: Little Sister)]
ru : Средняя[index,_1,, (10 - 30 часов), (Kana: Little Sister)]
+cs : Střední[index,_1,, (10 - 30 hours), (Kana: Little Sister)]
+hu : Közepes[index,_1,, (10 - 30 óra), (Kana: Little Sister)]
:_vnlength_4
en : Long[index,_1,, (30 - 50 hours), (Tsukihime)]
-ru : Длинная[index,_1,, (30 - 50 часов), (Tsukihime)]
+ru : Большая[index,_1,, (30 - 50 часов), (Tsukihime)]
+cs : Dlouhá[index,_1,, (30 - 50 hours), (Tsukihime)]
+hu : Hosszú[index,_1,, (30 - 50 óra), (Tsukihime)]
:_vnlength_5
en : Very long[index,_1,, (> 50 hours), (Clannad)]
-ru : Очень длинная[index,_1,, (> 50 часов), (Clannad)]
+ru : Очень большая[index,_1,, (> 50 часов), (Clannad)]
+cs : Velmi dlouhá[index,_1,, (> 50 hours), (Clannad)]
+hu : Nagyon hosszú[index,_1,, (> 50 óra), (Clannad)]
+
+
+# VN list statuses
+
+:_rlst_rstat_0
+en : Unknown
+ru : Неизвестно
+cs : Není známo
+hu : Ismeretlen
+
+:_rlst_rstat_1
+en : Pending
+ru : Ожидает
+cs : V čekání
+hu : Függő
+
+:_rlst_rstat_2
+en : Obtained
+ru : Приобретено
+cs : Obdrženo
+hu : Megszerezve
+
+:_rlst_rstat_3
+en : On loan
+ru : Взято напрокат
+cs : Vypůjčeno
+hu : Kölcsönadva
+
+:_rlst_rstat_4
+en : Deleted
+ru : Удалено
+cs : Smazáno
+hu : Törölve
+
+:_rlst_vstat_0
+en : Unknown
+ru : Неизвестно
+cs : Není známo
+hu : Ismeretlen
+
+:_rlst_vstat_1
+en : Playing
+ru : Играю
+cs : Ve hraní
+hu : Játszás
+
+:_rlst_vstat_2
+en : Finished
+ru : Завершено
+cs : Dohráno
+hu : Befejezve
+
+:_rlst_vstat_3
+en : Stalled
+ru : Застряло
+cs : Pozastaveno
+hu : Leragadva
+
+:_rlst_vstat_4
+en : Dropped
+ru : Заброшено
+cs : Vyřazeno
+hu : Lemondva
# Form messages
@@ -484,106 +1002,233 @@ ru : Очень длинная[index,_1,, (> 50 часов), (Clannad)]
:_formerr_e_login_failed
en : Invalid username or password
ru : Некорректное имя пользователя или пароль
+cs : Neplatné uživatelské jméno nebo heslo
+hu : Hibás felhasználónév vagy jelszó
:_formerr_e_nomail
en : No user found with that email address
ru : Пользователя с такой электронной почтой не существует
+cs : Uživatel s touto e-mailovou adresou nebyl nalezen
+hu : Nem található felhasználó ezzel az email címmel
:_formerr_e_passmatch
en : Passwords do not match
ru : Пароли не совпадают
+cs : Hesla nejsou stejná
+hu : A jelszavak nem egyeznek meg
:_formerr_e_usrexists
en : Someone already has this username, please choose something else
ru : Кто-то уже зарегистрировал такой ник, пожалуйста выберите другой
+cs : Toto uživatelské jméno už je v užívání, vyberte prosím jiné
+hu : Már foglalt ez a felhasználónév, kérlek válassz másikat
:_formerr_e_mailexists
en : Someone already registered with that email address
ru : Кто-то уже регистрировался с таким адресом электронной почты
+cs : Tuto e-mailovou adresu již někdo k registraci použil
+hu : Valaki már regisztrált ezzel az email címmel
:_formerr_e_noimage
en : Image must be in JPEG or PNG format
ru : Изображение должно быть в формате JPEG, либо в формате PNG
+cs : Obrázek musí být v JPEG nebo PNG formátu
+hu : A kép muszáj JPEG vagy PNG formátumba legyen
:_formerr_e_toolarge
en : Image is too large, only 500kB allowed
ru : Изображение слишком большое, 500 кб - максимально допустимый предел
+cs : Obrázek je moc velký, je povoleno pouze 500kB
+hu : A kép túl nagy, 500kb a megengedett
:_formerr_e_oneaday
en : You can only register one account from the same IP within 24 hours
ru : Вы можете зарегистрировать учётную запись с одного и того же IP лишь по прошествии 24 часов
+cs : V rámci 24 hodin si lze z jedné IP adresy zaregistrovat pouze jeden účet
+hu : 24 óra alatt csak egy fiókot hozhatsz létre ugyanazzal az IP-vel
:_formerr_e_nochanges
en : No changes, please don't create an entry that is fully identical to another
ru : Изменения отсутствуют, пожалуйста не создавайте идентичных копий записей
+cs : Nenalezeny změny, netvořte prosím záznam, který je plně totožný s jiným
+hu : Nem történt változtatás, kérlek ne készíts egy bejegyzést ami teljesen megegyezik egy másikkal
:_formerr_e_doublepost
en : Please wait 30 seconds before making another post
ru : Прежде чем публиковать очередное сообщение, пожалуйста подождите 30 секунд
+cs : Před posláním dalšího příspěvku počkejte prosím 30 sekund
+hu : Kérlek várj 30 másodpercet mielőtt új hozzászólást küldesz
:_formerr_title
en : Error
ru : Ошибка
+cs : Chyba
+hu : Hiba
:_formerr_subtitle
en : Form could not be sent:
ru : Невозможно отправить форму:
+cs : Dokument nemohl být odeslán:
+hu : Az űrlapot nem sikerült elküldeni:
:_formerr_required
en : [_1] is a required field!
ru : [_1] - обязательное поле!
+cs : [_1] je pole, které musí být vyplněno!
+hu : [_1] kitöltése kötelező!
:_formerr_minlength
en : [_1]: should have at least [_2] characters
ru : [_1]: [quant,_2,необходим,необходимы,необходимо] хотя бы [_2] [quant,_2,символ,символа,символов]
+cs : [_1]: musí mít minimálně [_2] znaků
+hu : [_1]: legalább [_2] karaktert kell tartalmazzon
:_formerr_maxlength
en : [_1]: only [_2] characters allowed
ru : [_1]: [quant,_2,разрешён,разрешены,разрешено] лишь [_2] [quant,_2,символ,символа,символов]
+cs : [_1]: je povoleno maximálně [_2] znaků
+hu : [_1]: csakis [_2] karakterek megengedettek
:_formerr_enum
en : [_1] must be one of the following: [_2]
ru : Поле '[_1]' должно равняться одному из следующих значений: [_2]
+cs : [_1] musí být z následujícího seznamu: [_2]
+hu : [_1] muszáj az alábbiak közül valamelyik legyen: [_2]
:_formerr_wrongboard
en : Wrong board: [_1]
ru : Некорректная ветка: [_1]
+cs : Špatný board: [_1]
+hu : Rossz alfórum: [_1]
:_formerr_tagexists
en : Tag [url,_1,_2] already exists!
ru : Тег [url,_1,_2] уже существует!
+cs : Tag [url,_1,_2] již existuje!
+hu : A(z) [url,_1,_2] címke már létezik!
:_formerr_tpl_mail
en : Invalid email address
ru : Некорректный адрес электронной почты
+cs : Neplatná e-mailová adresa
+hu : Érvénytelen email cím
:_formerr_tpl_url
en : [_1]: Invalid URL
ru : [_1]: Некорректная ссылка
+cs : [_1]: Neplatná URL
+hu : [_1]: Érvénytelen hivatkozás
:_formerr_tpl_asciiprint
en : [_1] may only contain ASCII characters
ru : Поле '[_1]' может содержать лишь символы диапазона ASCII
+cs : [_1] smí obsahovat pouze znaky ASCII
+hu : [_1] kizárólag ASCII karaktereket tartalmazhat
:_formerr_tpl_int
en : [_1]: Not a valid number
ru : [_1]: Не является правильным числом
+cs : [_1]: Neplatné číslo
+hu : [_1]: Nem egy érvényes szám
:_formerr_tpl_pname
en : [_1] can only contain lowercase alphanumeric characters and a hyphen, and must start with a character
ru : Поле '[_1]' может содержать лишь символы буквенно-цифрового диапазона в нижнем регистре и чёрточку, а так же начинаться с буквы
+cs : [_1] smí obsahovat pouze malá písmena, čísla a pomlčku, a musí začínat písmenem
+hu : [_1] egy karakterrel kell kezdődjön és csak kisbetűs alfanumerikus karaktereket és egy kötőjelet tartalmazhat
:_form_tab_all
en : All items
ru : Все поля
+cs : Všechny položky
+hu : Minden
:_form_editsum
en : Edit summary
ru : Суммарно о правке
+cs : Shrnutí editace
+hu : Összegzés
:_form_submit
en : Submit
ru : Отправить
+cs : Potvrdit
+hu : Beküldés
+
+
+# Common javascript strings
+
+:_js_expand
+en : expand
+ru : развернуть
+cs : rozbalit
+hu : kiterjeszt
+
+:_js_collapse
+en : collapse
+ru : свернуть
+cs : složit
+hu : összevonás
+
+:_js_loading
+en : Loading...
+ru : Загрузка...
+cs : Nahrává se...
+hu : Betöltés...
+
+:_js_date_year
+en : -year-
+ru : -год-
+cs : -rok-
+hu : -év-
+
+:_js_date_month
+en : -month-
+ru : -месяц-
+cs : -měsíc-
+hu : -hónap-
+
+:_js_date_day
+en : -day-
+ru : -день-
+cs : -dne-
+hu : -nap-
+
+:_js_ds_noresults
+en : No results...
+ru : Совпадений не найдено...
+cs : Žádné výsledky...
+hu : Nincs eredmény...
+
+:_js_iv_close
+en : close
+ru : закрыть
+cs : zavřít
+hu : bezárás
+
+:_js_iv_prev
+en : previous
+ru : назад
+cs : předchozí
+hu : előző
+
+:_js_iv_next
+en : next
+ru : далее
+cs : další
+hu : következő
+
+:_js_ds_tag_meta
+en : meta
+ru : мета
+cs : meta
+hu :
+
+:_js_ds_tag_mod
+en : awaiting moderation
+ru : ждёт модерирования
+cs : čeká na schválení
+hu : moderálásra vár
@@ -600,12 +1245,16 @@ ru : Отправить
:_site_title
en : The Visual Novel Database
ru : The Visual Novel Database
+cs : Databáze Vizuálních Novel
+hu : A Visual Novel Adatbázis
# the 'ALL' in "ALL A B C D .. #"
:_char_all
en : ALL
ru : ВСЕ
+cs : VŠE
+hu : Minden
# Main menu
@@ -613,54 +1262,80 @@ ru : ВСЕ
:_menu
en : Menu
ru : Меню
+cs : Menu
+hu : Menü
:_menu_home
en : Home
ru : Главная
+cs : Home
+hu : Kezdőlap
:_menu_vn
en : Visual novels
ru : Новеллы
+cs : Vizuální novely
+hu : Visual novellek
:_menu_releases
en : Releases
ru : Выпуски
+cs : Vydání
+hu : Kiadások
:_menu_producers
en : Producers
ru : Компании
+cs : Producenti
+hu : Készítők
:_menu_tags
en : Tags
ru : Теги
+cs : Tagy
+hu : Címkék
:_menu_users
en : Users
ru : Пользователи
+cs : Uživatelé
+hu : Felhasználók
:_menu_recent_changes
en : Recent changes
ru : Свежие правки
+cs : Poslední změny
+hu : Legútóbbi változtatások
:_menu_discussion_board
en : Discussion board
ru : Форум
+cs : Diskusní board
+hu : Fórum
:_menu_faq
en : FAQ
ru : ЧаВо
+cs : FAQ
+hu : GYIK
:_menu_randvn
en : Random visual novel
ru : Случайная новелла
+cs : Náhodná vizuální novela
+hu : Véletlenszerű novel
:_menu_webchat
en : webchat
ru : Веб-чат
+cs : webchat
+hu :
:_menu_emptysearch
en : search
ru : поиск
+cs : hledat
+hu : kereső
# User menu
@@ -668,80 +1343,118 @@ ru : поиск
:_menu_myprofile
en : My Profile
ru : Мой профиль
+cs : Můj profil
+hu : Profilom
:_menu_myvnlist
en : My Visual Novel List
ru : Мой список новелл
+cs : Můj list vizuálních novel
+hu : Visual Novel listám
:_menu_mywishlist
en : My Wishlist
ru : Мой список желаемого
+cs : Můj wishlist
+hu : Kivánságlistám
# [_1] = number of messages
:_menu_mymessages
en : My Messages ([_1])
ru : Мои сообщения
+cs : Mé zprávy ([_1])
+hu : Üzenenteim ([_1])
:_menu_mychanges
en : My Recent Changes
ru : Мои недавние правки
+cs : Mé poslední změny
+hu : Legutóbbi változtatásaim
:_menu_mytags
en : My Tags
ru : Мои теги
+cs : Moje tagy
+hu : Címkéim
:_menu_addvn
en : Add Visual Novel
ru : Добавить новеллу
+cs : Přidat vizuální novelu
+hu : Visual Novel hozzáadássa
:_menu_addproducer
en : Add Producer
ru : Добавить компанию
+cs : Přidat producenta
+hu : Készítő hozzáadássa
:_menu_logout
en : Logout
ru : Выйти
+cs : Odhlásit
+hu : Kijelentkezés
# used for both the box title and submit button
:_menu_login
en : Login
ru : Вход
+cs : Přihlášení
+hu : Bejelentkezés
:_menu_loginmsg
en : Need to [url,_1,register],[br]
or [url,_2,forgot your password]?
ru : Нужна [url,_1,регистрация],[br]
или [url,_2,забыли свой пароль]?
+cs : Potřebujete se [url,_1,přihlásit],[br]
+ nebo jste [url,_2,zapomněli heslo]?
+hu : Ha még nem [url,_1,regisztráltál], akkor most megteheted,[br]
+ vagy netán [url,_2,elfelejtetted a jelszavad]?
# database statistics
:_menu_dbstats
en : Database Statistics
ru : Статистика базы данных
+cs : Statistiky databáze
+hu : Adatbázis Statisztikák
:_menu_stat_vn
en : Visual Novels
ru : Новелл
+cs : Vizuální novely
+hu : Visual Novellek
:_menu_stat_releases
en : Releases
ru : Выпусков
+cs : Vydání
+hu : Kiadások
:_menu_stat_producers
en : Producers
ru : Компаний
+cs : Producenti
+hu : Készítők
:_menu_stat_users
en : Users
ru : Пользователей
+cs : Uživatelé
+hu : Felhasználók
:_menu_stat_threads
en : Threads
ru : Тем
+cs : Thready
+hu : Témák
:_menu_stat_posts
en : Posts
ru : Сообщений
+cs : Příspevky
+hu : Hozzászólások
# Footer
@@ -749,10 +1462,14 @@ ru : Сообщений
:_footer_aboutus
en : about us
ru : о нас
+cs : o nás
+hu : rólunk
:_footer_source
en : source
ru : исходный код
+cs : zdroj
+hu : forráskód
# Main tabs (those on the right top of the highest box)
@@ -760,70 +1477,100 @@ ru : исходный код
:_mtabs_hist
en : history
ru : история
+cs : historie
+hu : előzmények
:_mtabs_discuss
en : discussions ([_1])
ru : обсуждения ([_1])
+cs : diskuse ([_1])
+hu : beszélgetések ([_1])
# the following 4 tabs are only present on user pages
:_mtabs_posts
en : posts
ru : сообщения
+cs : příspevky
+hu : hozzászólások
:_mtabs_wishlist
en : wishlist
ru : список желаемого
+cs : wishlist
+hu : kivánságlista
:_mtabs_list
en : list
ru : список
+cs : seznam
+hu : lista
:_mtabs_tags
en : tags
ru : теги
+cs : tagy
+hu : címkék
# modify tags on VN pages
:_mtabs_tagmod
en : modify tags
ru : править теги
+cs : změnit tagy
+hu : címkék módosítása
# copy a release
:_mtabs_copy
en : copy
ru : копировать
+cs : kopírovat
+hu : másolás
# following line is also used on revision pages (it's the same action, anyway)
:_mtabs_edit
en : edit
ru : правка
+cs : upravit
+hu : szerkesztés
# hide/unhide a DB item
:_mtabs_hide
en : hide
ru : скрыть
+cs : skrýt
+hu : elrejtés
:_mtabs_unhide
en : unhide
ru : показать
+cs : odkrýt
+hu : megjelenítés
# lock/unlock for editing
:_mtabs_lock
en : lock
ru : заблокировать
+cs : zamknout
+hu : bezárás
:_mtabs_unlock
en : unlock
ru : снять блокировку
+cs : odemknout
+hu : kinyitás
# delete
:_mtabs_del
en : del
ru : удалить
+cs : smazat
+hu : törlés
# VN relations
:_mtabs_relations
en : relations
ru : связи
+cs : vztahy
+hu : összefüggések
# Navigation buttons on the browse pages
@@ -831,10 +1578,14 @@ ru : связи
:_browse_previous
en : previous
ru : назад
+cs : předchozí
+hu : előző
:_browse_next
en : next
ru : далее
+cs : další
+hu : következő
# Revision pages
@@ -842,31 +1593,45 @@ ru : далее
:_revision_previous
en : earlier revision
ru : более ранняя редакция
+cs : dřívější revize
+hu : korábbi javítás
:_revision_next
en : later revision
ru : более поздняя редакция
+cs : pozdější revize
+hu : későbbi javítás
:_revision_title
en : Revision [_1]
ru : Редакция [_1]
+cs : Revize [_1]
+hu : Javítás - #[_1]
# it's the summary of the edit, "edit" is not a verb here.
:_revision_new_summary
en : Edit summary
ru : Суммарно
+cs : Shrnutí editace
+hu : Szerkesztés összefoglalása
:_revision_edit_summary
en : Edit summary of revision [_1]:
ru : Суммарно о редакции [_1]:
+cs : Shrnutí editace revize[_1]:
+hu : [_1] javítás szerkesztésének az összefoglalása
:_revision_user_date
en : By [userstr,_1] on [date,_2,full]
ru : [userstr,_1], [date,_2,full]
+cs : Změnil [userstr,_1] [date,_2,full]
+hu : [userstr,_1] által, [date,_2,full]
:_revision_emptyfield
en : ~[empty~]
ru : ~[пусто~]
+cs : ~[prázdná~]
+hu : ~[üres~]
# tabs above the search boxes
@@ -874,27 +1639,39 @@ ru : ~[пусто~]
:_searchbox_vn
en : Visual novels
ru : Новеллы
+cs : Vizuální novely
+hu : Visual novellek
:_searchbox_releases
en : Releases
ru : Выпуски
+cs : Vydání
+hu : Kiadások
:_searchbox_producers
en : Producers
ru : Компании
+cs : Producenti
+hu : Keszítők
:_searchbox_tags
en : Tags
ru : Теги
+cs : Tagy
+hu : Címkék
:_searchbox_users
en : Users
ru : Пользователи
+cs : Uživatelé
+hu : Felhasználok
# text on the search button
:_searchbox_submit
en : Search!
ru : Искать!
+cs : Hledat!
+hu : Keress!
@@ -922,39 +1699,70 @@ ru : VNDB.org стремится быть наиболее полной базо
Если хотите, можете [url,/v/all,побродить по сайту], [url,/u/register,создать учётную запись] или
поучаствовать в обсуждении новелл (либо самой VNDB) на нашем [url,/t,форуме] (убедительная просьба писать
только на английском).
+cs : VNDB.org se snaží být srozumitelnou databází informací o vizuálních novelách.[br]
+ Tato stránka funguje na principu wikipedie, tedy kdokoliv může volně přispívat informacemi
+ do databáze, což nám umožňuje tvořit největší, nejpřesnější a nejaktuálnější databázi
+ vizuálních novel na webu.[br]
+ Registrovaní uživatelé si také mohou vytvořit osobní list her, které chtějí hrát nebo dohráli
+ a mohou hodnotit všechny vizuální novely.[br][br]
+ Můžete jen tak [url,/v/all,brouzdat kolem], [url,/u/register,zaregistrovat si účet]
+ nebo se účastnit diskusí o vizuálních novelách nebo VNDB na našem [url,/t,diskusním boardu].
+hu : A VNDB.org arra törekszik, hogy egy átfogó és információ dús adatbázist hozzon létre visual novellekről.[br]
+ Ez az oldal úgy van felépítve mint egy wiki, s annak érdekében, hogy ez legyen a legnagyobb, legpontosabb
+ és napra kész visual novel adatbázis a weben, mindenki szabadon hozzájárulhat a tartalom bővítéséhez.[br]
+ Regisztrált felhasználók ugyanakkor listát vezethetnek azokról a játékokról amelyeket már játszottak,játszanak
+ vagy épp játszani szeretnének, valamint szavazhatnak is az oldalon tálalható bármely novelre.[br][br]
+ Bátran, [url,/v/all,nézz körül], [url,/u/register,regisztrálj az oldalra] vagy vegyél részt a visual
+ novellek és a VNDB-ről szóló eszmecserékbe a [url,/t,fórumon].
:_home_recentchanges
en : Recent Changes
ru : Свежие правки
+cs : Poslední změny
+hu : Legútóbbi változtatások
:_home_recentchanges_item
en : [_1]:[_2] by [userstr,_3]
ru : [_1]:[_2], [userstr,_3]
+cs : [_1]:[_2], [userstr,_3]
+hu : [_1]:[_2] - [userstr,_3] által
:_home_announcements
en : Announcements
ru : Объявления
+cs : Oznámení
+hu : Hirdetések
:_home_recentposts
en : Recent Posts
ru : Недавние сообщения
+cs : Poslední příspěvky
+hu : Legfrissebb hozzászólások
:_home_recentposts_item
en : [age,_1] [_2] by [userstr,_3]
ru : [age,_1] [_2], [userstr,_3]
+cs : [age,_1] [_2], [userstr,_3]
+hu : [age,_1] [_2] - [userstr,_3] által
:_home_randomvn
en : Random visual novels
ru : Случайные новеллы
+cs : Náhodné vizuální novely
+hu : Véletlenszerű visual novel
:_home_upcoming
en : Upcoming releases
ru : Грядущие выпуски
+cs : Nadcházející vydání
+hu : Hamarosan megjelenő kiadások
:_home_justreleased
en : Just released
ru : Только что вышли
+cs : Právě vydáno
+hu : Épp most jelentek meg
@@ -968,10 +1776,14 @@ ru : Только что вышли
:_hist_title
en : Recent changes
ru : Свежие правки
+cs : Poslední změny
+hu : Legutóbbi változtatások
:_hist_title_item
en : Edit history of [_1]
ru : История изменений [_1]
+cs : Editovat historii [_1]
+hu : [_1] előzményeinek szerkesztése
# the filter buttons
@@ -979,54 +1791,80 @@ ru : История изменений [_1]
:_hist_filter_showauto
en : Show automated edits
ru : Показать автоматические правки
+cs : Ukázat automatické editace
+hu : Automata szerkesztések megjelenítése
:_hist_filter_hideauto
en : Hide automated edits
ru : Скрыть автоматические правки
+cs : Skrýt automatické editace
+hu : Automata szerkesztések elrejtése
:_hist_filter_hidedel
en : Hide deleted items
ru : Скрыть удалённые страницы
+cs : Skrýt smazané položky
+hu : Törölt elemek elrejtése
:_hist_filter_showdel
en : Show deleted items
ru : Показать удалённые страницы
+cs : Ukázat smazané položky
+hu : Törölt elemek megjelenítése
:_hist_filter_alltypes
en : Show all items
ru : Показать все страницы
+cs : Ukázat všechny položky
+hu : Minden elem megjelenítése
:_hist_filter_onlyvn
en : Only visual novels
ru : Только новеллы
+cs : Pouze vizuální novely
+hu : Csak visual novellek
:_hist_filter_onlyreleases
en : Only releases
ru : Только выпуски
+cs : Pouze vydání
+hu : Csak kiadások
:_hist_filter_onlyproducers
en : Only producers
ru : Только компании
+cs : Pouze producenti
+hu : Csak készítők
:_hist_filter_allactions
en : Show all changes
ru : Показать все изменения
+cs : Ukázat všechny změny
+hu : Minden változtatás megjelenitése
:_hist_filter_onlyedits
en : Only edits
ru : Только правки
+cs : Pouze editace
+hu : Csak a szerkesztések
:_hist_filter_onlynew
en : Only newly created pages
ru : Только новые страницы
+cs : Pouze nově stvořené stránky
+hu : Csak újonnan létrehozott oldalakat
:_hist_filter_exrel
en : Exclude edits of releases
ru : Исключить правки выпусков
+cs : Výjmout editace vydání
+hu : Ezen kiadások szerkesztésének a kihagyása
:_hist_filter_increl
en : Include edits of releases
ru : Включить правки выпусков
+cs : Zahrnout editace vydání
+hu : Ezen kiadások szerkesztésének a mellékelése
# column headers
@@ -1035,18 +1873,26 @@ ru : Включить правки выпусков
:_hist_col_rev
en : Rev.
ru : Ревизия
+cs : Rev.
+hu : Javítás
:_hist_col_date
en : Date
ru : Дата
+cs : Datum
+hu : Dátúm
:_hist_col_user
en : User
ru : Пользователь
+cs : Uživatel
+hu : Felhasználó
:_hist_col_page
en : Page
ru : Страница
+cs : Stránka
+hu : Oldal
@@ -1062,46 +1908,68 @@ ru : Страница
:_thread_postedin
en : Posted in
ru : В теме
+cs : Přispěno v
+hu : Válaszolva
:_thread_byuser
en : by [userstr,_1]
ru : [userstr,_1]
+cs : , [userstr,_1]
+hu : - [userstr,_1]
:_thread_editpost
en : edit
ru : правка
+cs : editovat
+hu : szerkesztés
:_thread_deletedpost
en : Post deleted.
ru : Сообщение удалено.
+cs : Příspěvek smazán.
+hu : Hozzászólás törölve.
:_thread_lastmodified
en : Last modified on [date,_1,full]
ru : Последний раз редактировалось [date,_1,full]
+cs : Naposledy změněno [date,_1,full]
+hu : Utolsó szerkesztés [date,_1,full]
:_thread_noreply_title
en : Reply
ru : Ответить
+cs : Odpovědět
+hu : Válasz
:_thread_noreply_locked
en : This thread has been locked, you can't reply to it anymore
ru : Данная тема заблокирована, вы больше не можете в неё ответить
+cs : Tento thread byl zamčen, již se do něj nedá odpovědět
+hu : A téma le van zárva, nem lehet rá válaszolni
:_thread_noreply_login
en : You must be logged in to reply to this thread.
ru : Вы должны быть авторизованы чтобы ответить в эту тему.
+cs : Pro odpověď do tohoto threadu se musíte přihlásit.
+hu : Be kell legyél jelentkezve, hogy hozzászólhass a témához.
:_thread_quickreply_title
en : Quick reply
ru : Быстрый ответ
+cs : Rychlá odpověď
+hu : Gyors válasz
:_thread_quickreply_submit
en : Reply
ru : Ответить
+cs : Odpovědět
+hu : Válasz
:_thread_quickreply_full
en : Go advanced...
ru : В расширенный режим...
+cs : Rozšířený mód...
+hu : Haladó mód...
# Post edit/reply/new thread form
@@ -1109,54 +1977,80 @@ ru : В расширенный режим...
:_postedit_newthread
en : Start new thread
ru : Начать новую тему
+cs : Založit nový thread
+hu : Új téma létrehozása
:_postedit_replyto
en : Reply to [_1]
ru : Ответить в [_1]
+cs : Odpovědět na [_1]
+hu : Válasz [_1]-ra
:_postedit_edit
en : Edit post
ru : Правка сообщения
+cs : Editovat příspěvek
+hu : Hozzászólás szerkesztése
:_postedit_form_username
en : Username
ru : Имя пользователя
+cs : Uživatelské jméno
+hu : Felhasználónév
:_postedit_form_title
en : Thread title
ru : Название темы
+cs : Název threadu
+hu : Téma cím
:_postedit_form_boards
en : Board(s)
ru : Форум(ы)
+cs : Board(y)
+hu : Fórum(ok)
:_postedit_form_boards_info
en : Read [url,/d9.2,d9.2] for information about how to specify boards.
ru : О том как указывать форумы читайте в разделе [url,/d9.2,d9.2].
+cs : Přečtěte si [url,/d9.2,d9.2] pro informace jak určit správný board.
+hu : Lásd [url,/d9.2,d9.2], hogy hogy kell meghatározni a fórumokat.
:_postedit_form_locked
en : Locked
ru : Заблокировано
+cs : Zamčený
+hu : Lezárva
:_postedit_form_topic
en : Topic
ru : Тема
+cs : Téma
+hu : Téma
:_postedit_form_hidden
en : Hidden
ru : Скрыто
+cs : Skrytý
+hu : Rejtett
:_postedit_form_nolastmod
en : Don't update last modified field
ru : Не обновлять дату последнего изменения
+cs : Neměnit poslední změněné pole
+hu : Ne frissítse az utolsó változtatott mezőt
:_postedit_form_msg
en : Message
ru : Сообщение
+cs : Zpráva
+hu : Üzenet
:_postedit_form_msg_format
en : See [url,/d9.3,d9.3] for the allowed formatting codes
ru : Список разрешённых кодов смотрите в разделе [url,/d9.3,d9.3]
+cs : Přečtěte si [url,/d9.3,d9.3] pro povolené formátovací kódy
+hu : A formázó kódokat itt [url,/d9.3,d9.3] találod meg
# Browsing threads by board (/t/{board_id})
@@ -1164,22 +2058,32 @@ ru : Список разрешённых кодов смотрите в разд
:_disboard_item_title
en : Related discussions for [_1]
ru : Темы, относящиеся к [_1]
+cs : Diskuse pro [_1]
+hu : [_1]-hoz kapcsolódó beszélgetések
:_disboard_rootlink
en : Discussion board
ru : Форум
+cs : Diskusní board
+hu : Fórum
:_disboard_nothreads
en : No related threads found
ru : Связанных тем не найдено
+cs : Nenalezeny žádné diskuse
+hu : Nincs hozzá kapcsolódó téma
:_disboard_createyourown
en : Why not create one yourself?
ru : Почему бы не создать новую?
+cs : Proč jednu nevytvořite sami?
+hu : Miért ne kezdenél te egyet?
:_disboard_startnew
en : Start a new thread
ru : Начать новую тему
+cs : Začít nový thread
+hu : Új téma megnyitása
# The discussion board index (/t)
@@ -1187,6 +2091,8 @@ ru : Начать новую тему
:_disindex_title
en : Discussion board index
ru : Корневая директория форума
+cs : Seznam diskusních boardů
+hu : Fórum kezdőoldal
# Thread list (on discussion board index and board browser)
@@ -1194,18 +2100,26 @@ ru : Корневая директория форума
:_threadlist_col_topic
en : Topic
ru : Тема
+cs : Téma
+hu : Téma
:_threadlist_col_replies
en : Replies
ru : Ответов
+cs : Odpovědi
+hu : Válaszok
:_threadlist_col_starter
en : Starter
ru : Автор темы
+cs : První příspěvek
+hu : Készítő
:_threadlist_col_lastpost
en : Last post
ru : Последнее сообщение
+cs : Poslední příspěvek
+hu : Utolsó hozzászólás
@@ -1223,18 +2137,38 @@ ru : Последнее сообщение
:_prodpage_langtype
en : [_1] [_2]
ru : [_2], основной язык: [_1]
+cs : [_2], [_1]
+hu :
:_prodpage_aliases
en : a.k.a. [_1]
ru : a.k.a. [_1]
+cs : a.k.a. [_1]
+hu :
:_prodpage_vnrel
en : Visual Novel Relations
ru : Связи новелл
+cs : Vztahy k vizuálním novelám
+hu : Visual Novel Összefüggések
:_prodpage_norel
en : We have currently no visual novels by this producer.
ru : У нас пока нет сведений о новеллах авторства этой компании.
+cs : Nemáme žádné vizuální novely od tohoto producenta.
+hu : Jelenleg nincsen visual novel bejegyzésünk ettől a készítőtől.
+
+:_prodpage_dev
+en : developer
+ru : разработчик
+cs : vývojář
+hu : fejlesztő
+
+:_prodpage_pub
+en : publisher
+ru : издатель
+cs : vydavatel
+hu : kiadó
# producer diff fields
@@ -1242,30 +2176,65 @@ ru : У нас пока нет сведений о новеллах авторс
:_revfield_p_type
en : Type
ru : Тип
+cs : Typ
+hu : Típus
:_revfield_p_name
en : Name (romaji)
ru : Название (ромадзи)
+cs : Jméno (romaji)
+hu : Név (romaji)
:_revfield_p_original
en : Original name
ru : Оригинальное название
+cs : Originální jméno
+hu : Eredeti név
:_revfield_p_alias
en : Aliases
ru : Прочие названия
+cs : Aliasy
+hu : Más nevezések
:_revfield_p_lang
en : Language
ru : Язык
+cs : Jazyk
+hu : Nyelv
:_revfield_p_website
en : Website
ru : Веб-сайт
+cs : Internetová stránka
+hu : Weboldal
:_revfield_p_desc
en : Description
ru : Описание
+cs : Popis
+hu : Leírás
+
+:_revfield_p_relations
+en : Relations
+ru : Отношения
+cs : Vztahy
+hu : Összefüggések
+
+:_proddiff_none
+en : none
+ru : нет
+cs : žádné
+hu : semmi
+
+
+# Producer relation graph page (/p+/rg)
+
+:_prodrg_title
+en : Relation graph for [_1]
+ru : Схема отношений для [_1]
+cs : Graf vztahů pro producenta [_1]
+hu : [_1] összefüggés gráfja
# Add/Edit producer
@@ -1273,50 +2242,128 @@ ru : Описание
:_pedit_title_edit
en : Edit [_1]
ru : Править [_1]
+cs : Editovat [_1]
+hu : [_1] szerkesztése
:_pedit_title_add
en : Add new producer
ru : Добавление новой компании
+cs : Přidat nového producenta
+hu : Új készítő hozzáadása
:_pedit_form_generalinfo
en : General info
ru : Основная информация
+cs : Obecné informace
+hu : Általános info
:_pedit_form_type
en : Type
ru : Тип
+cs : Typ
+hu : Típus
:_pedit_form_name
en : Name (romaji)
ru : Название (ромадзи)
+cs : Jméno (romaji)
+hu : Név (romaji)
:_pedit_form_original
en : Original name
ru : Оригинальное название
+cs : Originální jméno
+hu : Eredeti név
:_pedit_form_original_note
en : The original name of the producer, leave blank if it is already in the Latin alphabet.
ru : Оригинальное название компании, оставьте пустым если уже в латинском алфавите.
+cs : Originální jméno producenta, ponechte prázdné, pokud již je v latince.
+hu : A készítő eredeti neve, hagyd üresen ha latin betűkből áll s már beírtad a "Név" mezőbe.
:_pedit_form_alias
en : Aliases
ru : Прочие названия
+cs : Aliasy
+hu : Más nevek
:_pedit_form_alias_note
en : (Un)official aliases, separated by a comma.
ru : (Не)официальные альтернативные названия, через запятую.
+cs : Ne(oficiální) aliasy, oddělené čárkou.
+hu : (Nem)hivatalos nevek, vesszővel elválasztva
:_pedit_form_lang
en : Primary language
ru : Основной язык
+cs : Hlavní jazyk
+hu : Elsődleges nyelv
:_pedit_form_website
en : Website
ru : Веб-сайт
+cs : Internetová stránka
+hu : Weboldal
:_pedit_form_desc
en : Description
ru : Описание
+cs : Popis
+hu : Leírás
+
+:_pedit_form_rel
+en : Relations
+ru : Отношения
+cs : Vztahy
+hu : Összefüggések
+
+:_pedit_rel_sel
+en : Selected producers
+ru : Выбранные компании
+cs : Vybraní producenti
+hu : Kiválasztott készítők
+
+:_pedit_rel_add
+en : Add producer
+ru : Добавить компанию
+cs : Přidat producenta
+hu : Készítő hozzáadása
+
+:_pedit_rel_addbut
+en : add
+ru : добавить
+cs : přidat
+hu : hozzáad
+
+:_pedit_rel_del
+en : remove
+ru : убрать
+cs : odebrat
+hu : eltávolít
+
+:_pedit_rel_none
+en : Nothing selected.
+ru : Ничего не выбрано.
+cs : Není nic vybráno.
+hu : Nincs semmi kiválasztva.
+
+:_pedit_rel_findformat
+en : Producer textbox should start with an ID (e.g. "p7:")
+ru : Строка компании должна начинаться с идентификатора (например, "p7:")
+cs : Textové pole producenta by mělo začínat ID (např. "p7:")
+hu : A Készítő szövegdoboz egy ID-val kell kezdődjön (pl. "p7:")
+
+:_pedit_rel_notfound
+en : Producer not found
+ru : Компания не найдена
+cs : Producent nenalezen
+hu : A készítő nem található
+
+:_pedit_rel_double
+en : Producer already selected!
+ru : Компания уже выбрана!
+cs : Producent byl již vybrán!
+hu : A készítő már ki van választva!
# Browse/search producers
@@ -1324,18 +2371,26 @@ ru : Описание
:_pbrowse_title
en : Browse producers
ru : Обзор компаний
+cs : Procházet producenty
+hu : Készítők böngészése
:_pbrowse_searchres
en : Search results
ru : Результаты поиска
+cs : Výsledky hledání
+hu : Keresés eredménye
:_pbrowse_list
en : Producer list
ru : Список компаний
+cs : Seznam producentů
+hu : Készítők listája
:_pbrowse_noresults
en : No results found
ru : Ничего не найдено
+cs : Nenalezeny žádné výsledky
+hu : Nem található semmi
@@ -1351,86 +2406,140 @@ ru : Ничего не найдено
:_revfield_r_vn
en : Relations
ru : Связи
+cs : Vztahy
+hu : Összefüggések
:_revfield_r_type
en : Type
ru : Тип
+cs : Typ
+hu : Típus
:_revfield_r_patch
en : Patch
ru : Патч
+cs : Patch
+hu :
:_revfield_r_freeware
en : Freeware
ru : Freeware
+cs : Freeware
+hu :
:_revfield_r_doujin
en : Doujin
ru : Додзинси
+cs : Doujin
+hu :
:_revfield_r_title
en : Title (romaji)
ru : Название (ромадзи)
+cs : Název (romaji)
+hu : Cím (romaji)
:_revfield_r_original
en : Original title
ru : Оригинальное название
+cs : Originální název
+hu : Eredeti cím
:_revfield_r_gtin
en : JAN/UPC/EAN
ru : JAN/UPC/EAN
+cs : JAN/UPC/EAN
+hu :
:_revfield_r_catalog
en : Catalog number
ru : Номер в каталоге
+cs : Číslo v katalogu
+hu : Katalógus szám
:_revfield_r_languages
en : Language
ru : Язык
+cs : Jazyk
+hu : Nyelv
:_revfield_r_website
en : Website
ru : Веб-сайт
+cs : Internetová stránka
+hu : Weboldal
:_revfield_r_released
en : Release date
ru : Дата выпуска
+cs : Datum vydání
+hu : Kiadás ideje
:_revfield_r_minage
en : Age rating
ru : Возрастной рейтинг
+cs : Věková přístupnost
+hu : Korhatár
:_revfield_r_notes
en : Notes
ru : Заметки
+cs : Poznámky
+hu : Megjegyzések
:_revfield_r_platforms
en : Platforms
ru : Платформы
+cs : Platformy
+hu : Platformok
:_revfield_r_media
en : Media
ru : Носители
+cs : Média
+hu : Médium
:_revfield_r_resolution
en : Resolution
ru : Разрешение
+cs : Rozlišení
+hu : Felbontás
:_revfield_r_voiced
en : Voiced
ru : Озвучка
+cs : Hlas
+hu : Szinkronos
:_revfield_r_ani_story
en : Story animation
ru : Сюжетная анимация
+cs : Animace příběhu
+hu : Animált történet
:_revfield_r_ani_ero
en : Ero animation
ru : Анимация эросцен
+cs : Ero animace
+hu : Animált ero jelenetek
:_revfield_r_producers
en : Producers
ru : Компании
+cs : Producenti
+hu : Készítők
+
+:_reldiff_developer
+en : developer
+ru : разработчик
+cs : vývojář
+hu : fejlesztő
+
+:_reldiff_publisher
+en : publisher
+ru : издатель
+cs : vydavatel
+hu : kiadó
# Information table (on every release page)
@@ -1438,114 +2547,176 @@ ru : Компании
:_relinfo_vnrel
en : Relation
ru : Связи
+cs : Vztah
+hu : Összefüggés
:_relinfo_title
en : Title
ru : Название
+cs : Název
+hu : Cím
:_relinfo_original
en : Original title
ru : Оригинальное название
+cs : Originální název
+hu : Eredeti cím
:_relinfo_type
en : Type
ru : Тип
+cs : Typ
+hu : Típus
:_relinfo_type_format
en : [_1][index,_2,, patch]
ru : [_1][index,_2,, патч]
+cs : [_1][index,_2,, patch]
+hu :
:_relinfo_lang
en : Language
ru : Язык
+cs : Jazyk
+hu : Nyelv
:_relinfo_publication
en : Publication
ru : Публикация
+cs : Publikováno
+hu : Kiadás
:_relinfo_pub_nopatch
en : [index,_1,Freeware,Non-free], [index,_2,doujin,commercial]
ru : [index,_1,Freeware,Несвободная], [index,_2,додзинси,коммерческая]
+cs : [index,_1,Freeware,Non-free], [index,_2,doujin,komerční]
+hu : [index,_1,Freeware,Nem Freeware], [index,_2,doujin,kereskedelmi]
:_relinfo_pub_patch
en : [index,_1,Freeware,Non-free]
ru : [index,_1,Freeware,Несвободная]
+cs : [index,_1,Freeware,Non-free]
+hu : [index,_1,Freeware,Nem Freeware]
:_relinfo_platform
en : [quant,_1,Platform,Platforms]
ru : [quant,_1,Платформа,Платформы,Платформ]
+cs : [quant,_1,Platforma,Platformy,Platforem]
+hu : [quant,_1,Platform,Platformok]
:_relinfo_media
en : [quant,_1,Medium,Media]
ru : [quant,_1,Носитель,Носители,Носителей]
+cs : [quant,_1,Médium,Média,Médií]
+hu : [quant,_1,Medium,Mediumok]
:_relinfo_resolution
en : Resolution
ru : Разрешение
+cs : Rozlišení
+hu : Felbontás
:_relinfo_voiced
en : Voiced
ru : Озвучка
+cs : Hlas
+hu : Szinkronos
:_relinfo_ani
en : Animation
ru : Анимация
+cs : Animace
+hu : Animáció
:_relinfo_ani_story
en : Story: [_1]
ru : Сюжет: [_1]
+cs : Příběh: [_1]
+hu : Történet: [_1]
:_relinfo_ani_ero
en : Ero scenes: [_1]
ru : Эросцены: [_1]
+cs : Ero scény: [_1]
+hu : Ero jelenetek: [_1]
:_relinfo_released
en : Released
ru : Дата выпуска
+cs : Vydáno
+hu : Kiadva
:_relinfo_minage
en : Age rating
ru : Возрастной рейтинг
+cs : Věková přístupnost
+hu : Korhatár
-:_relinfo_producer
-en : [quant,_1,Producer,Producers]
-ru : [quant,_1,Компания,Компании,Компаний]
+:_relinfo_developer
+en : [quant,_1,Developer,Developers]
+ru : [quant,_1,Разработчик,Разработчика,Разработчиков]
+cs : [quant,_1,Vývojář,Vývojáři,Vývojářů]
+hu : [quant,_1,Fejlesztő,Fejlesztők]
+
+:_relinfo_publisher
+en : [quant,_1,Publisher,Publishers]
+ru : [quant,_1,Издатель,Издателя,Издателей]
+cs : [quant,_1,Vydavatel,Vydavatelé,Vydavatelů]
+hu : [quant,_1,Kiadó,Kiadók]
:_relinfo_catalog
en : Catalog no.
ru : № в каталоге
+cs : Č. v katalogu
+hu : Katalógus szám
:_relinfo_links
en : Links
ru : Ссылки
+cs : Odkazy
+hu : Linkek
:_relinfo_website
en : Official website
ru : Официальный веб-сайт
+cs : Oficiální stránky
+hu : Hivatalos weboldal
:_relinfo_user
en : User options
ru : Настройки пользователя
+cs : Možnosti uživatele
+hu : Beállítások
:_relinfo_user_notlist
en : not in your list
ru : не в вашем списке
+cs : není ve vašem listu
+hu : nincs a listádba
:_relinfo_user_inlist
en : Status: [_1] / [_2]
ru : Статус: [_1] / [_2]
+cs : Status: [_1] / [_2]
+hu : Állapot: [_1] / [_2]
:_relinfo_user_setr
en : Set release status
ru : Установка статуса выпуска
+cs : Změnit status vydání
+hu : Kiadás állapotának módosítása
:_relinfo_user_setv
en : Set play status
ru : Установка статуса игры
+cs : Změnit herní status
+hu : Játszás állapotának módosítása
:_relinfo_user_del
en : remove from list
ru : убрать из списка
+cs : odstranit z listu
+hu : eltávolítás a listából
# Editing a release
@@ -1553,144 +2724,324 @@ ru : убрать из списка
:_redit_title_edit
en : Edit [_1]
ru : Правка [_1]
+cs : Editovat [_1]
+hu : [_1] szerkesztése
:_redit_title_copy
en : Copy [_1]
ru : Копирование [_1]
+cs : Kopírovat [_1]
+hu : [_1] másolása
:_redit_title_add
en : Add release to [_1]
ru : Добавление выпуска для [_1]
+cs : Přidat vydání hry [_1]
+hu : Kiadás hozzáadása [_1]-hoz
:_redit_form_geninfo
en : General info
ru : Основная информация
+cs : Obecné informace
+hu : Általános info
:_redit_form_type
en : Type
ru : Тип
+cs : Typ
+hu : Típus
:_redit_form_patch
en : This release is a patch to another release.
ru : Данный выпуск является патчем для другого выпуска.
+cs : Toto vydání je patch k jinému vydání.
+hu : Ez a kiadás egy patch egy másik kiadáshoz.
:_redit_form_freeware
en : Freeware (i.e. available at no cost)
ru : Freeware (т.е. распространяется бесплатно)
+cs : Freeware (tj. dostupný zdarma)
+hu : Freeware (vagyis ingyenes)
:_redit_form_doujin
en : Doujin (self-published, not by a company)
ru : Додзинси (выпущено самостоятельно, частным лицом)
+cs : Doujin (publikováno samostatně, ne firmou)
+hu : Doujin (magán kiadás, nem egy cég által)
:_redit_form_title
en : Title (romaji)
ru : Название (ромадзи)
+cs : Název (romaji)
+hu : Cím (romaji)
:_redit_form_original
en : Original title
ru : Оригинальное название
+cs : Originální název
+hu : Eredeti cím
:_redit_form_original_note
en : The original title of this release, leave blank if it already is in the Latin alphabet.
ru : Оригинальное название данного выпуска, осавьте пустым если уже в латинском алфавите.
+cs : Originální název tohoto vydání, ponechte prázdné, pokud již je latinkou.
+hu : A kiadás eredeti neve, hagyd üresen ha latin betűkből áll s már beírtad a "Cím" mezőbe.
:_redit_form_languages
en : Language(s)
ru : Язык(и)
+cs : Jazyk(y)
+hu : Nyelv(ek)
:_redit_form_gtin
en : JAN/UPC/EAN
ru : JAN/UPC/EAN
+cs : JAN/UPC/EAN
+hu :
:_redit_form_catalog
en : Catalog number
ru : Номер в каталоге
+cs : Číslo v katalogu
+hu : Katalógus szám
:_redit_form_website
en : Official website
ru : Официальный веб-сайт
+cs : Oficiální stránky
+hu : Hivatalos weboldal
:_redit_form_released
en : Release date
ru : Дата выпуска
+cs : Datum vydání
+hu : Kiadás ideje
:_redit_form_released_note
en : Leave month or day blank if they are unknown
ru : Если месяц или день неизвестны, оставьте их пустыми
+cs : Ponechte měsíc či den prázdné, pokud jsou neznámé
+hu : Ha a hónap és nap ismeretlen akkor hagyd üresen a mezőt.
:_redit_form_minage
en : Age rating
ru : Возрастной рейтинг
+cs : Věková přístupnist
+hu : Korhatár
:_redit_form_notes
en : Notes
ru : Заметки
+cs : Poznámky
+hu : Megjegyzések
:_redit_form_notes_note
en : Miscellaneous notes/comments, information that does not fit in the above fields.
E.g.: Censored/uncensored or for which releases this patch applies.
ru : Дополнительные заметки/комментарии, информация для которой не нашлось приемлимых полей.
Т.е.: присутствие/отсутствие цензуры, или для каких версий применим данный патч.
+cs : Různé poznámky/komentáře a informace, které se nehodí do předchozích polí.
+ Např.: Cenzurovaná/necenzurovaná verze nebo pro které vydání je patch.
+hu : Különböző észrevételek/megjegyzések, olyan infók amelyek nem illenek a fenti mezőbe.
+ Pl. : Cenzúrázott/Cenzúrázatlan, vagy, hogy melyik kiadásra vonatkozik a patch, stb.
:_redit_form_format
en : Format
ru : Формат
+cs : Formát
+hu : Formátúm
:_redit_form_resolution
en : Resolution
ru : Разрешение
+cs : Rozlišení
+hu : Felbontás
:_redit_form_voiced
en : Voiced
ru : Озвучка
+cs : Hlas
+hu : Szinkronos
:_redit_form_ani_story
en : Story animation
ru : Сюжетная анимация
+cs : Animace příběhu
+hu : Animált történet
:_redit_form_ani_ero
en : Ero animation
ru : Анимация эросцен
+cs : Ero animace
+hu : Animált ero jelenetek
:_redit_form_ani_ero_none
en : Unknown / no ero scenes
ru : Неизвестно / нет эросцен
+cs : Není známo / bez ero scén
+hu : Ismeretlen / nincs ero jelenet
:_redit_form_ani_ero_note
en : Animation in erotic scenes, leave to unknown if there are no ero scenes.
ru : Анимация в эротических сценах, не изменяйте поле если эросцены отсутствуют.
+cs : Animace v ero scénách, ponechte na Není známo, pokud novela nemá ero scény.
+hu : Animált erotikus jelenetek, hagyd 'Ismeretlen'-en ha nem tartalmaz erotikus jelenetek.
:_redit_form_platforms
en : Platforms
ru : Платформы
+cs : Platformy
+hu : Platformok
:_redit_form_media
en : Media
ru : Носители
+cs : Média
+hu : Médium
+
+:_redit_form_med_quantity
+en : -quantity-
+ru : -количество-
+cs : -množství-
+hu : -mennyiség-
+
+:_redit_form_med_medium
+en : -medium-
+ru : -носитель-
+cs : -médium-
+hu : -medium-
+
+:_redit_form_med_remove
+en : remove
+ru : убрать
+cs : odebrat
+hu : eltávolítás
:_redit_form_prod
en : Producers
ru : Компании
+cs : Producenti
+hu : Készítők
:_redit_form_prod_sel
en : Selected producers
ru : Выбранные компании
+cs : Vybraní producenti
+hu : Kiválasztot készítők
:_redit_form_prod_add
en : Add producer
ru : Добавить компанию
+cs : Přidat producenta
+hu : Készitő hozzáadása
+
+:_redit_form_prod_dev
+en : Developer
+ru : Разработчик
+cs : Vývojář
+hu : Fejlesztő
+
+:_redit_form_prod_pub
+en : Publisher
+ru : Издатель
+cs : Vydavatel
+hu : Kiadó
+
+:_redit_form_prod_both
+en : Both
+ru : Оба
+cs : Obojí
+hu : Mindkettő
+
+:_redit_form_prod_addbut
+en : add
+ru : добавить
+cs : přidat
+hu : hozzáadd
+
+:_redit_form_prod_remove
+en : remove
+ru : убрать
+cs : odebrat
+hu : eltávolít
+
+:_redit_form_prod_none
+en : Nothing selected.
+ru : Ничего не выбрано.
+cs : Není nic vybráno.
+hu : Nincsen semmi kiválasztva.
+
+:_redit_form_prod_pformat
+en : Producer textbox must start with an ID (e.g. p17)
+ru : Строка компании должна начинаться с идентификатора (например, p17)
+cs : Textové pole producenta musí začínat ID (např. p17)
+hu : A Készítő szövegdoboz muszáj egy ID-val kezdődjön (pl. p17)
+
+:_redit_form_prod_notfound
+en : Producer not found!
+ru : Компания не найдена!
+cs : Producent nenalezen!
+hu : A készítő nem található!
+
+:_redit_form_prod_double
+en : Producer already selected!
+ru : Компания уже выбрана!
+cs : Producent byl již vybrán!
+hu : A készítő már ki van választva!
:_redit_form_vn
en : Visual novels
ru : Новеллы
+cs : Vizuální novely
+hu : Visual novellek
:_redit_form_vn_sel
en : Selected visual novels
ru : Выбранные новеллы
+cs : Vybrané vizuální novely
+hu : Kiválasztot visual novellek
:_redit_form_vn_add
en : Add visual novel
ru : Добавить новеллу
+cs : Přidat vizuální novelu
+hu : Visual novel hozzáadása
+
+:_redit_form_vn_addbut
+en : add
+ru : добавить
+cs : přidat
+hu : hozzáadd
+
+:_redit_form_vn_remove
+en : remove
+ru : убрать
+cs : odebrat
+hu : eltávolít
+
+:_redit_form_vn_none
+en : Nothing selected.
+ru : Ничего не выбрано.
+cs : Není nic vybráno.
+hu : Nincsen semmi kiválasztva
+
+:_redit_form_vn_vnformat
+en : Visual novel textbox must start with an ID (e.g. v17)
+ru : Строка новеллы должна начинаться с идентификатора (например, v17)
+cs : Vizuální novela musí začínat ID (např. v17)
+hu : A Visual novel szövegdoboz muszáj egy ID-val kezdődjön (pl. v17)
+
+:_redit_form_vn_notfound
+en : Visual novel not found!
+ru : Новелла не найдена!
+cs : Vizuální novela nenalezena!
+hu : A visual novel nem található!
+
+:_redit_form_vn_double
+en : VN already selected!
+ru : Новелла уже выбрана!
+cs : VN již byla vybrána!
+hu : A visual novel már ki van választva!
# Release browser
@@ -1698,22 +3049,32 @@ ru : Добавить новеллу
:_rbrowse_title
en : Browse releases
ru : Обзор выпусков
+cs : Procházet vydání
+hu : Kiadások böngészése
:_rbrowse_col_released
en : Released
ru : Выпущено
+cs : Vydáno
+hu : Kiadva
:_rbrowse_col_minage
en : Rating
ru : Рейтинг
+cs : Hodnocení
+hu : Szavazat
:_rbrowse_col_title
en : Title
ru : Название
+cs : Název
+hu : Cím
:_rbrowse_noresults_title
en : No results found
ru : Совпадений не найдено
+cs : Nenalezeny žádné výsledky
+hu : Nem található semmi
:_rbrowse_noresults_msg
en : Sorry, couldn't find anything that comes through your filters. You might want to disable a few filters to get more results.
@@ -1722,102 +3083,156 @@ en : Sorry, couldn't find anything that comes through your filters. You might wa
ru : Простите, по вашему запросу ничего не найдено. Для получения результатов, попробуйте отключить пару-тройку фильтров.
Также, не забывайте что у нас нет информации об абсолютно всех выпусках. Поэтому, если вы, например, поставили фильтр по разрешению экрана, он исключит все новеллы, не удовлетворяющие выбранному разрешению, даже если в действительности та которую вы ищите имеет искомое разрешение.
+cs : Omlouváme se, nenalezli jsme nic, co by vyhovovalo vašemu nastavení filtrů. Možná najdete více výsledků, pokud nějaké z nich vypnete.
+
+ Také nezapomeňte, že o všech vydáních nemáme všechny údaje. Takže pokud například vyfiltrujete rozlišení obrazovky, pak ve výsledcích nebudou všechna vydání, u kterých neznáme jejich rozlišení, i když by to mohla být právě vydání v rozlišení, o které se zajímáte.
+hu : Bocs, de ezekkel a szűrőkkel nem található semmi. Talán ki kéne kapcsolj egy párat, hogy több eredmény jöjjön ki.
+
+ Valamint, ne felejtsd el, hogy nincsen minden kiadásról elégséges információnk. Szóval ha bekapcsolod a felbontásra vonatkozó szűrőt, azokat nem mutatja amelyeknek nincs megadva a felbontásuk, még akkor is ha valójában annyi, mint amennyit te keresel.
:_rbrowse_filters
en : Filters
ru : Фильтры
+cs : Filtry
+hu : Szűrők
:_rbrowse_minage
en : Age rating
ru : Возрастной рейтинг
+cs : Věková přístupnost
+hu : Korhatár
:_rbrowse_ge
en : Greater than or equal to
ru : Больше или равно
+cs : Větší než nebo rovno
+hu : Nagyobb vagy egyenlő
:_rbrowse_le
en : Less than or equal to
ru : Меньше или равно
+cs : Menší než nebo rovno
+hu : Kisebb vagy egyenlő
:_rbrowse_resolution
en : Screen resolution
ru : Разрешение экрана
+cs : Rozlišení obrazovky
+hu : Felbontás
:_rbrowse_type
en : Release type
ru : Тип выпуска
+cs : Typ vydání
+hu : Kiadás típus
:_rbrowse_all
en : All
ru : Все
+cs : Vše
+hu : Minden
:_rbrowse_patch
en : Patch status
ru : Статус патча
+cs : Status patche
+hu : Patch állapot
:_rbrowse_patchonly
en : Only patches
ru : Только патчи
+cs : Pouze patche
+hu : Csak patchek
:_rbrowse_patchnone
en : Only standalone releases
ru : Только самостоятельные выпуски
+cs : Pouze samostatná vydání
+hu : Csak különálló kiadások
:_rbrowse_freeware
en : Freeware
ru : Freeware
+cs : Freeware
+hu :
:_rbrowse_freewareonly
en : Freeware only
ru : Только Freeware
+cs : Pouze freeware
+hu : Csak Freeware
:_rbrowse_freewarenone
en : Only non-free releases
ru : Только несвободные
+cs : Pouze placená vydání
+hu : Minden nem ingyenes kiadás
:_rbrowse_doujin
en : Doujin
ru : Додзинси
+cs : Doujin
+hu :
:_rbrowse_doujinonly
en : Only doujin releases
ru : Только додзинси
+cs : Pouze doujin vydání
+hu : Csak doujin kiadások
:_rbrowse_doujinnone
en : Only commercial releases
ru : Только коммерческие
+cs : Pouze komerční vydání
+hu : Csak kereskedelmi kiadások
:_rbrowse_dateafter
en : Released after
ru : Выпущены после
+cs : Vydáno po
+hu : Kiadva útán
:_rbrowse_datebefore
en : Released before
ru : Выпущены до
+cs : Vydáno před
+hu : Kiadva előtt
:_rbrowse_languages
en : Languages
ru : Языки
+cs : Jazyky
+hu : Nyelvek
:_rbrowse_boolor
en : boolean or, selecting more gives more results
ru : булевое 'или', чем больше выбрано, тем больше даёт результатов
+cs : boolean nebo, výběr více dá více výsledků
+hu : Boole féle értékhalmaz(igaz/hamis), ha többet jelölsz be, több eredményt ad ki
:_rbrowse_platforms
en : Platforms
ru : Платформы
+cs : Platformy
+hu : Platformok
:_rbrowse_media
en : Media
ru : Носители
+cs : Média
+hu : Médium
:_rbrowse_apply
en : Apply
ru : Применить
+cs : Použít
+hu : Alkalmaz
:_rbrowse_clear
en : Clear
ru : Очистить
+cs : Začít znovu
+hu : Törlés
@@ -1833,88 +3248,132 @@ ru : Очистить
:_tagp_title
en : [index,_1,Meta tag,Tag]: [_2]
ru : [index,_1,Мета тег,Тег]: [_2]
+cs : [index,_1,Meta tag,Tag]: [_2]
+hu : [index,_1,Meta Címke,Címke]: [_2]
:_tagp_del_title
en : Tag deleted
ru : Тег удалён
+cs : Tag smazán
+hu : Címke törölve
:_tagp_del_msg
en : This tag has been removed from the database, and cannot be used or re-added.
File a request on the [url,/t/db,discussion board] if you disagree with this.
ru : Данный тег удалён из базы данных, и не может быть использован.
Напишите на [url,/t/db,форуме], если вы с этим не согласны.
+cs : Tento tag byl smazán z databáze a nemůže být použit nebo znovu přidán.
+ Pokud s tímto nesouhlasíte, napište žádost na [url,/t/db,diskusní board].
+hu : Ez a címke törölve lett az adatbázisból, s nem lehetséges visszaállítani vagy használni.
+ Tegyél panaszt a [url,/t/db,fórumon] ha nem értesz egyet.
:_tagp_pending_title
en : Waiting for approval
ru : Ждёт одобрения
+cs : Čeká na schválení
+hu : Megerősítésre vár
:_tagp_pending_msg
en : This tag is waiting for a moderator to approve it. You can still use it to tag VNs as you would with a normal tag.
ru : Данный тег ожидает одобрения модератором. Однако, вы можете использовать его для пометки новелл, как и любой нормальный тег.
+cs : Tento tag čeká na schválení moderátorem. Mezitím ho stále můžete používat jako normální tag.
+hu : Ez a címke arra vár, hogy egy moderátor elfogadja. Azonban használhatod bármely VN-nél nyugodtan akárcsak egy normális címkét.
:_tagp_addchild
en : Create child tag
ru : Создать дочерний тег
+cs : Vytvořit závislý tag
+hu : Gyerek címke létrehozása
:_tagp_indexlink
en : Tags
ru : Теги
+cs : Tagy
+hu : Címkék
:_tagp_aliases
en : Aliases
ru : Прочие названия
+cs : Aliasy
+hu : Más nevek
:_tagp_childs
en : Child tags
ru : Дочерние теги
+cs : Závislé tagy
+hu : Gyerek címkék
:_tagp_tree
en : Tag tree
ru : Древо тега
+cs : Strom tagů
+hu : Címke fa
:_tagp_moretags
en : [_1] more [quant,_1,tag,tags]
ru : ещё [_1] [quant,_1,тег,тега,тегов]
+cs : [_1] [quant,_1,další tag,další tagy,dalších tagů]
+hu : [_1] több [quant,_1,címke,címkék]
:_tagp_vnlist
en : Visual novels
ru : Новеллы
+cs : Vizuální novely
+hu : Visual novellek
:_tagp_spoil0
en : Hide spoilers
ru : Скрыть спойлеры
+cs : Skrýt spoilery
+hu : Spoilerek elrejtése
:_tagp_spoil1
en : Show minor spoilers
ru : Показать лёгкие спойлеры
+cs : Ukázat menší spoilery
+hu : Kisebb spoilerek megjelenítése
:_tagp_spoil2
en : Show major spoilers
ru : Показать жёсткие спойлеры
+cs : Ukázat všechny spoilery
+hu : Nagyobb spoilerek megjelenítése
:_tagp_novn
en : This tag has not been linked to any visual novels yet, or they were hidden because of the spoiler settings.
ru : Этот тег пока не содержит ссылок ни на одну новеллу, либо они скрыты из-за настроек отображения спойлеров.
+cs : Tento tag ještě nebyl použit v žádné vizuální novele nebo byly tyto skryty kvůli nastavení spoilerů.
+hu : Ez a címke eddig egy visual novelhez sincs hozzárendelve, vagy el lettek rejtve a spoiler-ek beállítása során.
:_tagp_cached
en : NOTE: This list is cached, it can take up to 24 hours after a visual novel has been tagged for it to show up on this page.
ru : ЗАМЕЧАНИЕ: Данный список кэшируется, поэтому может пройти вплоть до 24 часов, прежде чем новелла, помеченная тегом, отобразится на этой странице.
+cs : POZNÁMKA: Tento list se ukládá do cache, takže může trvat až 24 hodin po otagování vizuální novely do objevení se na této stránce.
+hu : FIGYELEM: Ez a lista le van mentve a gyorsítótárba, beletelhet 24 órába míg a visual novelhez újonnan hozzárendelt címke megjelenik ezen az oldalon.
:_tagp_vncol_score
en : Score
ru : Балл
+cs : Skóre
+hu : Pontozás
:_tagp_vncol_title
en : Title
ru : Название
+cs : Název
+hu : Cím
:_tagp_vncol_rel
en : Released
ru : Выпуск
+cs : Vydáno
+hu : Kiadva
:_tagp_vncol_pop
en : Popularity
ru : Популярность
+cs : Popularita
+hu : Népszerűség
# Tag add/edit form (/g+/edit, /g+/add, /g/add)
@@ -1922,26 +3381,38 @@ ru : Популярность
:_tagedit_err_notfound
en : Tag '[_1]' not found
ru : Тег '[_1]' не найден
+cs : Tag '[_1]' nenalezen
+hu : A(z) '[_1]' címke nem található
:_tagedit_title_add
en : Add child tag to [_1]
ru : Добавление дочернего тега для '[_1]'
+cs : Přidat závislý tag tagu [_1]
+hu : Gyerek címke hozzárendelése a(z) [_1] - hez
:_tagedit_title_edit
en : Edit tag: [_1]
ru : Править тег: [_1]
+cs : Editovat tag: [_1]
+hu : Címke szerkesztése: [_1]
:_tagedit_title_new
en : Add new tag
ru : Добавить новый тег
+cs : Přidat nový tag
+hu : Új címke készítése
:_tagedit_req_title
en : Requesting new tag
ru : Запросить новый тег
+cs : Požaduje se nový tag
+hu : Új címke kérése
:_tagedit_req_subtitle
en : Your tag must be approved
ru : Ваш тег должен быть одобрен
+cs : Váš tag musí být schválen
+hu : A címkédet előbb el kell fogadják
:_tagedit_req_msg
en : Because all tags have to be approved by moderators, it can take a while before it will show up in the tag list
@@ -1952,68 +3423,108 @@ ru : В связи с тем что все теги должны быть утв
займёт некоторое время. Однако, вы всё равно можете голосовать за тег, даже если тот ещё не был утверждён.
[br][br]
Также, пожалуйста убедитесь что прочли [url,/d10,рекомендации], тогда вам не придётся гадать, утвердят ваш тег или нет.
+cs : Protože všechny tagy musí být schválené moderátorem, může chvilku trvat, než se objeví na listu tagů
+ nebo na stránce vizuální novely. I před schválením ale můžete stále tag hodnotit.
+ [br][br]
+ Také se ujistěte, že jste přečetli [url,/d10,doporučení], abyste věděli, zda váš tag bude přidán nebo ne.
+hu : Mivel minden címkét külön el kell fogadjanak a moderátorok, beletarthat egy kis időbe míg azok megjelennek a listába
+ vagy a visual novellek oldalain. Viszont, akkor is tudsz egy címkére szavazni, ha még nincsen elfogadva.
+ [br][br]
+ Valamint, olvasd el az [url,/d10,útmutatót], ha biztosra akarsz menni, hogy a címkédet el fogják fogadni vagy nem.
:_tagedit_frm_name
en : Primary name
ru : Основное название
+cs : Hlavní jméno
+hu : Elsődleges név
:_tagedit_frm_by
en : Added by
ru : Добавлено
+cs : Přidal
+hu : Hozzáadta
:_tagedit_frm_state
en : State
ru : Состояние
+cs : Stav
+hu : Állapot
:_tagedit_frm_state0
en : Awaiting moderation
ru : Ждёт модерации
+cs : Čeká na schválení
+hu : Moderálásra vár
:_tagedit_frm_state1
en : Deleted/hidden
ru : Удалён/скрыт
+cs : Smazán/skryt
+hu : Rejtett/törölve
:_tagedit_frm_state2
en : Approved
ru : Одобрен
+cs : Schválen
+hu : Elfogadva
:_tagedit_frm_meta
en : This is a meta-tag (only to be used as parent for other tags, not for linking to VN entries)
ru : Это мета-тег (его нельзя добавлять к новеллам, он может использоваться лишь как родитель для других тегов)
+cs : Toto je meta-tag (používá se pouze jako mateřský pro ostatní tagy, ne pro vlastní vizuální novely)
+hu : Ez itt egy meta-címke (csakis más címkék szülője ként használandó, nem pedig VN bejegyzésekhez)
:_tagedit_frm_meta_warn
en : WARNING: Checking this option or selecting "Deleted" as state will permanently delete all existing VN relations!
ru : ПРЕДУПРЕЖДЕНИЕ: Если выбрать данную опцию, либо выставить состояние "Удалён", произойдёт немедленное удаление всех существующих связей с новеллами!
+cs : VAROVÁNÍ: Zaškrtnutí této volby nebo výběr položky "Smazáno" jako stavu smaže automaticky tag u všech vizuálních novel!
+hu : FIGYELEM: Ezen opció bejelölése vagy a "Törölve" állapot kiválasztása véglegesen megsemmisít minden összefüggést a VN-ekel!
:_tagedit_frm_alias
en : Aliases
(separated by newlines)
ru : Прочие названия
(каждое с новой строки)
+cs : Aliasy
+ (odděleny novou řádkou)
+hu : Más nevek
+ (új sorokkal elválasztva)
:_tagedit_frm_desc
en : Description
ru : Описание
+cs : Popis
+hu : Leírás
:_tagedit_frm_desc_msg
en : What should the tag be used for? Having a good description helps users choose which tags to link to a VN.
ru : Для пометки чего будет использоваться тег? Хорошее описание поможет пользователям выбрать подходящий тег для подходящей новеллы.
+cs : Pro co by měl být tag používán? Tagy s dobrým popisem usnadňují uživatelům jejich používání u vizuálních novel.
+hu : Mire és mikor használja valaki a címkét? Egy jó leírás megkönnyíti a felhasználók dolgát mikor hozzá akarják rendelni egy VN-hez.
:_tagedit_frm_parents
en : Parent tags
ru : Родительские теги
+cs : Mateřské tagy
+hu : Szülő címkék
:_tagedit_frm_parents_msg
en : Comma separated list of tag names to be used as parent for this tag.
ru : Теги, которые будут задействованы для этого в качестве родительских. Через запятую.
+cs : Seznam tagů, oddělených čárkou, které mají být použity jako mateřské pro tento tag
+hu : Vesszővel elválasztott címke nevek amelyek szülő ként szolgálnak ennek a címkének.
:_tagedit_frm_merge
en : Merge tags
ru : Объединение тегов
+cs : Sjednotit tagy
+hu : Címkék egyesítése
:_tagedit_frm_merge_tags
en : Tags to merge
ru : Теги для объединения
+cs : Tagy ke sjednocení
+hu : Egyesítésre váró címkék
:_tagedit_frm_merge_msg
en : Comma separated list of tag names to merge into this one. All votes and aliases/names will be moved over
@@ -2022,6 +3533,12 @@ en : Comma separated list of tag names to merge into this one. All votes and ali
ru : Список тегов для объединения в данный, через запятую. Все голоса и побочные названия будут перемещены в
данный тег, а все старые теги удалены. Оставьте поле пустым, если вы не собираетесь проводить объединение.
[br]ПРЕДУПРЕЖДЕНИЕ: данную операцию отменить невозможно!
+cs : Seznam tagů, oddělených čárkou, ke sjednocení do tohoto. Všechny hlasy a aliasy/jména budou přesunuty do tohoto tagu
+ a staré tagy budou smazány. Nechte toto pole prázdné, pokud nehodláte nic sjednocovat.
+ [br]VAROVÁNÍ: tato akce nejde vrátit!
+hu : Egy lista, veszővel elválasztott címke nevekkel amelyeket egyesíteni akarsz ebbe az egybe. Minden szavazat és alias/nevek át lesznek
+ helyezve ehhez az új címkéhez és a régi címkék pedig törölve lesznek. Ha nem akarsz címkéket egyesíteni akkor ezt a mezőt hagyd üresen.
+ [br]FIGYELEM: ez a művelet végleges, nem lehet visszavonni!
# Plain tag browser (/g/list)
@@ -2029,42 +3546,62 @@ ru : Список тегов для объединения в данный, че
:_tagb_title
en : Browse tags
ru : Обзор тегов
+cs : Procházet tagy
+hu : Címkék böngészése
:_tagb_state-1
en : All
ru : Все
+cs : Všechny
+hu : Mind
:_tagb_state0
en : Awaiting moderation
ru : Ожидающие модерации
+cs : Čekající na schválení
+hu : Moderálásra vár
:_tagb_state1
en : Deleted
ru : Удалённые
+cs : Smazané
+hu : Törölve
:_tagb_state2
en : Accepted
ru : Одобренные
+cs : Přijaté
+hu : Elfogadva
:_tagb_noresults
en : No results found
ru : Совпадений не найдено
+cs : Nenalezeny žádné výsledky
+hu : Keresés sikertelen
:_tagb_col_added
en : Created
ru : Созданы
+cs : Vytvořené
+hu : Létrehozva
:_tagb_col_name
en : Tag
ru : Тег
+cs : Tag
+hu : Címke
:_tagb_note_awaiting
en : awaiting moderation
ru : ждёт модерации
+cs : čeká na schválení
+hu : moderálásra vár
:_tagb_note_del
en : deleted
ru : удалён
+cs : smazán
+hu : törölve
# VN<->Tag voting (/v+/tagmod)
@@ -2072,60 +3609,132 @@ ru : удалён
:_tagv_title
en : Add/remove tags for [_1]
ru : Добавление/удаление тегов для [_1]
+cs : Přidat/odebrat tag pro novelu [_1]
+hu : Címke hozzáadása/törlése [_1]-hez
:_tagv_msg_title
en : Tagging
ru : Пометка тегами
+cs : Tagování
+hu : Címkézés
:_tagv_msg_guidelines
en : Make sure you have read the [url,/d10,guidelines]!
ru : Пожалуйста, убедитесь что прочли [url,/d10,рекомендации]!
+cs : Ujistěte se, že jste přečetli [url,/d10,doporučení]!
+hu : Olvasd el az [url,/d10,útmutatókat]!
:_tagv_msg_submit
en : Don't forget to hit the submit button on the bottom of the page to make your changes permanent.
ru : Не забудьте сохранить изменения, нажав соответствующую кнопку в низу страницы, иначе они не вступят в силу.
+cs : Nezapomeňte potvrdit tlačítko na spodku stránky, abyste uložili změny trvale.
+hu : Ne felejtsd el megnyomni a beküldés gombot a lap alján, hogy véglegesítsd a változtatásokat.
:_tagv_msg_cache
en : Some tag information on the site is cached, it can take up to an hour for your changes to be visible everywhere.
ru : Информация о некоторых тегах на данном сайте кэшируется, поэтому ваши изменения станут видны везде в течение часа.
+cs : Některé informace o tazích na této stránce jsou v cache a může zabrat až hodinu než budou změny vidět.
+hu : Valamely címkékkel kapcsolatos információ le van mentve a gyorsítótárba, beletelhet egy órába is mire mindenhol láthatóvá válnak a változtatások.
:_tagv_frm_title
en : Tags
ru : Теги
+cs : Tagy
+hu : Címkék
:_tagv_col_you
en : You
ru : Вы
+cs : Vy
+hu : Te
:_tagv_col_others
en : Others
ru : Остальные
+cs : Ostatní
+hu : Egyebek
:_tagv_col_tag
en : Tag
ru : Тег
+cs : Tag
+hu : Címke
:_tagv_col_rating
en : Rating
ru : Рейтинг
+cs : Hodnocení
+hu : Osztályozás
:_tagv_col_spoiler
en : Spoiler
ru : Спойлер
+cs : Spoiler
+hu : Spoiler
:_tagv_save
en : Save changes
ru : Сохранить изменения
+cs : Uložit změny
+hu : Változtatások mentése
:_tagv_add
en : Add tag
ru : Добавить тег
+cs : Přidat tag
+hu : Címke hozzáadása
:_tagv_addmsg
en : Check the [url,/g,tag list] to browse all available tags.[br]
Can't find what you're looking for? [url,/g/new,Request a new tag].
ru : Откройте [url,/g,список тегов] чтобы увидеть все доступные теги.[br]
Не нашли что искали? Вы можете [url,/g/new,запросить новый тег].
+cs : Zkontrolujte [url,/g,list tagů] pro seznam všech dostupných tagů.[br]
+ Nenašli jste, co jste hledali? [url,/g/new,Vyžádejte si nový tag].
+hu : Menj a [url,/g,cimke listára] ha szeretnél böngészni az összes rendelkezésre álló címke közt.[br]
+ Nem találod azt amelyiket keresed? [url,/g/new,Itt kérhetsz egy új címkét].
+
+:_tagv_spoil0
+en : neutral
+ru : нейтральный
+cs : neutrální
+hu : semleges
+
+:_tagv_spoil1
+en : no spoiler
+ru : без спойлеров
+cs : bez spoilerů
+hu : nem spoiler
+
+:_tagv_spoil2
+en : minor spoiler
+ru : лёгкий спойлер
+cs : menší spoilery
+hu : kisebb spoiler
+
+:_tagv_spoil3
+en : major spoiler
+ru : тяжёлый спойлер
+cs : všechny spoilery
+hu : nagyobb spoiler
+
+:_tagv_notfound
+en : Item not found!
+ru : Элемент не найден!
+cs : Předmět nenalezen!
+hu : A tárgy nem található!
+
+:_tagv_nometa
+en : Can't use meta tags here!
+ru : Использование мета-тегов здесь запрещено!
+cs : Zde se nedají použít meta-tagy!
+hu : Itt nem használhatsz meta címkéket!
+
+:_tagv_double
+en : Tag is already present!
+ru : Тег уже указан!
+cs : Tag už je přiřazen!
+hu : A címke már jelen van!
# User tag list (/u+/tags)
@@ -2133,34 +3742,50 @@ ru : Откройте [url,/g,список тегов] чтобы увидеть
:_tagu_title
en : Tags by [_1]
ru : Теги [_1]
+cs : Tagy podle [_1]
+hu : [_1] által létrehozott címkék
:_tagu_spoilerwarn
en : Warning: spoilery tags are not hidden in this list!
ru : Предупреждение: в этом списке отображаются теги-спойлеры!
+cs : Varování: spoilerové tagy nejsou v tomto seznamu skryté!
+hu : Figyelem: ebbe a listába a spoiler-t tartalmazó címkék nincsenek elrejtve!
:_tagu_notags
en : [_1] doesn't seem to have used the tagging system yet...
ru : Похоже, что [_1] пока не использует систему тегов...
+cs : Nezdá se, že by [_1] zatím použil tagovací systém...
+hu : Úgy tűnik, hogy [_1] még nem használta a címkéző rendszert...
:_tagu_col_num
en : #VNs
ru : #Новеллы
+cs : #VN
+hu : #VN-nek
:_tagu_col_name
en : Tag
ru : Тег
+cs : Tag
+hu : Címke
:_tagu_spoil0
en : No spoiler
ru : Нет спойлера
+cs : Bez spoilerů
+hu : Nem spoiler
:_tagu_spoil1
en : Minor spoiler
ru : Лёгкий спойлер
+cs : Menší spoilery
+hu : Kisebb spoiler
:_tagu_spoil2
en : Major spoiler
ru : Жёсткий спойлер
+cs : Všechny spoilery
+hu : Nagyobb spoiler
# Tag index (/g)
@@ -2168,42 +3793,62 @@ ru : Жёсткий спойлер
:_tagidx_title
en : Tag index
ru : Индекс тега
+cs : Seznam tagů
+hu : Címke index
:_tagidx_create
en : Create new tag
ru : Создать новый тег
+cs : Vytvořit nový tag
+hu : Új címke létrehozása
:_tagidx_search
en : Search tags
ru : Поиск тегов
+cs : Hledat tag
+hu : Címkék keresése
:_tagidx_browseall
en : Browse all tags
ru : Обзор всех тегов
+cs : Procházet všechny tagy
+hu : Összes címke böngészése
:_tagidx_recent
en : Recently added
ru : Недавно добавлены
+cs : Poslední přidané
+hu : Nemrég hozzáadva
:_tagidx_popular
en : Popular tags
ru : Популярные теги
+cs : Populární tagy
+hu : Népszerű címkék
:_tagidx_queue
en : Awaiting moderation
ru : Ожидают модерации
+cs : Čekající na schválení
+hu : Moderálásra vár
:_tagidx_queue_empty
en : Moderation queue empty! yay!
ru : В очереди модерации пусто! Няа!
+cs : Fronta ke schválení prázdná! Jej!
+hu : Moderálásra váró lista üres! Hurrá!
:_tagidx_queue_link
en : Moderation queue
ru : Очередь модерации
+cs : Fronta ke schválení
+hu : Moderálásra váró lista
:_tagidx_denied
en : Denied tags
ru : Отклонённые теги
+cs : Zamítnuté tagy
+hu : Megtagadott címkék
@@ -2219,42 +3864,62 @@ ru : Отклонённые теги
:_wishlist_title_my
en : My wishlist
ru : Мой список желаемого
+cs : Můj wishlist
+hu : Kívánságlistám
:_wishlist_title_other
en : [_1]'s wishlist
ru : Список желаемого [_1]
+cs : Wishlist uživatele [_1]
+hu : [_1] kívánság listája
:_wishlist_noresults
en : Wishlist empty...
ru : Список пуст...
+cs : Wishlist je prázdný...
+hu : Kívánságlista üres...
:_wishlist_prio_all
en : All priorities
ru : Все приоритеты
+cs : Všechny priority
+hu : Minden prioritás
:_wishlist_col_title
en : Title
ru : Название
+cs : Název
+hu : Cím
:_wishlist_col_prio
en : Priority
ru : Приоритет
+cs : Priorita
+hu : Prioritás
:_wishlist_col_added
en : Added
ru : Добавлено
+cs : Přidáno
+hu : Hozzáadva
:_wishlist_select
en : -- with selected --
ru : -- с выбранными --
+cs : -- s vybranými --
+hu : -- kiválasztva --
:_wishlist_changeprio
en : Change priority
ru : Изменить приоритет
+cs : Změnit prioritu
+hu : Prioritás megváltoztatása
:_wishlist_remove
en : remove from wishlist
ru : убрать из списка желаемого
+cs : odstranit z wishlistu
+hu : eltávolítás a kívánságlistából
# VN list (/u+/list)
@@ -2262,54 +3927,80 @@ ru : убрать из списка желаемого
:_rlist_title_my
en : My visual novel list
ru : Мой список новелл
+cs : Můj list vizuálních novel
+hu : Visual novel listám
:_rlist_title_other
en : [_1]'s visual novel list
ru : Список новелл [_1]
+cs : List vizuálních novel uživatele [_1]
+hu : [_1] visual novel listája
:_rlist_voted_all
en : All
ru : Все
+cs : Všechny
+hu : Mind
:_rlist_voted_only
en : Only voted
ru : Проголосованные
+cs : Pouze hodnocené
+hu : Csak a szavazottak
:_rlist_voted_none
en : Hide voted
ru : Скрыть проголосованные
+cs : Skrýt hodnocené
+hu : Szavazottak elrejtése
:_rlist_col_title
en : Title
ru : Название
+cs : Název
+hu : Cím
:_rlist_col_releases
en : Releases
ru : Выпуски
+cs : Vydání
+hu : Kiadások
:_rlist_col_vote
en : Vote
ru : Голос
+cs : Hodnocení
+hu : Szavazat
:_rlist_selection
en : -- with selected --
ru : -- с выбранными --
+cs : -- s vybranými --
+hu : -- kiválasztva --
:_rlist_changerel
en : Change release status
ru : Смена статуса выпуска
+cs : Změnit stav vydání
+hu : Kiadás állapotának megváltoztatása
:_rlist_changeplay
en : Change play status
ru : Смена статуса игры
+cs : Změnit stav hraní
+hu : Játszás állapot megváltoztatása
:_rlist_del
en : remove from list
ru : убрать из списка
+cs : odstranit z listu
+hu : eltávolítás a listából
:_rlist_releasenote
en : * Obtained/finished/total
ru : * Приобретено/прочитано/всего
+cs : * Sehnáno/dohráno/celkem
+hu : * Megszerezve/befejezve/összesen
@@ -2325,66 +4016,98 @@ ru : * Приобретено/прочитано/всего
:_userpage_title
en : [_1]'s profile
ru : Учётная запись [_1]
+cs : Profil uživatele [_1]
+hu : [_1] profilja
:_userpage_username
en : Username
ru : Имя пользователя
+cs : Uživatelské jméno
+hu : Felhasználónév
:_userpage_registered
en : Registered
ru : Регистрация
+cs : Registrován
+hu : Regisztrálva
:_userpage_edits
en : Edits
ru : Правки
+cs : Editace
+hu : Szerkesztések
:_userpage_votes
en : Votes
ru : Отдано голосов
+cs : Hlasování
+hu : Szavazatok
:_userpage_votes_item
en : [url,_1,_2] ([_3] average)
ru : [url,_1,_2] ([_3] в среднем)
+cs : [url,_1,_2] (průměr [_3])
+hu : [url,_1,_2] ([_3] átlag)
:_userpage_hidden
en : hidden
ru : скрыто
+cs : skryto
+hu : elrejtve
:_userpage_tags
en : Tags
ru : Теги
+cs : Tagy
+hu : Címkék
:_userpage_tags_item
en : [_1] [quant,_1,vote,votes] on [_2] distinct [quant,_2,tag,tags] and [_3] visual [quant,_3,novel,novels]
ru : [_1] [quant,_1,голос,голоса,голосов] по [_2][quant,_2,-у тегу,-м различным тегам,-и различным тегам] и [_3][quant,_3,-й новелле,-м новеллам,-и новеллам]
+cs : [_1] [quant,_1,hlasy,hlasy,hlasů] pro [_2] [quant,_2,tag,různé tagy,různých tagů] a [_3] [quant,_3,vizuální novelu,vizuální novely,vizuálních novel]
+hu : [_1] [quant,_1,szavazat,szavazat] [_2] egyedi [quant,_2,címkére,címkére] és [_3] visual [quant,_3,novelre,novelre]
:_userpage_list
en : List stats
ru : Статистика по спискам
+cs : Statistiky listu
+hu : Lista adatok
:_userpage_list_item
en : [_1] [quant,_1,release,releases] of [_2] visual [quant,_2,novel,novels]
ru : [_1] [quant,_1,выпуск,выпуска,выпусков] [_2][quant,_2,-й новеллы,-х новелл,-и новелл]
+cs : [_1] [quant,_1,vydání,vydání,vydání] [_2] [quant,_2,vizuální novely,vizuálních novel,vizuálních novel]
+hu : [_1] [quant,_1,kiadás,kiadás] és [_2] visual [quant,_2,novel,novel]
:_userpage_forum
en : Forum stats
ru : Статистика по форуму
+cs : Statistiky fóra
+hu : Fórum adatok
:_userpage_forum_item
en : [_1] [quant,_1,post,posts], [_2] new [quant,_2,thead,threads].
ru : [_1] [quant,_1,сообщение,сообщения,сообщений], [_2] [quant,_2,новая тема,новые темы,новых тем]
+cs : [_1] [quant,_1,příspěvek, příspěvky, příspěvků], [_2] [quant,_2,nový thread, nové thready, nových threadů].
+hu : [_1] [quant,_1,válasz,válaszok], [_2] új [quant,_2,téma,téma].
:_userpage_forum_browse
en : Browse posts
ru : Обзор сообщений
+cs : Procházet příspěvky
+hu : Hozzászólások böngészése
:_userpage_votestats
en : Vote statistics
ru : Статистика по голосованиям
+cs : Statistiky hlasování
+hu : Szavazási statisztikák
:_userpage_changes
en : Recent changes
ru : Свежие правки
+cs : Poslední změny
+hu : Legutóbbi változtatások
# Login form (/u/login)
@@ -2392,22 +4115,32 @@ ru : Свежие правки
:_login_title
en : Login
ru : Вход
+cs : Přihlásit
+hu : Bejelentkezés
:_login_username
en : Username
ru : Имя пользователя
+cs : Uživatelské jméno
+hu : Felhasználónév
:_login_register
en : No account yet?
ru : Нет учётной записи?
+cs : Ještě nemáte účet?
+hu : Nincs még fiókod?
:_login_password
en : Password
ru : Пароль
+cs : Heslo
+hu : Jelszó
:_login_forgotpass
en : Forgot your password?
ru : Забыли пароль?
+cs : Zapomněli jste své heslo?
+hu : Elfelejtetted a jelszavad?
# Reset password (/u/newpass)
@@ -2435,13 +4168,38 @@ ru : Привет, [_1]
Постарайтесь больше не забывать свой пароль! :-)
vndb.org
+cs : Zdravíme, [_1]
+
+ Vaše heslo bylo změněno, nyní se můžete přihlásit na http://vndb.org
+ s následujícími informacemi:
+
+ Uživatelské jmno: [_1]
+ Heslo: [_2]
+
+ A snažte se už heslo znovu nezapomenout! :-)
+hu : Üdv [_1]
+
+ A jelszavad vissza lett állítva, most már bejelentkezhetsz a http://vndb.org/ címen az
+ új adataiddal:
+
+ Felhasználónév: [_1]
+ Jelszó: [_2]
+
+ Legközelebb próbáld meg nem elfelejteni a jelszavad! :-)
+
+ vndb.org
+
:_newpass_mail_subject
en : New password for [_1]
ru : Новый пароль для [_1]
+cs : Nové heslo pro uživatele [_1]
+hu : Új jelszó [_1]-nak
:_newpass_title
en : Forgot password
ru : Забытый пароль
+cs : Zapomenuté heslo
+hu : Elfelejtetted a jelszavad?
:_newpass_msg
en : Forgot your password and can't login to VNDB anymore?
@@ -2451,22 +4209,36 @@ ru : Забыли пароль и больше не можете авториз
Без паники! Всё что вам нужно - указать адрес электронной почты, который
вы использовали для регистрации в VNDB, и мы вышлем вам новый пароль за
считанные минуты!
+cs : Zapomněli jste své heslo a nemůžete se přihlásit na VNDB?
+ Nebojte! Stačí zadat e-mailovou adresu, se kterou jste se na VNDB registrovali,
+ a my vám do několika minut pošleme heslo nové!
+hu : Elfelejtetted a jelszavad és nem tudsz bejelentkezni a VNBD-be?
+ Ne aggódj! Csak add meg nekünk az email címet amivel regisztráltál,
+ s egy pár percen belül kapsz tőlünk egy új jelszót!
:_newpass_reset_title
en : Reset password
ru : Сброс пароля
+cs : Změnit heslo
+hu : Jelszó visszaállitása
:_newpass_mail
en : Email
ru : E-mail
+cs : E-mail
+hu :
:_newpass_sent_title
en : New password
ru : Новый пароль
+cs : Nové heslo
+hu : Új jelszó
:_newpass_sent_subtitle
en : Password reset
ru : Сбросить пароль
+cs : Změnit heslo
+hu : Jelszó visszaállitás
:_newpass_sent_msg
en : Your password has been reset and your new password should reach your mailbox in a few minutes.[br]
@@ -2477,16 +4249,28 @@ ru : Ваш пароль был сброшен. Через несколько м
Вы всегда можете изменить пароль после того как прошли авторизацию.[br]
[br]
[url,/u/login,Вход] - [url,/,Главная]
+cs : Vaše heslo bylo změněno a vaše nové heslo by mělo být za několik minut doručeno do vaší schránky.[br]
+ Vždy můžete změnit vaše heslo po přihlášení.[br]
+ [br]
+ [url,/u/login,Přihlásit] - [url,/,Home]
+hu : A jelszavad vissza lett állítva s pár percen belül kapsz egy ujjat a postafiókodba.[br]
+ Miután bejelentkezel, nyugodtan megváltoztathatod a jelszavad amire akarod.[br]
+ [br]
+ [url,/u/login,Bejelentkezés] - [url,/,Kezdőlap]
# Register new account (/u/register)
:_register_title
en : Create an account
ru : Создание учётной записи
+cs : Vytvořit účet
+hu : Új fiók létrehozása
:_register_why
en : Why should I register?
ru : Для чего нужна регистрация?
+cs : Proč bych se měl registrovat?
+hu : Miért kéne regisztráljak?
:_register_why_msg
en : Creating an account is completely painless, the only thing we need to know is your prefered username
@@ -2509,36 +4293,70 @@ ru : Создание учётной записи совершенно безо
- Следить за всеми новеллами и выпусками, которые у вас есть, в которые вы бы хотели сыграть, в которые играете, либо уже доиграли[br]
- Голосовать за понравившиеся или, наоборот, не понравившиеся новеллы[br]
- Вступать в обсуждения на ветках форума
+cs : Tvorba účtu je zcela bezbolestná, jediné údaje, které potřebujeme znát jsou uživatelské jméno, které chcete, a vaše heslo.
+ Můžete i použít e-mail, který není váš, jelikož ani neověřujeme, že jste nám zadali opravdu váš e-mail.
+ Pamatujte ale, že pokud jste zadali neplatnou e-mailovou adresu, pak byste si měli dobře zapamatovat vaše heslo...[br]
+ [br]
+ V každém případě, založení účtu vám přináší několik výhod oproti běžným návštěvníkům[br]
+ - Můžete přispívat do databáze editací všech dat a přidáváním dat nových[br]
+ - Mějte přehled o všech vizuálních novelách, které máte, chtěli byste hrát, hrajete, nebo jste dohráli[br]
+ - Hlasujte pro vizuální novely, které se vám líbily nebo nelíbily[br]
+ - Přispívejte do diskusí na boardech
+hu : Egy fiókot nagyon egyszerű készíteni, minden ami kell egy felhasználónév és egy jelszó. Bármilyen email címet használhatsz,
+ minket nem érdekel, hogy a tied vagy nem, mivel nincs külön aktiválás. Viszont ha nem jó emailt adsz meg nekünk
+ akkor jól vésd az eszedbe a jelszavad, különben nem tudunk neked segíteni ha elfelejted.[br]
+ [br]
+ Nos, a saját fiókkal rendelkezők több előnybe is részesülnek mint az egyszerű látogatók[br]
+ - Hozzájárulhatsz az adatbázis fejlődéséhez, hiszen szerkeszthetsz, vagy létrehozhatsz új bejegyzéseket[br]
+ - Nyomon követheted a tulajdonodban levő visual novellek, helyzetét, állapotát...mit játszol, melyiket fejezted már be stb.[br]
+ - Szavazhatsz a visual novellekre, melyek tetszettek, vagy ép meggyűlöltették magukat[br]
+ - Bekapcsolódhatsz a fórumba levő beszélgetésekbe
:_register_form_title
en : New account
ru : Новая учётная запись
+cs : Nový účet
+hu : Új fiók
:_register_username
en : Username
ru : Имя пользователя
+cs : Uživatelské jméno
+hu : Felhasználónév
:_register_username_msg
-en : Requested username. Must be lowercase and can only consist of alphanumeric characters.
+en : Preferred username. Must be lowercase and can only consist of alphanumeric characters.
ru : Запрашиваемое имя пользователя. Должно состоять из буквенно-цифровых символов в нижнем регистре.
+cs : Požadováné uživatelské jméno. Musí být malými písmeny a smí obsahovat pouze písmena a číslice.
+hu : A kívánt felhasználónév. Muszáj kisbetűkkel legyen s csakis alfanumerikus karaktereket tartalmazhat.
:_register_mail
en : Email
ru : E-mail
+cs : E-mail
+hu :
:_register_mail_msg
en : Your email address will only be used in case you lose your password. We will never send
spam or newsletters unless you explicitly ask us for it.
ru : Адрес вашей электронной почты будет использоваться лишь в случае утери пароля. Мы никогда не
пришлём вам спама или новостных рассылок, пока вы недвусмысленно не попросите об обратном.
+cs : Vaše e-mailová adresa bude použita pouze v případě ztráty vašeho hesla. Nikdy vám nezašleme spam nebo newslettery,
+ pokud o to explicitně nezažádáte.
+hu : Az email címed csakis akkor kerül használatba ha elveszted a jelszavad. Soha nem fogunk
+ spam vagy hírleveleket küldeni, csakis ha te azt kifejezetten kéred tőlünk.
:_register_password
en : Password
ru : Пароль
+cs : Heslo
+hu : Jelszó
:_register_confirm
en : Confirm password
ru : Подтверждение пароля
+cs : Potvrdit heslo
+hu : Jelszó megerősítése
# User edit (/u+/edit)
@@ -2546,70 +4364,104 @@ ru : Подтверждение пароля
:_usere_title
en : My account
ru : Моя учётная запись
+cs : Můj účet
+hu : Fiókom
:_usere_saved_title
en : Settings saved
ru : Параметры сохранены
+cs : Nastavení uloženo
+hu : Beállítások mentve
:_usere_saved_msg
en : Settings successfully saved.
ru : Параметры успешно сохранены.
+cs : Nastavení úspěšně uloženo.
+hu : Beállítások sikeresen mentve.
:_usere_geninfo
en : General info
ru : Основная информация
+cs : Obecné informace
+hu : Általános info
:_usere_username
en : Username
ru : Имя пользователя
+cs : Uživatelské jméno
+hu : Felhasználónév
:_usere_rank
en : Rank
ru : Ранг
+cs : Postavení
+hu : Rang
:_usere_ignvotes
en : Ignore votes in VN statistics
ru : Игнорировать голосования в статистике новелл
+cs : Ignorovat hlasy ve statistikách vizuálních novel
+hu : VN szavazatok figyelmen kívül hagyása a statisztikákban
:_usere_mail
en : Email
ru : E-mail
+cs : E-mail
+hu :
:_usere_changepass
en : Change password
ru : Смена пароля
+cs : Změnit heslo
+hu : Jelszó megváltoztatása
:_usere_changepass_msg
en : Leave blank to keep your current password
ru : Оставьте пустым чтобы сохранить текущий пароль
+cs : Zanechte prázdné pro zachování současného hesla
+hu : Hagyd üresen ha a jelenlegi jelszavad meg akarod tartani
:_usere_password
en : Password
ru : Пароль
+cs : Heslo
+hu : Jelszó
:_usere_confirm
en : Confirm password
ru : Подтверждение пароля
+cs : Potvrdit heslo
+hu : Jelszó megerősítése
:_usere_options
en : Options
ru : Настройки
+cs : Nastavení
+hu : Beállítások
:_usere_flist
en : Allow other people to see my visual novel list ([url,_1,_1]) and wishlist ([url,_2,_2])
ru : Разрешить остальным видеть мой список новелл ([url,_1,_1]) и список желаемого ([url,_2,_2])
+cs : Povolit ostatním zobrazit můj list vizuálních novel ([url,_1,_1]) a wishlist ([url,_2,_2])
+hu : Más embereknek megengedni, hogy lássák a visual novel listám ([url,_1,_1]) és a kívánságlistám ([url,_2,_2])
:_usere_fnsfw
en : Disable warnings for images that are not safe for work.
ru : Отключить предупреждения для небезопасных изображений.
+cs : Vypnout varování pro obrázky, které jsou "Not Safe For Work".
+hu : Nem biztonságos képek előtti figyelmeztetés kikapcsolása.
:_usere_skin
en : Prefered skin
ru : Предпочитаемая шкурка
+cs : Preferovaný skin
+hu : Előnybe részesített stílus
:_usere_css
en : Additional [url,http://en.wikipedia.org/wiki/Cascading_Style_Sheets,CSS]
ru : Дополнительный [url,http://en.wikipedia.org/wiki/Cascading_Style_Sheets,CSS]
+cs : Další [url,http://en.wikipedia.org/wiki/Cascading_Style_Sheets,CSS]
+hu : Továbbiak [url,http://en.wikipedia.org/wiki/Cascading_Style_Sheets,CSS]
# Users posts (/u+/posts)
@@ -2617,18 +4469,26 @@ ru : Дополнительный [url,http://en.wikipedia.org/wiki/Cascading_St
:_uposts_title
en : Posts made by [_1]
ru : Сообщения [_1]
+cs : Příspěvek od uživatele [_1]
+hu : Hozzászólás [_1] által
:_uposts_noresults
en : [_1] hasn't made any posts yet.
ru : [_1] пока не имеет сообщений.
+cs : [_1] ještě nenapsal žádný příspěvek.
+hu : [_1] még nem szólt hozzá semmihez
:_uposts_col_date
en : Date
ru : Дата
+cs : Datum
+hu : Dátum
:_uposts_col_title
en : Title
ru : Название
+cs : Název
+hu : Cím
# User list (/u/all)
@@ -2636,26 +4496,38 @@ ru : Название
:_ulist_title
en : Browse users
ru : Обзор пользователей
+cs : Procházet uživatele
+hu : Felhasználók böngészése
:_ulist_col_username
en : Username
ru : Имя пользователя
+cs : Uživatelské jméno
+hu : Felhasználónév
:_ulist_col_registered
en : Registered
ru : Регистрация
+cs : Registrován
+hu : Regisztrált
:_ulist_col_votes
en : Votes
ru : Отдано голосов
+cs : Hlasy
+hu : Szavazatok
:_ulist_col_edits
en : Edits
ru : Правок
+cs : Editace
+hu : Szerkesztések
:_ulist_col_tags
en : Tags
ru : Тегов
+cs : Tagy
+hu : Címkék
@@ -2674,86 +4546,128 @@ ru : Тегов
:_vnbrowse_title
en : Browse visual novels
ru : Обзор новелл
+cs : Procházet vizuální novely
+hu : Visual novellek böngészése
:_vnbrowse_col_score
en : Score
ru : Рейтинг
+cs : Skóre
+hu : Pontozás
:_vnbrowse_col_title
en : Title
ru : Название
+cs : Název
+hu : Cím
:_vnbrowse_col_released
en : Released
ru : Выпуск
+cs : Vydáno
+hu : Kiadva
:_vnbrowse_col_popularity
en : Popularity
ru : Популярность
+cs : Popularita
+hu : Népszerűség
:_vnbrowse_tagign_title
en : The following tags were ignored:
ru : Следующие теги были пропущены:
+cs : Následující tagy byly ignorovány:
+hu : Az allábi címkék nem lettek figyelembe véve:
:_vnbrowse_tagign_meta
en : can't filter on meta tags
ru : фильтрация мета-тегов невозможна
+cs : nedají se filtrovat meta tagy
+hu : meta címkéken nem működik a szűrő
:_vnbrowse_tagign_notfound
en : no such tag found
ru : тег не найден
+cs : nenalezeny takové tagy
+hu : nem található ilyen címke
:_vnbrowse_advsearch
en : advanced search
ru : расширенный поиск
+cs : pokročilé hledání
+hu : bővített keresés
:_vnbrowse_tags
en : Tag filters
ru : Фильтры тегов
+cs : Filtry tagů
+hu : címke szűrők
:_vnbrowse_booland
en : boolean and, selecting more gives less results
ru : булевое 'и', чем больше выбрано, тем меньше даёт результатов
+cs : boolean a, výběr více dá méně výsledků
+hu : Boole féle értékhalmaz(igaz/hamis), s ha többet választasz ki akkor kevesebb találatot ad
:_vnbrowse_taginc
en : Tags to include
ru : Включить теги
+cs : Tagy k zahrnutí
+hu : Címkék beleszámítása
:_vnbrowse_tagexc
en : Tags to exclude
ru : Исключить теги
+cs : Tagy k vyjmutí
+hu : Címkék kihagyása
:_vnbrowse_spoil0
en : Hide spoilers
ru : Скрыть спойлеры
+cs : Skrýt spoilery
+hu : Spoilerek elrejtése
:_vnbrowse_spoil1
en : Show minor spoilers
ru : Показать лёгкие спойлеры
+cs : Ukázat menší spoilery
+hu : Kisebb spoilerek megjelenítése
:_vnbrowse_spoil2
en : Show major spoilers
ru : Показать жёсткие спойлеры
+cs : Ukázat všechny spoilery
+hu : Nagyobb spoilerek megjelenítése
:_vnbrowse_lang
en : Languages
ru : Языки
+cs : Jazyky
+hu : Nyelvek
:_vnbrowse_boolor
en : boolean or, selecting more gives more results
ru : булевое 'или', чем больше выбрано, тем больше даёт результатов
+cs : boolean nebo, výběr více dá více výsledků
+hu : Boole féle értékhalmaz(igaz/hamis), ha többet választasz ki akkor több találatot ad ki
:_vnbrowse_plat
en : Platforms
ru : Платформы
+cs : Platformy
+hu : Platformok
:_vnbrowse_apply
en : Apply
ru : Применить
+cs : Použít
+hu : Alkalmazás
:_vnbrowse_clear
en : Clear
ru : Очистить
+cs : Začít znovu
+hu : Törlés
# VN add/edit form (/v+/edit)
@@ -2761,63 +4675,95 @@ ru : Очистить
:_vnedit_title_edit
en : Edit [_1]
ru : Правка [_1]
+cs : Editovat vizuální novelu [_1]
+hu : [_1] szerkesztése
:_vnedit_title_add
en : Add a new visual novel
ru : Добавление новой новеллы
+cs : Přidat novou vizuální novelu
+hu : Új visual novel hozzáadása
:_vnedit_geninfo
en : General info
ru : Основная информация
+cs : Obecné informace
+hu : Általános info
:_vnedit_frm_title
en : Title (romaji)
ru : Название (ромадзи)
+cs : Název (romaji)
+hu : Cím (romaji)
:_vnedit_original
en : Original title
ru : Оригинальное название
+cs : Originální název
+hu : Eredeti cím
:_vnedit_original_msg
en : The original title of this visual novel, leave blank if it already is in the Latin alphabet.
ru : Оригинальное название данной новеллы, оставьте пустым если уже набрано в латинском алфавите.
+cs : Originální název této vizuální novely, ponechte prázdné, pokud již je latinkou.
+hu : A visual novel eredeti címe, ha latin betűkkel van akkor ne írd be mégegyszer ide.
:_vnedit_alias
en : Aliases
ru : Прочие названия
+cs : Aliasy
+hu : Más nevek
:_vnedit_alias_msg
-en : Comma seperated list of alternative titles or abbreviations. Can include both official
+en : Comma separated list of alternative titles or abbreviations. Can include both official
(japanese/english) titles and unofficial titles used around net.[br]
Titles that are listed in the releases should not be added here!
ru : Список альтернативных названий или аббревиатур, перечисленных через запятую. Может включать как
официальные (японские/английские/русские) названия, так и неофициальные, имеющие хождение в сети.[br]
Не следует добавлять сюда названия, указанные в выпусках!
+cs : Seznam alternativních názvů nebo zkratek, oddělených čárkou. Může zahrnovat oba
+ (japonský/anglický) názvy a neoficiální názvy používané na internetu.[br]
+ Názvy, vypsané ve vydáních by se sem přidávat neměly!
+hu : Vesszővel elválasztott lista, más címekkel vagy rövidítésekkel. Tartalmazhat hivatalos
+ (japán/angol) címeket vagy nem hivatalosokat amelyet a netten használnak.[br]
+ Azok a címek amelyek a kiadásokba vannak ne tegyétek bele!
:_vnedit_desc
en : Description
ru : Описание
+cs : Popis
+hu : Leírás
:_vnedit_desc_msg
en : Short description of the main story. Please do not include spoilers, and don't forget to list
the source in case you didn't write the description yourself. (formatting codes are allowed)
ru : Краткое описание главной сюжетной линии. Пожалуйста, не вносите сюда спойлеры, а так же не
забывайте указать источник описания, если не являетесь его автором. (коды форматирования разрешены)
+cs : Krátký popis hlavního příběhu. Prosím nepište spoilery a nezapomeňte uvést zdroj,
+ pokud jste popis nenapsali sami. (formátovací kódy jsou povoleny)
+hu : A fő történet rövid leírása. Ne tartalmazzon spoilert és ha nem te írtad akkor ne felejtsd el
+ megemlíteni a forrást amit felhasználtál. (formázó kódok megengedettek)
:_vnedit_length
en : Length
ru : Продолжительность
+cs : Délka
+hu : Hossz
:_vnedit_links
en : External links
ru : Внешние ссылки
+cs : Externí odkazy
+hu : Külső linkek
:_vnedit_anime
en : Anime
ru : Аниме
+cs : Anime
+hu :
:_vnedit_anime_msg
-en : Whitespace seperated list of [url,http://anidb.net/,AniDB] anime IDs.
+en : Whitespace separated list of [url,http://anidb.net/,AniDB] anime IDs.
E.g. "1015 3348" will add [url,http://anidb.net/a1015,Shingetsutan Tsukihime]
and [url,http://anidb.net/a3348,Fate/stay night] as related anime.[br]
Note: It can take a few minutes for the anime titles to appear on the VN page.
@@ -2825,22 +4771,38 @@ ru : Список идентификаторов аниме по [url,http://ani
Например, "1015 3348" добавит связь с аниме [url,http://anidb.net/a1015,Shingetsutan Tsukihime]
и [url,http://anidb.net/a3348,Fate/stay night].[br]
Замечание: появление названия аниме на странице новеллы может занять несколько минут.
+cs : Seznam AnimeID z [url,http://anidb.net/,AniDB], oddělených mezerou.
+ Např. "1015 3348" přidá [url,http://anidb.net/a1015,Shingetsutan Tsukihime]
+ a [url,http://anidb.net/a3348,Fate/stay night] jako příbuzná anime.[br]
+ Poznámka: Může trvat několik minut, než se názvy anime objeví na stránce vizuální novely.
+hu : Szóközzel elválasztott lista [url,http://anidb.net/,AniDB] anime ID-kal.
+ Pl. "1015 3348" belinkeli [url,http://anidb.net/a1015,Shingetsutan Tsukihime]
+ és [url,http://anidb.net/a3348,Fate/stay night] mint kapcsolódó animék.[br]
+ Figyelem: Beletelhet pár percbe míg az anime címek megjelennek a VN oldalán.
:_vnedit_image
en : Image
ru : Изображение
+cs : Obrázek
+hu : Kép
:_vnedit_image_none
en : No image uploaded yet
ru : Изображения пока нет
+cs : Obrázek ještě nebyl nahrán
+hu : Még nincs kép feltöltve
:_vnedit_image_processing
en : ~[processing image, please return in a few minutes~]
ru : ~[обработка изображения, пожалуйста подождите несколько минут~]
+cs : ~[obrázek se zpracovává, vraťte se prosím za několik minut~]
+hu : ~[a kép feldolgozás alatt van, gyere vissza pár perc múlva~]
:_vnedit_image_upload
en : Upload new image
ru : Загрузить новое изображение
+cs : Nahrát nový obrázek
+hu : Tölts fel egy új képet
:_vnedit_image_upload_msg
en : Preferably the cover of the CD/DVD/package. Image must be in JPEG or PNG format
@@ -2848,36 +4810,105 @@ en : Preferably the cover of the CD/DVD/package. Image must be in JPEG or PNG fo
ru : Желательно, обложка CD/DVD/коробки. Изображение должно быть в формате JPEG, либо в
формате PNG, и весить не более 500 Кб. Изображения размером более 256 на 400 точек
будут автоматически уменьшены.
+cs : Pokud možno obálka CD/DVD/balení. Obrázek musí být ve formátu JPEG nebo PNG
+ a ne větší 500kB. Obrázek větší než 256x400 pixelů bude automaticky zmenšen.
+hu : Lehetőleg a CD/DVD/csomagolás borítóját. A képek muszáj JPEG vagy PNG formátumba
+ legyenek és maximum 500kb nagyok. 254x400-nál nagyobb képek automatikusan át lesznek
+ méretezve.
:_vnedit_image_nsfw
en : NSFW
ru : НБДР (NSFW)
+cs : NSFW
+hu : Nem Biztonságos
:_vnedit_image_nsfw_check
en : Not Safe For Work
ru : Не безопасно для работы
+cs : Not Safe For Work
+hu : Nem biztonságos
:_vnedit_image_nsfw_msg
en : Please check this option if the image contains nudity, gore, or is otherwise not safe in a work-friendly environment.
ru : Пожалуйста, поставьте эту галочку если изображение содержит наготу, кровищу, либо тем или иным образом не безопасно для рабочего окружения.
+cs : Prosíme, zaškrtněte tuto možnost, pokud obrázek obsahuje nahotu, gore nebo je jinak nevhodný pro pracovní prostředí.
+hu : Kérlek pipáld ezt be ha a kép tartalmaz meztelenséget, vért vagy más módon nem szalon képes dolgokat.
:_vnedit_rel
en : Relations
ru : Связи
+cs : Vztahy
+hu : Összefüggések
:_vnedit_rel_sel
en : Selected relations
ru : Выбранные связи
+cs : Vybrané vztahy
+hu : Kiválasztott összefüggések
:_vnedit_rel_add
en : Add relation
ru : Добавить связь
+cs : Přidat vztah
+hu : Összefüggés hozzáadása
+
+# <title1> is a <relation> of <title2>
+# this trick doesn't even work very well in English, so just an approximation is fine
+:_vnedit_rel_isa
+en : is a
+ru : это
+cs : je
+hu*: -
+
+:_vnedit_rel_of
+en : of
+ru : для
+cs : titulu
+hu*: -
+
+:_vnedit_rel_addbut
+en : add
+ru : добавить
+cs : přidat
+hu : hozzáadd
+
+:_vnedit_rel_del
+en : del
+ru : убрать
+cs : smazat
+hu : töröl
+
+:_vnedit_rel_none
+en : No relations selected.
+ru : Отношений не выбрано.
+cs : Nejsou vybrány žádné vztahy.
+hu : Semmilyen összefüggés nincs kiválasztva.
+
+:_vnedit_rel_findformat
+en : Visual novel textbox must start with an ID (e.g. v17)
+ru : Строка новеллы должна начинаться с идентификатора (например, v17)
+cs : textové pole vizuální novely musí začínat ID (např. v17)
+hu : A visual novel szövegdoboz muszáj egy ID-val kezdődjön (pl. v17)
+
+:_vnedit_rel_novn
+en : Visual novel not found!
+ru : Новелла не найдена!
+cs : Vizuální novela nenalezena!
+hu : A visual novel nem található!
+
+:_vnedit_rel_double
+en : This visual novel has already been selected!
+ru : Эта новелла уже была выбрана!
+cs : Tato vizuální novela již byla vybrána!
+hu : Ez a visual novel már ki van választva!
:_vnedit_scr
en : Screenshots
ru : Скриншоты
+cs : Screenshoty
+hu : Pillanatképek
-:_vnedit_scr_msg
+:_vnedit_scrmsg
en : Please keep the following in mind when uploading screenshots:[br]
- Screenshots have to be in the native resolution of the game,[br]
- Remove any window borders and make sure the image is unmarked,[br]
@@ -2890,6 +4921,168 @@ ru : Когда загружаете скриншоты, пожалуйста п
- Старайтесь загружать не только одни сюжетные картинки.[br]
За подробностями обращайтесь к [url,/d2#6,рекомендациям].[br]
Не забудьте сохранить изменения когда закончите загружать изображения!
+cs : Prosíme, pamatujte na následující věci, pokud nahráváte screenshoty:[br]
+ - Screenshots musí být v původním rozlišení hry,[br]
+ - Odstraňte všechna okna kolem a ujistěte se, že na obrázku není nic navíc,[br]
+ - Nenahrávejte pouze CG.[br]
+ Prosíme, přečtěte si [url,/d2#6,doporučení] pro více informací.[br]
+ Ujistěte se, že jste potvrdili odeslání informací poté, co nahrajete obrázky!
+hu : Mielőtt pillanatképeket töltesz fel, tarts néhány dolgot figyelembe:[br]
+ - A pillanatképek a játék eredeti felbontásába kell legyenek,[br]
+ - Távolíts el bármilyen ablak szélet és vigyázz, hogy a képen ne legyenek jelölések,[br]
+ - Ne csak esemény CG-ket tölts fel.[br]
+ Legyél szíves és olvasd el az [url,/d2#6,útmutatókat] több információért.[br]
+ A feltöltés után ne felejtsd beküldeni a formulát!
+
+:_vnedit_scr_selrel
+en : -- select release --
+ru : -- выбор выпуска --
+cs : -- vybrat vydání --
+hu : -- kiadás kiválasztása --
+
+:_vnedit_scr_frmloading
+en : Please wait for the screenshots to be uploaded before submitting the form.
+ru : Прежде чем сохранить изменения, дождитесь загрузки скриншотов на сервер.
+cs : Prosíme, počkejte na nahrání screenshotů před potvrzením změn.
+hu : Kérlek várj míg a pillanatképek feltöltődnek, mielőtt beküldenéd a forma lapot.
+
+:_vnedit_scr_frmnorel
+en : Please select the appropriate release for every screenshot.
+ru : Пожалуйста, укажите подходящий выпуск для каждого скриншота.
+cs : Prosíme, vyberte příslušné vydání pro každý screenshot.
+hu : Kérlek válaszd ki a megfelelő kiadást mindegyik pillanatképhez.
+
+:_vnedit_scr_fetching
+en : Fetching thumbnail...
+ru : Обработка превьюшки...
+cs : Načítám náhled...
+hu : Indexképek előkészítése...
+
+:_vnedit_scr_uploading
+en : Uploading screenshot
+ru : Загрузка скриншота
+cs : Nahrávám screenshot
+hu : Pillanatkép feltöltése
+
+:_vnedit_scr_upl_msg
+en : This can take a while, depending on the file size and your upload speed.
+ru : Это может занять некоторое время, в зависимости от размера файла и скорости вашего соединения.
+cs : Toto může chvíli trvat, v závislosti na velikosti souboru a vaší rychlosti uploadu.
+hu : Ez eltarthat egy ideig, attól függ milyen nagy a fájl mérete és mennyivel tudsz feltölteni.
+
+:_vnedit_scr_cancel
+en : cancel
+ru : отменить
+cs : zrušit
+hu : mégse
+
+:_vnedit_scr_full
+en : Enough screenshots
+ru : Больше нет места
+cs : Již máme dostatek screenshotů
+hu : Elég ennyi pillanatkép
+
+:_vnedit_scr_full_msg
+en : The limit of 10 screenshots per visual novel has been reached.
+ If you want to add a new screenshot, please remove an existing one first.
+ru : Достигнуто ограничение в 10 скриншотов.
+ Если вы хотите добавить новый скриншот, пожалуйста удалите один из существующих.
+cs : Limit deseti screenshotů na vizuální novelu byl dosažen.
+ Pokud chcete přidat další screenshot, prosíme odstraňte nejdříve nějaký již existující.
+hu : A megengedett 10 pillanatkép/visual novel megtelt.
+ Ha szeretnél egy új pillanatképet feltölteni, akkor el kell távolíts egy már létezőt.
+
+:_vnedit_scr_add
+en : Add screenshot
+ru : Добавить скриншот
+cs : Přidat screenshot
+hu : Pillanatkép hozzáadása
+
+:_vnedit_scr_imgnote
+en : Image must be smaller than 5MB and in PNG or JPEG format.
+ru : Изображение должно быть не более 5 Мегабайт и в формате PNG, либо JPEG.
+cs : Obrázek musí být menší 5MB a ve formátu PNG nebo JPEG.
+hu : A kép kisebb kell legyen mint 5MB és PNG vagy JPEG formátumba kell legyen.
+
+:_vnedit_scr_addbut
+en : Upload!
+ru : Загрузить!
+cs : Nahrát!
+hu : Feltöltés!
+
+:_vnedit_scr_id
+en : Screenshot #[_1]
+ru : Скриншот #[_1]
+cs : Screenshot #[_1]
+hu : #[_1]-dik pillanatkép
+
+:_vnedit_scr_remove
+en : remove
+ru : убрать
+cs : odebrat
+hu : eltávolítás
+
+:_vnedit_scr_fullsize
+en : Full size: [_1]
+ru : Полный размер: [_1]
+cs : Plná velikost: [_1]
+hu : Teljes méret: [_1]
+
+:_vnedit_scr_nsfw
+en : This screenshot is NSFW
+ru : Этот скриншот НБДР (NSFW)
+cs : Tento screenshot je NSFW
+hu : Ez a pillanatkép nem biztonságos
+
+:_vnedit_scr_oops
+en : Oops! Seems like something went wrong...
+ Make sure the file you're uploading doesn't exceed 5MB in size.
+ If that isn't the problem, then please report a bug.
+ru : Э-кхм! Похоже, возникла проблема...
+ Пожалуйста, убедитесь что ваш файл не превышает размер в 5 Мегабайт.
+ Если дело не в размере, тогда пожалуйста сообщите администрации сайта об ошибке.
+cs : Hups! Zdá se, že se něco pokazilo...
+ Ujistěte se, že soubor, který nahráváte nemá více jak 5MB.
+ Pokud v tom problém neleží, nahlaste systémovou chybu.
+hu : Hoppá! Úgy tűnik valami rosszul sült el...
+ Ellenőrizd, hogy a fájl amit fel akarsz tölteni nem haladja meg az 5MB-os méretet.
+ Ha nem az a baj, akkor kérlek jelentsd a hibát.
+
+:_vnedit_scr_errformat
+en : Upload failed!
+ Only JPEG or PNG images are accepted.
+ru : Ошибка при загрузке!
+ Допускаются только JPEG или PNG.
+cs : Nahrání se nezdařilo!
+ Přijímány jsou pouze obrázky formátu JPEG nebo PNG.
+hu : Feltöltés sikertelen!
+ Csakis JPEG vagy PNG képek az elfogadhatóak.
+
+:_vnedit_scr_errempty
+en : Upload failed!
+ No file selected, or an empty file?
+ru : Ошибка при загрузке!
+ Файл не выбран или имеет нулевой размер.
+cs : Nahrání se nezdařilo!
+ Buď jste nevybrali soubor, nebo byl tento soubor prázdný.
+hu : Feltöltés sikertelen!
+ Nincs fájl kiválasztva, vagy üres volna a fájl?
+
+:_vnedit_scr_genthumb
+en : Generating thumbnail...
+ru : Генерация превьюшки...
+cs : Tvořím náhled...
+hu : Indexkép készítése
+
+:_vnedit_scr_genthumb_msg
+en : Note: if this takes longer than 30 seconds, there\'s probably something wrong on our side.
+ Please try again later or report a bug if that is the case.
+ru : Замечание: если эта операция длится более 30 секунд, значит у нас на сервере неполадки.
+ В таком случае попытайтесь снова или сообщите администрации об ошибках.
+cs : Poznámka: Pokud toto zabere více jak 30 sekund, je pravděpodobně chyba někde na naší straně.
+ Prosíme, zkuste to znovu později nebo nahlaste systémovou chybu, pokud tomu tak je.
+hu : Figyelem: ha ez tovább tart mint 30 másodperc, akkor a mivelünk van a gond.
+ Kérlek próbáld meg később és ha adott az eset, akkor jelentsd a hibát.
# VN Relation graph page (/v+/rg)
@@ -2897,6 +5090,8 @@ ru : Когда загружаете скриншоты, пожалуйста п
:_vnrg_title
en : Relation graph for [_1]
ru : Схема связей для [_1]
+cs : Graf vztahů pro vizuální novelu [_1]
+hu : Összefüggés gráf [_1]-hoz
# VN Diff viewer (/v+.+)
@@ -2904,86 +5099,128 @@ ru : Схема связей для [_1]
:_revfield_v_title
en : Title (romaji)
ru : Название (ромадзи)
+cs : Název (romaji)
+hu : Cím (romaji)
:_revfield_v_original
en : Original title
ru : Оригинальное название
+cs : Originální název
+hu : Eredeti cím
:_revfield_v_alias
en : Alias
ru : Прочие названия
+cs : Alias
+hu : Más nevek
:_revfield_v_desc
en : Description
ru : Описание
+cs : Popis
+hu : Leírás
:_revfield_v_length
en : Length
ru : Продолжительность
+cs : Délka
+hu : Hossz
:_vndiff_nolink
en : ~[no link~]
ru : ~[нет ссылки~]
+cs : ~[žádný odkaz~]
+hu : ~[nincs link~]
:_vndiff_none
en : ~[none~]
ru : ~[пусто~]
+cs : ~[nic~]
+hu : ~[nincs~]
:_revfield_v_l_wp
en : Wikipedia link
ru : Ссылка Википедии
+cs : Link na Wikipedii
+hu :
:_revfield_v_l_encubed
en : Encubed tag
ru : Тег Encubed'а
+cs : Tag na Encubed
+hu : Encubed címke
:_revfield_v_l_renai
en : Renai.us link
ru : Ссылка Renai.us
+cs : Odkaz na Renai.us
+hu :
:_revfield_v_l_vnn
en : V-N.net link
ru : Ссылка V-N.net
+cs : Odkaz na V-N.net
+hu :
:_revfield_v_relations
en : Relations
ru : Связи
+cs : Vztahy
+hu : Összefüggések
:_revfield_v_anime
en : Anime
ru : Аниме
+cs : Anime
+hu :
:_revfield_v_screenshots
en : Screenshots
ru : Скриншоты
+cs : Screenshoty
+hu : Pillanatképek
:_revfield_v_image
en : Image
ru : Изображение
+cs : Obrázek
+hu : Kép
:_vndiff_image_nsfw
en : (NSFW)
ru : (НБДР|NSFW)
+cs : (NSFW)
+hu : (Nem Biztonságos)
:_vndiff_image_proc
en : ~[processing~]
ru : ~[обработка~]
+cs : ~[zpracovává se~]
+hu : ~[feldolgozás~]
:_vndiff_image_none
en : No image
ru : Нет изображения
+cs : Obrázek není
+hu : Nincs kép
:_revfield_v_img_nsfw
en : Image NSFW
ru : Изображение НБДР (NSFW)
+cs : Vhodnost obrázku
+hu : A kép nem biztonságos
:_vndiff_nsfw_safe
en : Safe
ru : Безопасно
+cs : SFW
+hu : Biztonságos
:_vndiff_nsfw_notsafe
en : Not safe
ru : Не безопасно
+cs : NSFW
+hu : Nem biztonságos
# VN page (/v+)
@@ -2991,194 +5228,334 @@ ru : Не безопасно
:_vnpage_noimg
en : No image uploaded yet
ru : Нет загруженного изображения
+cs : Ještě nebyl nahrán žádný obrázek
+hu : Még nincs kép feltöltve
:_vnpage_imgproc
en : ~[processing image, please return in a few minutes~]
ru : ~[идёт обработка изображения, подождите несколько минут~]
+cs : ~[obrázek se zpracovává, prosíme, vraťte se za několik minut~]
+hu : ~[a kép feldolgozás alatt van, gyere vissza pár perc múlva~]
:_vnpage_imgnsfw_msg
en : This image has been flagged as Not Safe For Work.
ru : Данное изображение помечено как не безопасное для работы.
+cs : Tento obrázek byl označen jako "Not Safe For Work"
+hu : Ezt a képet Nem Biztonságosnak nyilvánították.
:_vnpage_imgnsfw_show
en : Show me anyway
ru : Всё равно показать
+cs : Ukázat i přes to
+hu : Mutasd meg akkor is
:_vnpage_imgnsfw_note
en : (This warning can be disabled in your account)
ru : (Это предупреждение можно отключить в настройках вашей учётной записи)
+cs : (Toto varování může být vypnuto ve vašem nastavení)
+hu : (Ezt a figyelmeztetést ki lehet kapcsolni a profilodba)
:_vnpage_imgnsfw_foot
en : Flagged as NSFW
ru : Помечено как НБДР (NSFW)
+cs : Označeno jako NSFW
+hu : Nem biztonságosnak van bejelölve
:_vnpage_vntitle
en : Title
ru : Название
+cs : Název
+hu : Cím
:_vnpage_original
en : Original title
ru : Оригинальное название
+cs : Originální název
+hu : Eredeti cím
:_vnpage_alias
en : Aliases
ru : Прочие названия
+cs : Aliasy
+hu : Más nevek
:_vnpage_length
en : Length
ru : Продолжительность
+cs : Délka
+hu : Hossz
:_vnpage_links
en : Links
ru : Ссылки
+cs : Odkazy
+hu : Linkek
:_vnpage_description
en : Description
ru : Описание
+cs : Popis
+hu : Leírás
:_vnpage_tags_spoil0
en : hide spoilers
ru : скрыть спойлеры
+cs : skrýt spoilery
+hu : spoilerek elrejtése
:_vnpage_tags_spoil1
en : show minor spoilers
ru : показать лёгкие спойлеры
+cs : ukázat menší spoilery
+hu : kisebb spoilerek megjelenítése
:_vnpage_tags_spoil2
en : spoil me!
ru : показать все!
+cs : prozraď mi vše!
+hu : rontsd el nekem!
:_vnpage_tags_summary
en : summary
ru : суммарно
+cs : shrnutí
+hu : összefoglalás
:_vnpage_tags_all
en : all
ru : все
+cs : vše
+hu : mind
-:_vnpage_producers
-en : Producers
-ru : Компании
+:_vnpage_developer
+en : Developer
+ru : Разработчик
+cs : Vývojář
+hu : Fejlesztő
+
+:_vnpage_publisher
+en : Publishers
+ru : Издатели
+cs : Vydavatel
+hu : Kiadó
:_vnpage_relations
en : Relations
ru : Связи
+cs : Vztahy
+hu : Összefüggések
:_vnpage_anime
en : Related anime
ru : Связанное аниме
+cs : Příbuzná anime
+hu : Kapcsolódó animék
:_vnpage_anime_noinfo
en : ~[no information available at this time: [url,_2,_1]~]
ru : ~[к сожалению, пока никакой информации: [url,_2,_1]~]
+cs : ~[nyní nejsou dostupné žádné informace: [url,_2,_1]~]
+hu : ~[jelenleg nincs semmilyen információ: [url,_2,_1]~]
:_vnpage_uopt
en : User options
ru : Настройки пользователя
+cs : Možnosti uživatele
+hu : Felhasználó beállítások
:_vnpage_uopt_voted
en : your vote: [_1]
ru : ваш голос: [_1]
+cs : váš hlas: [_1]
+hu : szavazatod: [_1]
:_vnpage_uopt_novote
en : not voted yet
ru : пока без голоса
+cs : ještě nehlasováno
+hu : még nem kapott szavazatot
:_vnpage_uopt_changevote
en : Change vote
ru : Переголосовать
+cs : Změnit hlas
+hu : Szavazat megváltoztatássa
:_vnpage_uopt_dovote
en : Vote
ru : Голосовать
+cs : Hlasovat
+hu : Szavazás
:_vnpage_uopt_delvote
en : revoke
ru : снять голос
+cs : zdržet se
+hu : visszavonás
+
+:_vnpage_uopt_1vote
+en : You are about to give this visual novel a 1 out of 10. This is a rather extreme rating, meaning this game has absolutely nothing to offer, and that it's the worst game you have ever played.
+ Are you really sure this visual novel matches that description?
+ru : Вы собираетесь дать этой новелле оценку 1 из 10. Это весьма сильная оценка, означающая что это совершенно бездарная работа и худшее из всего во что вы играли.
+ Вы уверены, что эта новелла соответствует такому критерию?
+cs : Chystáte se této vizuální novele dát hodnocení 1 z 10. To je celkem extrémní hodnocení, znamenající, že tato hra nemá absolutně nic co nabídnout, a že je to ta nejhorší hra, kterou jste kdy hráli.
+ Jste si opravdu jisti, že tato vizuální novela odpovídá tomuto popisu?
+hu : Te most épp a 10-ből, 1-et adsz ennek a visual novelnek. Ez egy elég extrém osztályozás, ez azt jelenti, hogy ez a játék egyáltalán semmit nem tud nyújtani és, hogy ez a legrosszabb amit valaha játszottál.
+ Biztos vagy benne, hogy ezt a visual novel azok közzé lehet sorolni?
+
+:_vnpage_uopt_10vote
+en : You are about to give this visual novel a 10 out of 10. This is a rather extreme rating, meaning this is one of the best visual novels you've ever played and it's unlikely that any other game could ever be better than this one.
+ It is generally a bad idea to have more than three games in your vote list with this rating, choose carefully!
+ru : Вы собираетесь дать этой новелле оценку 10 из 10. Это весьма сильная оценка, означающая что это одна из лучших когда либо выпущенных новелл, которую вы когда-либо читали.
+ Вы уверены, что эта новелла соответствует такому критерию?
+cs : Chystáte se této vizuální novele dát hodnocení 10 z 10. To je celkem extrémní hodnocení, znamenající, že to je jedna z nejlepších vizuálních novel, které jste kdy hráli a je nepravděpodobné, že by nějaká další hra mohla být lepší než tato.
+ Obecně vztao je špatný nápad mít víc jak tři hry s tímto hodnocením ve vašem listu hlasování, vybírejte pečlivě!
+hu : Te most épp a 10-ből, 10-et adsz ennek a visual novelnek. Ez egy elég extrém osztályozás, ami annyit jelent, hogy ez a legjobb visual novel amit valaha játszottál és elég valószínűtlen, hogy találkozol ettől jobb játékkal.
+ Általában rossz ötlet ha 3-nál több ilyen játékot tartasz a szavazási listádba, válasz óvatosan!
:_vnpage_uopt_wishlisted
en : wishlist: [_1]
ru : список желаемого: [_1]
+cs : wishlist: [_1]
+hu : Kivánságlista: [_1]
:_vnpage_uopt_nowish
en : not on your wishlist
ru : не в вашем списке
+cs : není na vašem wishlistu
+hu : nincs benne a kivánságlistádba
:_vnpage_uopt_changewish
en : Change status
ru : Сменить статус
+cs : Změnit status
+hu : Állapot megváltoztatássa
:_vnpage_uopt_addwish
en : Add to wishlist
ru : Добавить к списку
+cs : Přidat na wishlist
+hu : Hozzáadás a kivánságlistdhoz
:_vnpage_uopt_delwish
en : remove from wishlist
ru : убрать из списка
+cs : odstranit z wishlistu
+hu : eltávolitás a kivánságlistából
+
+:_vnpage_uopt_relrstat
+en : Release status
+ru : Статус выпуска
+cs : Status vydání
+hu : Kiadás állapota
+
+:_vnpage_uopt_relvstat
+en : Play status
+ru : Статус играбельности
+cs : Herní status
+hu : Játszás állapota
+
+:_vnpage_uopt_reldel
+en : Remove from VN list
+ru : Убрать из списка новелл
+cs : Odstranit z listu VN
+hu : Eltávolítás a VN listából
:_vnpage_rel
en : Releases
ru : Выпуски
+cs : Vydání
+hu : Kiadások
:_vnpage_rel_none
en : We don't have any information about releases of this visual novel yet...
ru : У нас пока нет информации о выпусках этой новеллы...
+cs : K této vizuální novele zatím nemáme informace o žádném vydání...
+hu : Még nincs információnk ennek a visual novelnek a kiadásairól...
:_vnpage_rel_add
en : add release
ru : добавить выпуск
+cs : přidat vydání
+hu : kiadás hozzáadása
:_vnpage_rel_patch
en : (patch)
ru : (патч)
+cs : (patch)
+hu :
:_vnpage_rel_extlink
en : External link
ru : Внешняя ссылка
+cs : Externí odkaz
+hu : Külső link
:_vnpage_scr
en : Screenshots
ru : Скриншоты
+cs : Screenshoty
+hu : Pillanatképek
:_vnpage_scr_showing
en : Showing [_1] out of [_2] [quant,_2,screenshot,screenshots].
ru : [_1] из [_2] [quant,_2,скриншота,скриншотов,скриншотов] отображено.
+cs : Ukazuji screenshot [_1] z celkém [_2] [quant,_2,screenshotu,screenshotů,screenshotů].
+hu : [_1] megjelenitve a [_2] [quant,_2,pillanatkép,pillanatképekből].
:_vnpage_scr_nsfwhide
en : show/hide NSFW
ru : показать/скрыть НБДР (NSFW)
+cs : skrýt/zobrazit NSFW
+hu : megjelenít/elrejt NB
:_vnpage_scr_num
en : Screenshot #[_1]
ru : Скриншот #[_1]
+cs : Screenshot #[_1]
+hu : Pillanatkép #[_1]
:_vnpage_stats
en : User stats
ru : Статистика пользователей
+cs : Statistiky uživatelů
+hu : Felhasználó adatok
:_vnpage_stats_none
en : Nobody has voted on this visual novel yet...
ru : Никто пока не голосовал за эту новеллу...
+cs : Pro tuto vizuální novelu ještě nikdo nehlasoval...
+hu : Még senki nem szavazot erre a visual novelre...
:_votestats_title
en : Vote stats
ru : Статистика голосования
+cs : Statistika hlasování
+hu : Szavazási adatok
:_votestats_sum
en : [_1] [quant,_1,vote,votes] total, average [_2]
ru : Всего [_1] [quant,_1,голос,голоса,голосов], в среднем [_2]
+cs : Celkem [_1] [quant,_1,hlas,hlasy,hlasů], průměr [_2]
+hu : [_1] [quant,_1,szavazat,szavazatok] összesen, átlagba [_2]
:_votestats_recent
en : Recent votes
ru : Недавно проголосовали
+cs : Poslední hlasy
+hu : Legútóbbi szavazatok
:_votestats_pop_title
en : Popularity
ru : Популярность
+cs : Popularita
+hu : Népszerűsség
:_votestats_pop_sum
en : Ranked #[_1] out of [_2] with a score of [_3].
ru : Рейтинг - #[_1] из [_2], средний балл - [_3].
+cs : Hodnoceno na [_1]. místě z celkem [_2] se skóre [_3].
+hu : [_1]. helyezet a [_2] bejegyzésből [_3] pontszámmal.
@@ -3195,24 +5572,34 @@ ru : Рейтинг - #[_1] из [_2], средний балл - [_3].
:_denied_title
en : Access Denied
ru : В доступе отказано
+cs : Přístup odepřen
+hu : Belépés megtagadva
# not logged in
:_denied_needlogin_title
en : You need to be logged in to perform this action.
ru : Для выполнения этого действия требуется авторизация.
+cs : Pro vykonání této akce se musíte přihlásit.
+hu : Be kell legyél jelentkezve ha ezt akarod csinálni.
:_denied_needlogin_msg
en : Please [url,/u/login,login], or [url,/u/register,create an account] if you don't have one yet.
ru : Пожалуйста [url,/u/login,авторизуйтесь], либо [url,/u/register,создайте учётную запись], если таковой у вас нет.
+cs : Prosíme, [url,/u/login,přihlašte se], nebo si [url,/u/register,vytvořte účet], pokud ještě žádný nemáte.
+hu : Kérlek [url,/u/login,lépj be] vagy [url,/u/register,készíts egy fiókot] ha még nem volna.
# logged in, but simply no access
:_denied_noaccess_title
en : You are not allowed to perform this action.
ru : Вам запрещено выполнять это действие.
+cs : Nemáte oprávnění provést tuto akci.
+hu : Ezt a műveletet nem hajthatod végre.
:_denied_noaccess_msg
en : It seems you don't have the proper rights to perform the action you wanted to perform...
ru : Похоже, у вас нет прав на выполнение того, чего вы хотите...
+cs : Zdá se, že nemáte potřebná práva pro vykonání akce, kterou chcete provést...
+hu : Úgy tűnik, hogy nem rendelkezel megfelelő jogokkal, hogy elvégezd azt a műveletet amit szerettél volna...
# "DB Item has been deleted" page
@@ -3220,12 +5607,18 @@ ru : Похоже, у вас нет прав на выполнение того,
:_hiddenmsg_title
en : Item deleted
ru : Запись удалена
+cs : Položka smazána
+hu : Tárgy törölve
:_hiddenmsg_msg
en : This item has been deleted from the database, File a request on the
[url,_1,discussion board] to undelete this page.
ru : Данная запись удалена из базы данных. Пожалуйста, подайте заявку на
[url,_1,форуме] для восстановления этой страницы.
+cs : Tato položka byla smazána z databáze. Pro obnovení této stránky napište žádost na
+ [url,_1,diskusní board].
+hu : Ez a bejegyzés ki lett törölve az adatbázisból. Nyújtsd be egy kérvényt a
+ [url,_1,fórumon], hogy állítsák vissza ezt az oldalt.
# The warning/notice messages on edit pages
@@ -3233,42 +5626,64 @@ ru : Данная запись удалена из базы данных. Пож
:_editmsg_copy_title
en : You're not editing a release!
ru : Вы не редактируете выпуск, а копируете его!
+cs : Needitujete vydání!
+hu : Te most nem szerkesztesz egy kiadást!
:_editmsg_copy_msg
en : You're about to insert a new release into the database with information based on [_1].[br]
Hit the 'edit' tab on the right-top if you intended to edit the release instead of creating a new one.
ru : Вы собираетесь вставить новый выпуск, основанный на информации из [_1].[br]
Щёлкните вкладку 'правка' в правом верхнем углу, если собирались редактировать выпуск, а не создавать новый.
+cs : Chystáte se vložit nové vydání do databáze s informacemi založenými na vydání [_1].[br]
+ Pokud chcete editovat vydání místo toho, abyste vložili další vydání, přejděte na záložku 'editovat' v pravém horním rohu.
+hu : Te most ép arra készülsz, hogy egy új kiadást hozzál létre az adatbázisban, [_1]-ra alapozva az információkat.[br]
+ Klikkelj a 'szerkesztés' fülre a jobb felső sarokba ha egy új kiadás helyet, csak szerkeszteni akartad a meglévőt.
:_editmsg_msg_title
en : Before editing:
ru : Прежде чем приступить к редактированию:
+cs : Před editací:
+hu : Szerkesztés előtt:
:_editmsg_msg_guidelines
en : Read the [url,_1,guidelines]!
ru : Прочтите [url,_1,рекомендации]!
+cs : Přečtěte si [url,_1,doporučení]!
+hu : Olvasd el az [url,_1,útmutatókat]!
:_editmsg_msg_discuss
en : Check for any existing discussions on the [url,_1,discussion board]
ru : Проверьте все существующие темы на [url,_1,форуме]
+cs : Podívejte se do existujících diskusí na [url,_1,diskusním boardu]
+hu : Keress bármilyen meglévő beszélgetésre a [url,_1,fórumon]
:_editmsg_msg_history
en : Browse the [url,_1,edit history] for any recent changes related to what you want to change.
ru : Просмотрите [url,_1,историю правок] на предмет недавних изменений данных, которые вы хотите изменить.
+cs : Projděte [url,_1,historii editací] pro nedávné změny týkající se změn, které se chystáte provést.
+hu : Nézz bele a [url,_1,szerkesztési előzményekbe], hogy lásd milyen változtatások voltak mielőtt te is megváltoztatnál valamit.
:_editmsg_msg_search
en : [url,_1,Search the database] to see if we already have information about this [index,_2,visual novel,release,producer].
ru : [url,_1,Воспользуйтесь поиском], ведь вполне возможно что у нас уже есть информация об [index,_2,этой новелле,этом выпуске,этой компании].
+cs : [url,_1,Prohledejte databázi], zda již nemáme informaci o [index,_2,této vizuální novele,tomto vydání,tomto producentovi].
+hu : [url,_1,Nézd át az adatbázist], hogy van-e már információ erről a [index,_2,visual novelről, kiadásról, készítőről].
:_editmsg_revert_title
en : Reverting
ru : Восстановление
+cs : Vracení
+hu : Visszaállítás
:_editmsg_revert_msg
en : You are editing an old revision of this [index,_1,visual novel,release,producer].
If you save it, all changes made after this revision will be reverted!
ru : Вы правите старую редакцию страницы [index,_1,новеллы,выпуска,компании].
Если вы сохраните её, все изменения сделанные после этой правки будут утеряны!
+cs : Editujete starší verzi [index,_1,této vizuální novely,tohoto vydání,tohoto producenta].
+ Pokud ji uložíte, všechny změny provedené po této revizi budou vráceny!
+hu : Te egy régi átjavítását szerkeszted ennek a [index,_1,visual novelnek, kiadásnak, készítőnek].
+ Ha lemented, akkor minden szerkesztés amit ezután a javítás után végeztek megsemmisül!
# Messages about editing on the VN/Release/Producer pages (right beneath the tabs)
@@ -3276,14 +5691,20 @@ ru : Вы правите старую редакцию страницы [index,_
:_itemmsg_locked
en : Locked for editing
ru : Правка заблокирована
+cs : Zamčeno pro editaci
+hu : Lezárva
:_itemmsg_login
en : You need to be [url,_1,logged in] to edit this page
ru : Чтобы редактировать эту страницу, вы должны быть [url,_1,авторизованы]
+cs : Pro editaci této stránky musíte být [url,_1,přihlášen]
+hu : Be kell legyél [url,_1, jelentkezve], hogy szerkeszthesd ezt az oldalt
:_itemmsg_denied
en : You are not allowed to edit this page
ru : Вам запрещено редактировать эту страницу
+cs : Nemáte oprávnění editovat tuto stránku
+hu : Nincs engedélyed, hogy szerkeszd ezt az oldalt
# User didn't pass the spam protection
@@ -3291,14 +5712,20 @@ ru : Вам запрещено редактировать эту страниц
:_nospam_title
en : Could not send form
ru : Невозможно отправить форму
+cs : Formulář nemohl být odeslán
+hu : Nem lehetett elküldeni a formulát
:_nospam_subtitle
en : Error
ru : Ошибка
+cs : Chyba
+hu : Hiba
:_nospam_msg
en : The form could not be sent, please make sure you have Javascript enabled in your browser.
ru : Не удалось отправить форму, пожалуйста убедитесь что в вашем браузере включён Javascript.
+cs : Formulář nemohl být odeslán, ujistěte se, že máte ve svém prohlížeči povolen Javascript.
+hu : A formulát nem lehetett elküldeni, kérlek ellenőrizd, hogy engedélyezve van a JavaScript a böngésződbe.
# Short message reminding the user to post in ENGLISH (Used at about every message/description input field)
@@ -3306,5 +5733,21 @@ ru : Не удалось отправить форму, пожалуйста у
:_inenglish
en : English please!
ru : Пожалуйста, пишите на английском!
+cs : Prosíme, anglicky!
+hu : Kérlek angolul válaszolj!
+
+
+# No browser support to display the relation graphs
+
+:_rg_notsupp
+en : Not supported
+ru : Не поддерживается
+cs : Nepodporováno
+hu : Nem támogatott
+:_rg_notsupp_msg
+en : Your browser sucks, it doesn't have the functionality to render our nice relation graphs.
+ru : Ваш браузер настолько отстоен, что даже не может отрендерить наши графики.
+cs : Váš prohlížeč je na nic, nepodporuje vykreslování našich krásných vztahových grafů.
+hu : A böngésződ szar, nem képes megjeleníteni a szép összefüggés gráfunkat.
diff --git a/data/script.js b/data/script.js
new file mode 100644
index 00000000..78169c1b
--- /dev/null
+++ b/data/script.js
@@ -0,0 +1,1902 @@
+/* function/attribute prefixes:
+ * date -> Date selector
+ * dd -> dropdown
+ * ds -> dropdown search
+ * iv -> image viewer
+ * jt -> Javascript Tabs
+ * med -> Release media selector
+ * prr -> Producer relation editor
+ * rl -> Release List dropdown
+ * rpr -> Release <-> producer linking
+ * rvn -> Release <-> visual novel linking
+ * scr -> VN screenshot uploader
+ * tgl -> VN tag linking
+ * tvs -> VN page tag spoilers
+ * vnr -> VN relation editor
+ */
+var expanded_icon = '▾';
+var collapsed_icon = '▸';
+
+/* M I N I M A L J A V A S C R I P T L I B R A R Y */
+
+var http_request = false;
+function ajax(url, func) {
+ if(http_request)
+ http_request.abort();
+ http_request = (window.ActiveXObject) ? new ActiveXObject('Microsoft.XMLHTTP') : new XMLHttpRequest();
+ if(http_request == null)
+ return alert("Your browser does not support the functionality this website requires.");
+ http_request.onreadystatechange = function() {
+ if(!http_request || http_request.readyState != 4 || !http_request.responseText)
+ return;
+ if(http_request.status != 200)
+ return alert('Whoops, error! :(');
+ func(http_request);
+ };
+ url += (url.indexOf('?')>=0 ? ';' : '?')+(Math.floor(Math.random()*999)+1);
+ http_request.open('GET', url, true);
+ http_request.send(null);
+}
+
+function setCookie(n,v) {
+ var date = new Date();
+ date.setTime(date.getTime()+(365*24*60*60*1000));
+ document.cookie = n+'='+v+'; expires='+date.toGMTString()+'; path=/';
+}
+function getCookie(n) {
+ var l = document.cookie.split(';');
+ for(var i=0; i<l.length; i++) {
+ var c = l[i];
+ while(c.charAt(0) == ' ')
+ c = c.substring(1,c.length);
+ if(c.indexOf(n+'=') == 0)
+ return c.substring(n.length+1,c.length);
+ }
+ return null;
+}
+
+function byId(n) {
+ return document.getElementById(n)
+}
+function byName(){
+ var d = arguments.length > 1 ? arguments[0] : document;
+ var n = arguments.length > 1 ? arguments[1] : arguments[0];
+ return d.getElementsByTagName(n);
+}
+function byClass() { // [class], [parent, class], [tagname, class], [parent, tagname, class]
+ var par = typeof arguments[0] == 'object' ? arguments[0] : document;
+ var tag = arguments.length == 2 && typeof arguments[0] == 'string' ? arguments[0] : arguments.length == 3 ? arguments[1] : '*';
+ var c = arguments[arguments.length-1];
+ var l = byName(par, tag);
+ var ret = [];
+ for(var i=0; i<l.length; i++)
+ if(hasClass(l[i], c))
+ ret[ret.length] = l[i];
+ return ret;
+}
+
+/* wrapper around DOM element creation
+ * tag('string') -> createTextNode
+ * tag('tagname', tag(), 'string', ..) -> createElement(), appendChild(), ..
+ * tag('tagname', { class: 'meh', title: 'Title' }) -> createElement(), setAttribute()..
+ * tag('tagname', { <attributes> }, <elements>) -> create, setattr, append */
+function tag() {
+ if(arguments.length == 1)
+ return typeof arguments[0] != 'object' ? document.createTextNode(arguments[0]) : arguments[0];
+ var el = typeof document.createElementNS != 'undefined'
+ ? document.createElementNS('http://www.w3.org/1999/xhtml', arguments[0])
+ : document.createElement(arguments[0]);
+ for(var i=1; i<arguments.length; i++) {
+ if(arguments[i] == null)
+ continue;
+ if(typeof arguments[i] == 'object' && !arguments[i].appendChild) {
+ for(attr in arguments[i]) {
+ if(attr == 'style')
+ el.setAttribute(attr, arguments[i][attr]);
+ else
+ el[ attr == 'class' ? 'className' : attr == 'for' ? 'htmlFor' : attr ] = arguments[i][attr];
+ }
+ } else
+ el.appendChild(tag(arguments[i]));
+ }
+ return el;
+}
+function addBody(el) {
+ if(document.body.appendChild)
+ document.body.appendChild(el);
+ else if(document.documentElement.appendChild)
+ document.documentElement.appendChild(el);
+ else if(document.appendChild)
+ document.appendChild(el);
+}
+function setContent() {
+ setText(arguments[0], '');
+ for(var i=1; i<arguments.length; i++)
+ arguments[0].appendChild(tag(arguments[i]));
+}
+function getText(obj) {
+ return obj.textContent || obj.innerText || '';
+}
+function setText(obj, txt) {
+ if(obj.textContent != null)
+ obj.textContent = txt;
+ else
+ obj.innerText = txt;
+}
+
+function listClass(obj) {
+ var n = obj.className;
+ if(!n)
+ return [];
+ return n.split(/ /);
+}
+function hasClass(obj, c) {
+ var l = listClass(obj);
+ for(var i=0; i<l.length; i++)
+ if(l[i] == c)
+ return true;
+ return false;
+}
+function setClass(obj, c, set) {
+ var l = listClass(obj);
+ var n = [];
+ if(set) {
+ n = l;
+ if(!hasClass(obj, c))
+ n[n.length] = c;
+ } else {
+ for(var i=0; i<l.length; i++)
+ if(l[i] != c)
+ n[n.length] = l[i];
+ }
+ obj.className = n.join(' ');
+}
+
+function shorten(v, l) {
+ return v.length > l ? v.substr(0, l-3)+'...' : v;
+}
+
+/* maketext function, less powerful than the Perl equivalent:
+ * - Only supports [_n], ~[, ~]
+ * - When it finds [quant,_n,..], it will only return the first argument (and doesn't support ~ in an argument)
+ * assumes that a TL structure called 'L10N_STR' is defined in the header of this file */
+var mt_curlang = getCookie('l10n') || 'en';
+function mt() {
+ var key = arguments[0];
+ var val = L10N_STR[key] ? L10N_STR[key][mt_curlang] || L10N_STR[key].en : key;
+ for(var i=1; i<arguments.length; i++) {
+ var expr = '[_'+i+']';
+ while(val.indexOf(expr) >= 0)
+ val = val.replace(expr, arguments[i]);
+ }
+ val = val.replace(/\[quant,_\d+\,([^,]+)[^\]]+\]/g, "$1");
+ while(val.indexOf('~[') >= 0 || val.indexOf('~]') >= 0)
+ val = val.replace('~[', '[').replace('~]', ']');
+ return val;
+}
+
+
+
+
+/* I M A G E V I E W E R */
+
+function ivInit() {
+ var init = 0;
+ var l = byName('a');
+ for(var i=0;i<l.length;i++)
+ if(l[i].rel.substr(0,3) == 'iv:') {
+ init++;
+ l[i].onclick = ivView;
+ }
+ if(init && !byId('iv_view')) {
+ addBody(tag('div', {id: 'iv_view'},
+ tag('b', {id:'ivimg'}, ''),
+ tag('br', null),
+ tag('a', {href:'#', id:'ivfull'}, ''),
+ tag('a', {href:'#', onclick: ivClose, id:'ivclose'}, mt('_js_iv_close')),
+ tag('a', {href:'#', onclick: ivView, id:'ivprev'}, '« '+mt('_js_iv_prev')),
+ tag('a', {href:'#', onclick: ivView, id:'ivnext'}, mt('_js_iv_next')+' »')
+ ));
+ addBody(tag('b', {id:'ivimgload'}, mt('_js_loading')));
+ }
+}
+
+function ivView(what) {
+ what = what && what.rel ? what : this;
+ var u = what.href;
+ var opt = what.rel.split(':');
+ var view = byId('iv_view');
+ var next = byId('ivnext');
+ var prev = byId('ivprev');
+ var full = byId('ivfull');
+
+ // fix prev/next links (if any)
+ if(opt[2]) {
+ var ol = byName('a');
+ var l=[];
+ for(i=0;i<ol.length;i++)
+ if(ol[i].rel.substr(0,3) == 'iv:' && ol[i].rel.indexOf(':'+opt[2]) > 4 && !hasClass(ol[i], 'hidden') && ol[i].id != 'ivprev' && ol[i].id != 'ivnext')
+ l[l.length] = ol[i];
+ for(i=0;i<l.length;i++)
+ if(l[i].href == u) {
+ next.style.visibility = l[i+1] ? 'visible' : 'hidden';
+ next.href = l[i+1] ? l[i+1].href : '#';
+ next.rel = l[i+1] ? l[i+1].rel : '';
+ prev.style.visibility = l[i-1] ? 'visible' : 'hidden';
+ prev.href = l[i-1] ? l[i-1].href : '#';
+ prev.rel = l[i-1] ? l[i-1].rel : '';
+ }
+ } else
+ next.style.visibility = prev.style.visibility = 'hidden';
+
+ // calculate dimensions
+ var w = Math.floor(opt[1].split('x')[0]);
+ var h = Math.floor(opt[1].split('x')[1]);
+ var ww = typeof(window.innerWidth) == 'number' ? window.innerWidth : document.documentElement.clientWidth;
+ var wh = typeof(window.innerHeight) == 'number' ? window.innerHeight : document.documentElement.clientHeight;
+ var st = typeof(window.pageYOffset) == 'number' ? window.pageYOffset : document.body && document.body.scrollTop ? document.body.scrollTop : document.documentElement.scrollTop;
+ if(w+100 > ww || h+70 > wh) {
+ full.href = u;
+ setText(full, w+'x'+h);
+ full.style.visibility = 'visible';
+ if(w/h > ww/wh) { // width++
+ h *= (ww-100)/w;
+ w = ww-100;
+ } else { // height++
+ w *= (wh-70)/h;
+ h = wh-70;
+ }
+ } else
+ full.style.visibility = 'hidden';
+ var dw = w;
+ var dh = h+20;
+ dw = dw < 200 ? 200 : dw;
+
+ // update document
+ view.style.display = 'block';
+ setContent(byId('ivimg'), tag('img', {src:u, onclick:ivClose,
+ onload: function() { byId('ivimgload').style.top='-400px'; },
+ style: 'width: '+w+'px; height: '+h+'px'
+ }));
+ view.style.width = dw+'px';
+ view.style.height = dh+'px';
+ view.style.left = ((ww - dw) / 2 - 10)+'px';
+ view.style.top = ((wh - dh) / 2 + st - 20)+'px';
+ byId('ivimgload').style.left = ((ww - 100) / 2 - 10)+'px';
+ byId('ivimgload').style.top = ((wh - 20) / 2 + st)+'px';
+ return false;
+}
+
+function ivClose() {
+ byId('iv_view').style.display = 'none';
+ byId('iv_view').style.top = '-5000px';
+ byId('ivimgload').style.top = '-400px';
+ setText(byId('ivimg'), '');
+ return false;
+}
+
+ivInit();
+
+
+
+
+/* D R O P D O W N */
+
+function ddInit(obj, align, contents) {
+ obj.dd_align = align; // see ddRefresh for details
+ obj.dd_contents = contents;
+ document.onmousemove = ddMouseMove;
+ document.onscroll = ddHide;
+ if(!byId('dd_box'))
+ addBody(tag('div', {id:'dd_box', dd_used: false}));
+}
+
+function ddHide() {
+ var box = byId('dd_box');
+ setText(box, '');
+ box.style.left = '-500px';
+ box.dd_used = false;
+ box.dd_lnk = null;
+}
+
+function ddMouseMove(e) {
+ e = e || window.event;
+ var lnk = e.target || e.srcElement;
+ while(lnk && (lnk.nodeType == 3 || !lnk.dd_align))
+ lnk = lnk.parentNode;
+ var box = byId('dd_box');
+ if(!box.dd_used && !lnk)
+ return;
+
+ if(box.dd_used) {
+ var mouseX = e.pageX || (e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft);
+ var mouseY = e.pageY || (e.clientY + document.body.scrollTop + document.documentElement.scrollTop);
+ if((mouseX < ddx-10 || mouseX > ddx+box.offsetWidth+10 || mouseY < ddy-10 || mouseY > ddy+box.offsetHeight+10)
+ || (lnk && lnk == box.dd_lnk))
+ ddHide();
+ }
+
+ if(!box.dd_used && lnk) {
+ box.dd_lnk = lnk;
+ box.dd_used = true;
+ if(!ddRefresh())
+ ddHide();
+ }
+}
+
+function ddRefresh() {
+ var box = byId('dd_box');
+ if(!box.dd_used)
+ return false;
+ var lnk = box.dd_lnk;
+ var content = lnk.dd_contents(lnk, box);
+ if(content == null)
+ return false;
+ setContent(box, content);
+
+ var o = lnk;
+ ddx = ddy = 0;
+ do {
+ ddx += o.offsetLeft;
+ ddy += o.offsetTop;
+ } while(o = o.offsetParent);
+
+ if(lnk.dd_align == 'left')
+ ddx -= box.offsetWidth;
+ if(lnk.dd_align == 'tagmod')
+ ddx += lnk.offsetWidth-35;
+ if(lnk.dd_align == 'bottom')
+ ddy += lnk.offsetHeight;
+ box.style.left = ddx+'px';
+ box.style.top = ddy+'px';
+ return true;
+}
+
+
+// release list dropdown on VN pages
+
+function rlDropDown(lnk) {
+ var relid = lnk.id.substr(6);
+ var st = getText(lnk).split(' / ');
+ if(st[0].indexOf(mt('_js_loading')) >= 0)
+ return null;
+
+ var rs = tag('ul', tag('li', tag('b', mt('_vnpage_uopt_relrstat'))));
+ var vs = tag('ul', tag('li', tag('b', mt('_vnpage_uopt_relvstat'))));
+ for(var i=0; i<rlst_rstat.length; i++) {
+ var val = mt('_rlst_rstat_'+rlst_rstat[i]);
+ if(st[0] && st[0].indexOf(val) >= 0)
+ rs.appendChild(tag('li', tag('i', val)));
+ else
+ rs.appendChild(tag('li', tag('a', {href:'#', rl_rid:relid, rl_act:'r'+rlst_rstat[i], onclick:rlMod}, val)));
+ }
+ for(var i=0; i<rlst_vstat.length; i++) {
+ var val = mt('_rlst_vstat_'+rlst_vstat[i]);
+ if(st[1] && st[1].indexOf(val) >= 0)
+ vs.appendChild(tag('li', tag('i', val)));
+ else
+ vs.appendChild(tag('li', tag('a', {href:'#', rl_rid:relid, rl_act:'v'+rlst_vstat[i], onclick:rlMod}, val)));
+ }
+
+ return tag('div', {'class':'vrdd'}, rs, vs, st[0] == '--' ? null :
+ tag('ul', {'class':'full'}, tag('li', tag('a', {href:'#', rl_rid: relid, rl_act:'del', onclick:rlMod}, mt('_vnpage_uopt_reldel'))))
+ );
+}
+
+function rlMod() {
+ var lnk = byId('rlsel_'+this.rl_rid);
+ ddHide();
+ setContent(lnk, tag('b', {'class': 'grayedout'}, mt('_js_loading')));
+ ajax('/xml/rlist.xml?id='+this.rl_rid+';e='+this.rl_act, function(hr) {
+ // TODO: get rid of innerHTML here...
+ lnk.innerHTML = hr.responseXML.getElementsByTagName('rlist')[0].firstChild.nodeValue;
+ });
+ return false;
+}
+
+{
+ var l = byClass('a', 'vnrlsel');
+ for(var i=0;i<l.length;i++)
+ ddInit(l[i], 'left', rlDropDown);
+}
+
+
+
+
+/* J A V A S C R I P T T A B S */
+
+function jtInit() {
+ if(!byId('jt_select'))
+ return;
+ var sel = '';
+ var first = '';
+ var l = byName(byId('jt_select'), 'a');
+ if(l.length < 1)
+ return;
+ for(var i=0; i<l.length; i++) {
+ l[i].onclick = jtSel;
+ if(!first)
+ first = l[i].id;
+ if(location.hash && l[i].id == 'jt_sel_'+location.hash.substr(1))
+ sel = l[i].id;
+ }
+ if(!sel)
+ sel = first;
+ jtSel(sel, 1);
+}
+
+function jtSel(which, nolink) {
+ which = typeof(which) == 'string' ? which : which && which.id ? which.id : this.id;
+ which = which.substr(7);
+
+ var l = byName(byId('jt_select'), 'a');
+ for(var i=0;i<l.length;i++) {
+ var name = l[i].id.substr(7);
+ if(name != 'all')
+ byId('jt_box_'+name).style.display = name == which || which == 'all' ? 'block' : 'none';
+ var tab = l[i].parentNode;
+ setClass(tab, 'tabselected', name == which);
+ }
+
+ if(!nolink)
+ location.href = '#'+which;
+ return false;
+}
+
+jtInit();
+
+
+
+
+/* V N P A G E T A G S P O I L E R S */
+
+function tvsInit() {
+ if(!byId('tagops'))
+ return;
+ var l = byName(byId('tagops'), 'a');
+ for(var i=0;i<l.length; i++)
+ l[i].onclick = tvsClick;
+ tvsSet(getCookie('tagspoil'), true);
+}
+
+function tvsClick() {
+ var sel;
+ var l = byName(byId('tagops'), 'a');
+ for(var i=0; i<l.length; i++)
+ if(l[i] == this) {
+ if(i < 3) {
+ tvsSet(i, null);
+ setCookie('tagspoil', i);
+ } else
+ tvsSet(null, i == 3 ? true : false);
+ }
+ return false;
+}
+
+function tvsSet(lvl, lim) {
+ /* set/get level and limit to/from the links */
+ var l = byName(byId('tagops'), 'a');
+ for(var i=0; i<l.length; i++) {
+ if(i < 3) { /* spoiler level */
+ if(lvl != null)
+ setClass(l[i], 'tsel', i == lvl);
+ if(lvl == null && hasClass(l[i], 'tsel'))
+ lvl = i;
+ } else { /* display limit (3 = summary) */
+ if(lim != null)
+ setClass(l[i], 'tsel', lim == (i == 3));
+ if(lim == null && hasClass(l[i], 'tsel'))
+ lim = i == 3;
+ }
+ }
+
+ /* update tag visibility */
+ l = byName(byId('vntags'), 'span');
+ lim = lim ? 15 : 999;
+ var s=0;
+ for(i=0;i<l.length;i++) {
+ var thislvl = l[i].className.substr(6, 1);
+ if(thislvl <= lvl && s < lim) {
+ setClass(l[i], 'hidden', false);
+ s++;
+ } else
+ setClass(l[i], 'hidden', true);
+ }
+ return false;
+}
+
+tvsInit();
+
+
+
+
+/* D A T E I N P U T */
+
+function dateLoad(obj) {
+ var val = Math.floor(obj.value) || 0;
+ val = [ Math.floor(val/10000), Math.floor(val/100)%100, val%100 ];
+
+ var year = tag('select', {style: 'width: 70px', onchange: dateSerialize}, tag('option', {value:0}, mt('_js_date_year')));
+ for(var i=1980; i<=(new Date()).getFullYear()+5; i++)
+ year.appendChild(tag('option', {value: i, selected: i==val[0]}, i));
+ year.appendChild(tag('option', {value: 9999, selected: val[0]==9999}, 'TBA'));
+
+ var month = tag('select', {style: 'width: 70px', onchange: dateSerialize}, tag('option', {value:99}, mt('_js_date_month')));
+ for(var i=1; i<=12; i++)
+ month.appendChild(tag('option', {value: i, selected: i==val[1]}, i));
+
+ var day = tag('select', {style: 'width: 70px', onchange: dateSerialize}, tag('option', {value:99}, mt('_js_date_day')));
+ for(var i=1; i<=31; i++)
+ day.appendChild(tag('option', {value: i, selected: i==val[2]}, i));
+
+ obj.parentNode.insertBefore(tag('div', {date_obj: obj}, year, month, day), obj);
+}
+
+function dateSerialize() {
+ var div = this.parentNode;
+ var sel = byName(div, 'select');
+ var val = [
+ sel[0].options[sel[0].selectedIndex].value*1,
+ sel[1].options[sel[1].selectedIndex].value*1,
+ sel[2].options[sel[2].selectedIndex].value*1
+ ];
+ div.date_obj.value = val[0] == 0 ? 0 : val[0] == 9999 ? 99999999 : val[0]*10000+val[1]*100+(val[1]==99?99:val[2]);
+}
+
+{
+ var l = byClass('input', 'dateinput');
+ for(i=0; i<l.length; i++)
+ dateLoad(l[i]);
+}
+
+
+
+
+/* D R O P D O W N S E A R C H */
+
+function dsInit(obj, url, trfunc, serfunc, retfunc, parfunc) {
+ obj.setAttribute('autocomplete', 'off');
+ obj.onkeydown = dsKeyDown;
+ obj.onblur = function() { setTimeout(function () { byId('ds_box').style.top = '-500px'; }, 500) };
+ obj.ds_returnFunc = retfunc;
+ obj.ds_trFunc = trfunc;
+ obj.ds_serFunc = serfunc;
+ obj.ds_parFunc = parfunc;
+ obj.ds_searchURL = url;
+ obj.ds_selectedId = 0;
+ obj.ds_dosearch = null;
+ if(!byId('ds_box'))
+ addBody(tag('div', {id: 'ds_box', style: 'position: absolute; top: -500px'}, tag('b', mt('_js_loading'))));
+}
+
+function dsKeyDown(ev) {
+ var c = document.layers ? ev.which : document.all ? event.keyCode : ev.keyCode;
+ var obj = this;
+
+ if(c == 9) // tab
+ return true;
+
+ // do some processing when the enter key has been pressed
+ if(c == 13) {
+ var frm = obj;
+ while(frm && frm.nodeName.toLowerCase() != 'form')
+ frm = frm.parentNode;
+ if(frm) {
+ var oldsubmit = frm.onsubmit;
+ frm.onsubmit = function() { return false };
+ setTimeout(function() { frm.onsubmit = oldsubmit }, 100);
+ }
+
+ if(obj.ds_selectedId != 0)
+ obj.value = obj.ds_serFunc(byId('ds_box_'+obj.ds_selectedId).ds_itemData, obj);
+ if(obj.ds_returnFunc)
+ obj.ds_returnFunc();
+
+ byId('ds_box').style.top = '-500px';
+ setContent(byId('ds_box'), tag('b', mt('_js_loading')));
+ obj.ds_selectedId = 0;
+ if(obj.ds_dosearch) {
+ clearTimeout(obj.ds_dosearch);
+ obj.ds_dosearch = null;
+ }
+
+ return false;
+ }
+
+ // process up/down keys
+ if(c == 38 || c == 40) {
+ var l = byName(byId('ds_box'), 'tr');
+ if(l.length < 1)
+ return true;
+
+ // get new selected id
+ if(obj.ds_selectedId == 0) {
+ if(c == 38) // up
+ obj.ds_selectedId = l[l.length-1].id.substr(7);
+ else
+ obj.ds_selectedId = l[0].id.substr(7);
+ } else {
+ var sel = null;
+ for(var i=0; i<l.length; i++)
+ if(l[i].id == 'ds_box_'+obj.ds_selectedId) {
+ if(c == 38) // up
+ sel = i>0 ? l[i-1] : l[l.length-1];
+ else
+ sel = l[i+1] ? l[i+1] : l[0];
+ }
+ obj.ds_selectedId = sel.id.substr(7);
+ }
+
+ // set selected class
+ for(var i=0; i<l.length; i++)
+ setClass(l[i], 'selected', l[i].id == 'ds_box_'+obj.ds_selectedId);
+ return true;
+ }
+
+ // perform search after a timeout
+ if(obj.ds_dosearch)
+ clearTimeout(obj.ds_dosearch);
+ obj.ds_dosearch = setTimeout(function() {
+ dsSearch(obj);
+ }, 500);
+
+ return true;
+}
+
+function dsSearch(obj) {
+ var box = byId('ds_box');
+ var val = obj.ds_parFunc ? obj.ds_parFunc(obj.value) : obj.value;
+
+ clearTimeout(obj.ds_dosearch);
+ obj.ds_dosearch = null;
+
+ // hide the ds_box div
+ if(val.length < 2) {
+ box.style.top = '-500px';
+ setContent(box, tag('b', mt('_js_loading')));
+ obj.ds_selectedId = 0;
+ return;
+ }
+
+ // position the div
+ var ddx=0;
+ var ddy=obj.offsetHeight;
+ var o = obj;
+ do {
+ ddx += o.offsetLeft;
+ ddy += o.offsetTop;
+ } while(o = o.offsetParent);
+
+ box.style.position = 'absolute';
+ box.style.left = ddx+'px';
+ box.style.top = ddy+'px';
+ box.style.width = obj.offsetWidth+'px';
+
+ // perform search
+ ajax(obj.ds_searchURL + encodeURIComponent(val), function(hr) {
+ dsResults(hr, obj);
+ });
+}
+
+function dsResults(hr, obj) {
+ var lst = hr.responseXML.getElementsByTagName('item');
+ var box = byId('ds_box');
+ if(lst.length < 1) {
+ setContent(box, tag('b', mt('_js_ds_noresults')));
+ obj.selectedId = 0;
+ return;
+ }
+
+ var tb = tag('tbody', null);
+ for(var i=0; i<lst.length; i++) {
+ var id = lst[i].getAttribute('id');
+ var tr = tag('tr', {id: 'ds_box_'+id, ds_itemData: lst[i]} );
+ setClass(tr, 'selected', obj.selectedId == id);
+
+ tr.onmouseover = function() {
+ obj.ds_selectedId = this.id.substr(7);
+ var l = byName(box, 'tr');
+ for(var j=0; j<l.length; j++)
+ setClass(l[j], 'selected', l[j].id == 'ds_box_'+obj.ds_selectedId);
+ };
+ tr.onmousedown = function() {
+ obj.value = obj.ds_serFunc(this.ds_itemData, obj);
+ if(obj.ds_returnFunc)
+ obj.ds_returnFunc();
+ box.style.top = '-500px';
+ obj.ds_selectedId = 0;
+ };
+
+ obj.ds_trFunc(lst[i], tr);
+ tb.appendChild(tr);
+ }
+ setContent(box, tag('table', tb));
+
+ if(obj.ds_selectedId != 0 && !byId('ds_box_'+obj.ds_selectedId))
+ obj.ds_selectedId = 0;
+}
+
+
+
+
+/* V I S U A L N O V E L R E L A T I O N S (/v+/edit) */
+
+function vnrLoad() {
+ // read the current relations
+ var rels = byId('vnrelations').value.split('|||');
+ for(var i=0; i<rels.length && rels[0].length>1; i++) {
+ var rel = rels[i].split(',', 3);
+ vnrAdd(rel[0], rel[1], rel[2]);
+ }
+ vnrEmpty();
+
+ // make sure the title is up-to-date
+ byId('title').onchange = function() {
+ var l = byClass(byId('jt_box_vn_rel'), 'td', 'tc_title');
+ for(i=0; i<l.length; i++)
+ setText(l[i], shorten(this.value, 40));
+ };
+
+ // bind the add-link
+ byName(byClass(byId('relation_new'), 'td', 'tc_add')[0], 'a')[0].onclick = vnrFormAdd;
+
+ // dropdown
+ dsInit(byName(byClass(byId('relation_new'), 'td', 'tc_vn')[0], 'input')[0], '/xml/vn.xml?q=', function(item, tr) {
+ tr.appendChild(tag('td', { style: 'text-align: right; padding-right: 5px'}, 'v'+item.getAttribute('id')));
+ tr.appendChild(tag('td', shorten(item.firstChild.nodeValue, 40)));
+ }, function(item) {
+ return 'v'+item.getAttribute('id')+':'+item.firstChild.nodeValue;
+ }, vnrFormAdd);
+}
+
+function vnrAdd(rel, vid, title) {
+ var sel = tag('select', {onchange: vnrSerialize});
+ var ops = byName(byClass(byId('relation_new'), 'td', 'tc_rel')[0], 'select')[0].options;
+ for(var i=0; i<ops.length; i++)
+ sel.appendChild(tag('option', {value: ops[i].value, selected: ops[i].value==rel}, getText(ops[i])));
+
+ byId('relation_tbl').appendChild(tag('tr', {id:'relation_tr_'+vid},
+ tag('td', {'class':'tc_vn' }, 'v'+vid+':', tag('a', {href:'/v'+vid}, shorten(title, 40))),
+ tag('td', {'class':'tc_rel' }, mt('_vnedit_rel_isa')+' ', sel, ' '+mt('_vnedit_rel_of')),
+ tag('td', {'class':'tc_title'}, shorten(byId('title').value, 40)),
+ tag('td', {'class':'tc_add' }, tag('a', {href:'#', onclick:vnrDel}, mt('_vnedit_rel_del')))
+ ));
+
+ vnrEmpty();
+}
+
+function vnrEmpty() {
+ var tbl = byId('relation_tbl');
+ if(byName(tbl, 'tr').length < 1)
+ tbl.appendChild(tag('tr', {id:'relation_tr_none'}, tag('td', {colspan:4}, mt('_vnedit_rel_none'))));
+ else if(byId('relation_tr_none'))
+ tbl.removeChild(byId('relation_tr_none'));
+}
+
+function vnrSerialize() {
+ var r = [];
+ var trs = byName(byId('relation_tbl'), 'tr');
+ for(var i=0; i<trs.length; i++) {
+ if(trs[i].id == 'relation_tr_none')
+ continue;
+ var rel = byName(byClass(trs[i], 'td', 'tc_rel')[0], 'select')[0];
+ r[r.length] = [
+ rel.options[rel.selectedIndex].value, // relation
+ trs[i].id.substr(12), // vid
+ getText(byName(byClass(trs[i], 'td', 'tc_vn')[0], 'a')[0]) // title
+ ].join(',');
+ }
+ byId('vnrelations').value = r.join('|||');
+}
+
+function vnrDel() {
+ var tr = this;
+ while(tr.nodeName.toLowerCase() != 'tr')
+ tr = tr.parentNode;
+ byId('relation_tbl').removeChild(tr);
+ vnrSerialize();
+ vnrEmpty();
+ return false;
+}
+
+function vnrFormAdd() {
+ var relnew = byId('relation_new');
+ var txt = byName(byClass(relnew, 'td', 'tc_vn')[0], 'input')[0];
+ var sel = byName(byClass(relnew, 'td', 'tc_rel')[0], 'select')[0];
+ var lnk = byName(byClass(relnew, 'td', 'tc_add')[0], 'a')[0];
+ var input = txt.value;
+
+ if(!input.match(/^v[0-9]+/)) {
+ alert('_vnedit_rel_findformat');
+ return false;
+ }
+
+ txt.disabled = sel.disabled = true;
+ txt.value = mt('_js_loading');
+ setText(lnk, mt('_js_loading'));
+
+ ajax('/xml/vn.xml?q='+encodeURIComponent(input), function(hr) {
+ txt.disabled = sel.disabled = false;
+ txt.value = '';
+ setText(lnk, mt('_vnedit_rel_addbut'));
+
+ var items = hr.responseXML.getElementsByTagName('item');
+ if(items.length < 1)
+ return alert(mt('_vnedit_rel_novn'));
+
+ var id = items[0].getAttribute('id');
+ if(byId('relation_tr_'+id))
+ return alert(mt('_vnedit_rel_double'));
+
+ vnrAdd(sel.options[sel.selectedIndex].value, id, items[0].firstChild.nodeValue);
+ sel.selectedIndex = 0;
+ vnrSerialize();
+ });
+ return false;
+}
+
+if(byId('vnrelations'))
+ vnrLoad();
+
+
+
+
+/* R E L E A S E M E D I A (/r+/edit) */
+
+var medTypes = [ ];
+function medLoad() {
+ // load the medTypes and clear the div
+ var sel = byName(byId('media_div'), 'select')[0].options;
+ for(var i=0; i<sel.length; i++)
+ medTypes[medTypes.length] = [ sel[i].value, getText(sel[i]), !hasClass(sel[i], 'noqty') ];
+ setText(byId('media_div'), '');
+
+ // load the selected media
+ var med = byId('media').value.split(',');
+ for(var i=0; i<med.length && med[i].length > 1; i++)
+ medAdd(med[i].split(' ')[0], Math.floor(med[i].split(' ')[1]));
+
+ medAdd('', 0);
+}
+
+function medAdd(med, qty) {
+ var qsel = tag('select', {'class':'qty', onchange:medSerialize}, tag('option', {value:0}, mt('_redit_form_med_quantity')));
+ for(var i=1; i<=20; i++)
+ qsel.appendChild(tag('option', {value:i, selected: qty==i}, i));
+
+ var msel = tag('select', {'class':'medium', onchange: med == '' ? medFormAdd : medSerialize});
+ if(med == '')
+ msel.appendChild(tag('option', {value:''}, mt('_redit_form_med_medium')));
+ for(var i=0; i<medTypes.length; i++)
+ msel.appendChild(tag('option', {value:medTypes[i][0], selected: med==medTypes[i][0]}, medTypes[i][1]));
+
+ byId('media_div').appendChild(tag('span', qsel, msel,
+ med != '' ? tag('input', {type: 'button', 'class':'submit', onclick:medDel, value:mt('_redit_form_med_remove')}) : null
+ ));
+}
+
+function medDel() {
+ var span = this;
+ while(span.nodeName.toLowerCase() != 'span')
+ span = span.parentNode;
+ byId('media_div').removeChild(span);
+ medSerialize();
+ return false;
+}
+
+function medFormAdd() {
+ var span = this;
+ while(span.nodeName.toLowerCase() != 'span')
+ span = span.parentNode;
+ var med = byClass(span, 'select', 'medium')[0];
+ var qty = byClass(span, 'select', 'qty')[0];
+ if(!med.selectedIndex)
+ return;
+ medAdd(med.options[med.selectedIndex].value, qty.options[qty.selectedIndex].value);
+ byId('media_div').removeChild(span);
+ medAdd('', 0);
+ medSerialize();
+}
+
+function medSerialize() {
+ var r = [];
+ var meds = byName(byId('media_div'), 'span');
+ for(var i=0; i<meds.length-1; i++) {
+ var med = byClass(meds[i], 'select', 'medium')[0];
+ var qty = byClass(meds[i], 'select', 'qty')[0];
+
+ /* correct quantity if necessary */
+ if(medTypes[med.selectedIndex][2] && !qty.selectedIndex)
+ qty.selectedIndex = 1;
+ if(!medTypes[med.selectedIndex][2] && qty.selectedIndex)
+ qty.selectedIndex = 0;
+
+ r[r.length] = medTypes[med.selectedIndex][0] + ' ' + qty.selectedIndex;
+ }
+ byId('media').value = r.join(',');
+}
+
+if(byId('jt_box_rel_format'))
+ medLoad();
+
+
+
+
+/* V I S U A L N O V E L S C R E E N S H O T U P L O A D E R (/v+/edit) */
+
+var scrRel = [ [ 0, mt('_vnedit_scr_selrel') ] ];
+var scrStaticURL;
+var scrUplNr = 0;
+
+function scrLoad() {
+ // get scrRel and scrStaticURL
+ var rel = byId('scr_rel');
+ scrStaticURL = rel.className;
+ for(var i=0; i<rel.options.length; i++)
+ scrRel[scrRel.length] = [ rel.options[i].value, getText(rel.options[i]) ];
+ rel.parentNode.removeChild(rel);
+
+ // load the current screenshots
+ var scr = byId('screenshots').value.split(' ');
+ for(i=0; i<scr.length && scr[i].length>1; i++) {
+ var r = scr[i].split(',');
+ scrAdd(r[0], r[1], r[2]);
+ }
+
+ scrLast();
+ scrCheckStatus();
+ scrSetSubmit();
+}
+
+function scrSetSubmit() {
+ var frm = byId('screenshots');
+ while(frm.nodeName.toLowerCase() != 'form')
+ frm = frm.parentNode;
+ oldfunc = frm.onsubmit;
+ frm.onsubmit = function() {
+ var loading = 0;
+ var norelease = 0;
+ var l = byName(byId('scr_table'), 'tr');
+ for(var i=0; i<l.length-1; i++) {
+ if(l[i].scr_status > 0)
+ loading = 1;
+ else if(byName(l[i], 'select')[0].selectedIndex == 0)
+ norelease = 1;
+ }
+ if(loading) {
+ alert(mt('_vnedit_scr_frmloading'));
+ return false;
+ } else if(norelease) {
+ alert(mt('_vnedit_scr_frmnorel'));
+ return false;
+ } else if(oldfunc)
+ return oldfunc();
+ };
+}
+
+function scrURL(id, t) {
+ return scrStaticURL+'/s'+t+'/'+(id%100<10?'0':'')+(id%100)+'/'+id+'.jpg';
+}
+
+function scrAdd(id, nsfw, rel) {
+ // tr.scr_status = 0: done, 1: uploading, 2: waiting for thumbnail, 3: deleted
+
+ var tr = tag('tr', { id:'scr_tr_'+id, scr_id: id, scr_status: id?2:1, scr_rel: rel, scr_nsfw: nsfw},
+ tag('td', { 'class': 'thumb'}, mt('_js_loading')),
+ tag('td',
+ tag('b', mt(id ? '_vnedit_scr_fetching' : '_vnedit_scr_uploading')),
+ tag('br', null),
+ id ? null : mt('_vnedit_scr_upl_msg'),
+ tag('br', null),
+ id ? null : tag('a', {href:'#', onclick:scrDel}, mt('_vnedit_scr_cancel'))
+ )
+ );
+ byId('scr_table').appendChild(tr);
+ scrStripe();
+ return tr;
+}
+
+function scrLast() {
+ if(byId('scr_last'))
+ byId('scr_table').removeChild(byId('scr_last'));
+ var full = byName(byId('scr_table'), 'tr').length >= 10;
+
+ byId('scr_table').appendChild(tag('tr', {id:'scr_last'},
+ tag('td', {'class': 'thumb'}),
+ full ? tag('td',
+ tag('b', mt('_vnedit_scr_full')),
+ tag('br', null),
+ mt('_vnedit_scr_full_msg')
+ ) : tag('td',
+ tag('b', mt('_vnedit_scr_add')),
+ tag('br', null),
+ mt('_vnedit_scr_imgnote'),
+ tag('br', null),
+ tag('input', {name:'scr_upload', id:'scr_upload', type:'file', 'class':'text'}),
+ tag('br', null),
+ tag('input', {type:'button', value:mt('_vnedit_scr_addbut'), 'class':'submit', onclick:scrUpload})
+ )
+ ));
+ scrStripe();
+}
+
+function scrStripe() {
+ var l = byName(byId('scr_table'), 'tr');
+ for(var i=0; i<l.length; i++)
+ setClass(l[i], 'odd', i%2==0);
+}
+
+function scrCheckStatus() {
+ var ids = [];
+ var trs = byName(byId('scr_table'), 'tr');
+ for(var i=0; i<trs.length-1; i++)
+ if(trs[i].scr_status == 2)
+ ids[ids.length] = 'id='+trs[i].scr_id;
+ if(!ids.length)
+ return setTimeout(scrCheckStatus, 1000);
+
+ var ti = setTimeout(scrCheckStatus, 10000);
+ ajax('/xml/screenshots.xml?'+ids.join(';'), function(hr) {
+ var ls = hr.responseXML.getElementsByTagName('item');
+ for(var i=0; i<ls.length; i++) {
+ var tr = byId('scr_tr_'+ls[i].getAttribute('id'));
+ if(!tr || ls[i].getAttribute('processed') != '1')
+ continue;
+ tr.scr_status = 0; // ready
+
+ // image
+ var dim = ls[i].getAttribute('width')+'x'+ls[i].getAttribute('height');
+ setContent(byName(tr, 'td')[0],
+ tag('a', {href: scrURL(tr.scr_id, 'f'), rel:'iv:'+dim+':edit'},
+ tag('img', {src: scrURL(tr.scr_id, 't')})
+ )
+ );
+
+ // content
+ var rel = tag('select', {onchange: scrSerialize, 'class':'scr_relsel'});
+ for(var j=0; j<scrRel.length; j++)
+ rel.appendChild(tag('option', {value: scrRel[j][0], selected: tr.scr_rel == scrRel[j][0]}, scrRel[j][1]));
+ var nsfwid = 'scr_sfw_'+tr.scr_id;
+ setContent(byName(tr, 'td')[1],
+ tag('b', mt('_vnedit_scr_id', tr.scr_id)),
+ ' (', tag('a', {href: '#', onclick:scrDel}, mt('_vnedit_scr_remove')), ')',
+ tag('br', null),
+ mt('_vnedit_scr_fullsize', dim),
+ tag('br', null),
+ tag('br', null),
+ tag('input', {type:'checkbox', onclick:scrSerialize, id:nsfwid, name:nsfwid, checked: tr.scr_nsfw>0, 'class':'scr_nsfw'}),
+ tag('label', {'for':nsfwid}, mt('_vnedit_scr_nsfw')),
+ tag('br', null),
+ rel
+ );
+ }
+ scrSerialize();
+ ivInit();
+ clearTimeout(ti);
+ setTimeout(scrCheckStatus, 1000);
+ });
+}
+
+function scrDel(what) {
+ var tr = what && what.scr_status != null ? what : this;
+ while(tr.nodeName.toLowerCase() != 'tr')
+ tr = tr.parentNode;
+ tr.scr_status = 3;
+ byId('scr_table').removeChild(tr);
+ scrSerialize();
+ scrLast();
+ return false;
+}
+
+function scrUpload() {
+ scrUplNr++;
+
+ // create temporary form
+ var ifid = 'scr_upl_'+scrUplNr;
+ var frm = tag('form', {method: 'post', action:'/xml/screenshots.xml', target: ifid, enctype:'multipart/form-data'});
+ var ifr = tag('iframe', {id:ifid, name:ifid, src:'about:blank', onload:scrUploadComplete});
+ addBody(tag('div', {'class':'scr_uploader'}, ifr, frm));
+
+ // submit form and delete it
+ frm.appendChild(byId('scr_upload'));
+ frm.submit();
+ frm.parentNode.removeChild(frm);
+ ifr.scr_tr = scrAdd(0, 0, 0);
+ scrLast();
+ return false;
+}
+
+function scrUploadComplete() {
+ var ifr = this;
+ var fr = window.frames[ifr.id];
+ if(fr.location.href.indexOf('screenshots') < 0)
+ return;
+
+ var tr = ifr.scr_tr;
+ if(!tr || tr.scr_status == 3)
+ return;
+
+ try {
+ tr.scr_id = fr.window.document.getElementsByTagName('image')[0].getAttribute('id');
+ } catch(e) {
+ tr.scr_id = -10;
+ }
+ if(tr.scr_id < 0) {
+ alert(mt(tr.scr_id == -10 ? '_vnedit_scr_oops' : tr.scr_id == -1 ? '_vnedit_scr_errformat' : '_vnedit_scr_errempty'));
+ return scrDel(tr);
+ }
+
+ tr.id = 'scr_tr_'+tr.scr_id;
+ tr.scr_status = 2;
+ setContent(byName(tr, 'td')[1],
+ tag('b', mt('_vnedit_scr_genthumb')),
+ tag('br', null),
+ mt('_vnedit_scr_genthumb_msg')
+ );
+
+ // remove the <div> in a timeout, otherwise some browsers think the page is still loading
+ setTimeout(function() { ifr.parentNode.parentNode.removeChild(ifr.parentNode) }, 100);
+}
+
+function scrSerialize() {
+ var r = [];
+ var l = byName(byId('scr_table'), 'tr');
+ for(var i=0; i<l.length-1; i++)
+ if(l[i].scr_status == 0)
+ r[r.length] = [
+ l[i].scr_id,
+ byClass(l[i], 'input', 'scr_nsfw')[0].checked ? 1 : 0,
+ scrRel[byClass(l[i], 'select', 'scr_relsel')[0].selectedIndex][0]
+ ].join(',');
+ byId('screenshots').value = r.join(' ');
+}
+
+if(byId('jt_box_vn_scr'))
+ scrLoad();
+
+
+
+
+/* V I S U A L N O V E L T A G L I N K I N G (/v+/tagmod) */
+
+var tglSpoilers = [];
+
+function tglLoad() {
+ for(var i=0; i<=3; i++)
+ tglSpoilers[i] = mt('_tagv_spoil'+i);
+
+ // tag dropdown search
+ dsInit(byId('tagmod_tag'), '/xml/tags.xml?q=', function(item, tr) {
+ tr.appendChild(tag('td',
+ shorten(item.firstChild.nodeValue, 40),
+ item.getAttribute('meta') == 'yes' ? tag('b', {'class':'grayedout'}, ' '+mt('_js_ds_tag_meta')) :
+ item.getAttribute('state') == 0 ? tag('b', {'class':'grayedout'}, ' '+mt('_js_ds_tag_mod')) : null
+ ));
+ }, function(item) {
+ return item.firstChild.nodeValue;
+ }, tglAdd);
+ byId('tagmod_add').onclick = tglAdd;
+
+ // JS'ify the voting bar and spoiler setting
+ tglStripe();
+ var trs = byName(byId('tagtable'), 'tr');
+ for(var i=0; i<trs.length; i++) {
+ var vote = byClass(trs[i], 'td', 'tc_myvote')[0];
+ vote.tgl_vote = getText(vote)*1;
+ tglVoteBar(vote);
+
+ var spoil = byClass(trs[i], 'td', 'tc_myspoil')[0];
+ spoil.tgl_spoil = getText(spoil)*1+1;
+ setText(spoil, tglSpoilers[spoil.tgl_spoil]);
+ ddInit(spoil, 'tagmod', tglSpoilDD);
+ spoil.onclick = tglSpoilNext;
+ }
+ tglSerialize();
+}
+
+function tglSpoilNext() {
+ if(++this.tgl_spoil >= tglSpoilers.length)
+ this.tgl_spoil = 0;
+ setText(this, tglSpoilers[this.tgl_spoil]);
+ tglSerialize();
+ ddRefresh();
+}
+
+function tglSpoilDD(lnk) {
+ var lst = tag('ul', null);
+ for(var i=0; i<tglSpoilers.length; i++)
+ lst.appendChild(tag('li', i == lnk.tgl_spoil
+ ? tag('i', tglSpoilers[i])
+ : tag('a', {href: '#', onclick:tglSpoilSet, tgl_td:lnk, tgl_sp:i}, tglSpoilers[i])
+ ));
+ return lst;
+}
+
+function tglSpoilSet() {
+ this.tgl_td.tgl_spoil = this.tgl_sp;
+ setText(this.tgl_td, tglSpoilers[this.tgl_sp]);
+ ddHide();
+ tglSerialize();
+ return false;
+}
+
+function tglVoteBar(td, vote) {
+ setText(td, '');
+ for(var i=-3; i<=3; i++)
+ td.appendChild(tag('a', {
+ 'class':'taglvl taglvl'+i, tgl_num: i,
+ onmouseover:tglVoteBarSel, onmouseout:tglVoteBarSel, onclick:tglVoteBarSel
+ }, ' '));
+ tglVoteBarSel(td, td.tgl_vote);
+ return false;
+}
+
+function tglVoteBarSel(td, vote) {
+ // nasty trick to make this function multifunctional
+ if(this && this.tgl_num != null) {
+ var e = td || window.event;
+ td = this.parentNode;
+ vote = this.tgl_num;
+ if(e.type.toLowerCase() == 'click') {
+ td.tgl_vote = vote;
+ tglSerialize();
+ }
+ if(e.type.toLowerCase() == 'mouseout')
+ vote = td.tgl_vote;
+ }
+ var l = byName(td, 'a');
+ var num;
+ for(var i=0; i<l.length; i++) {
+ num = l[i].tgl_num;
+ if(num == 0)
+ setText(l[i], vote || '-');
+ else
+ setClass(l[i], 'taglvlsel', num<0&&vote<=num || num>0&&vote>=num);
+ }
+}
+
+function tglAdd() {
+ var tg = byId('tagmod_tag');
+ var add = byId('tagmod_add');
+ tag.disabled = add.disabled = true;
+ add.value = mt('_js_loading');
+
+ ajax('/xml/tags.xml?q=name:'+encodeURIComponent(tg.value), function(hr) {
+ tg.disabled = add.disabled = false;
+ tg.value = '';
+ add.value = mt('_tagv_add');
+
+ var items = hr.responseXML.getElementsByTagName('item');
+ if(items.length < 1)
+ return alert(mt('_tagv_notfound'));
+ if(items[0].getAttribute('meta') == 'yes')
+ return alert(mt('_tagv_nometa'));
+
+ var name = items[0].firstChild.nodeValue;
+ var id = items[0].getAttribute('id');
+ if(byId('tgl_'+id))
+ return alert(mt('_tagv_double'));
+
+ var vote = tag('td', {'class':'tc_myvote', tgl_vote: 2}, '');
+ tglVoteBar(vote);
+ var spoil = tag('td', {'class':'tc_myspoil', tgl_spoil: 0}, tglSpoilers[0]);
+ ddInit(spoil, 'tagmod', tglSpoilDD);
+ spoil.onclick = tglSpoilNext;
+
+ byId('tagtable').appendChild(tag('tr', {id:'tgl_'+id},
+ tag('td', {'class':'tc_tagname'}, tag('a', {href:'/g'+id}, name)),
+ vote, spoil,
+ tag('td', {'class':'tc_allvote'}, '-'),
+ tag('td', {'class':'tc_allspoil'}, '-')
+ ));
+ tglStripe();
+ tglSerialize();
+ });
+}
+
+function tglStripe() {
+ var l = byName(byId('tagtable'), 'tr');
+ for(var i=0; i<l.length; i++)
+ setClass(l[i], 'odd', i%2);
+}
+
+function tglSerialize() {
+ var r = [];
+ var l = byName(byId('tagtable'), 'tr');
+ for(var i=0; i<l.length; i++) {
+ var vote = byClass(l[i], 'td', 'tc_myvote')[0].tgl_vote;
+ if(vote != 0)
+ r[r.length] = [
+ l[i].id.substr(4),
+ vote,
+ byClass(l[i], 'td', 'tc_myspoil')[0].tgl_spoil-1
+ ].join(',');
+ }
+ byId('taglinks').value = r.join(' ');
+}
+
+if(byId('taglinks'))
+ tglLoad();
+
+
+
+
+/* R E L E A S E -> V I S U A L N O V E L L I N K I N G (/r+/edit) */
+
+function rvnLoad() {
+ var vns = byId('vn').value.split('|||');
+ for(var i=0; i<vns.length && vns[i].length>1; i++)
+ rvnAdd(vns[i].split(',',2)[0], vns[i].split(',',2)[1]);
+ rvnEmpty();
+
+ dsInit(byId('vn_input'), '/xml/vn.xml?q=',
+ function(item, tr) {
+ tr.appendChild(tag('td', {style:'text-align: right; padding-right: 5px'}, 'v'+item.getAttribute('id')));
+ tr.appendChild(tag('td', shorten(item.firstChild.nodeValue, 40)));
+ }, function(item) {
+ return 'v'+item.getAttribute('id')+':'+item.firstChild.nodeValue;
+ },
+ rvnFormAdd
+ );
+ byId('vn_add').onclick = rvnFormAdd;
+}
+
+function rvnAdd(id, title) {
+ byId('vn_tbl').appendChild(tag('tr', {id:'rvn_'+id, rvn_id:id},
+ tag('td', {'class':'tc_title'}, 'v'+id+':', tag('a', {href:'/v'+id}, shorten(title, 40))),
+ tag('td', {'class':'tc_rm'}, tag('a', {href:'#', onclick:rvnDel}, mt('_redit_form_vn_remove')))
+ ));
+ rvnStripe();
+ rvnEmpty();
+}
+
+function rvnDel() {
+ var tr = this;
+ while(tr.nodeName.toLowerCase() != 'tr')
+ tr = tr.parentNode;
+ tr.parentNode.removeChild(tr);
+ rvnEmpty();
+ rvnSerialize();
+ rvnStripe();
+ return false;
+}
+
+function rvnEmpty() {
+ var tbl = byId('vn_tbl');
+ if(byName(tbl, 'tr').length < 1)
+ tbl.appendChild(tag('tr', {id:'rvn_tr_none'}, tag('td', {colspan:2}, mt('_redit_form_vn_none'))));
+ else if(byId('rvn_tr_none'))
+ tbl.removeChild(byId('rvn_tr_none'));
+}
+
+function rvnStripe() {
+ var l = byName(byId('vn_tbl'), 'tr');
+ for(var i=0; i<l.length; i++)
+ setClass(l[i], 'odd', i%2);
+}
+
+function rvnFormAdd() {
+ var txt = byId('vn_input');
+ var lnk = byId('vn_add');
+ var val = txt.value;
+
+ if(!val.match(/^v[0-9]+/)) {
+ alert(mt('_redit_form_vn_vnformat'));
+ return false;
+ }
+
+ txt.disabled = true;
+ txt.value = mt('_js_loading');
+ setText(lnk, mt('_js_loading'));
+
+ ajax('/xml/vn.xml?q='+encodeURIComponent(val), function(hr) {
+ txt.disabled = false;
+ txt.value = '';
+ setText(lnk, mt('_redit_form_vn_addbut'));
+
+ var items = hr.responseXML.getElementsByTagName('item');
+ if(items.length < 1)
+ return alert(mt('_redit_form_vn_notfound'));
+
+ var id = items[0].getAttribute('id');
+ if(byId('rvn_'+id))
+ return alert(mt('_redit_form_vn_double'));
+
+ rvnAdd(id, items[0].firstChild.nodeValue);
+ rvnSerialize();
+ });
+ return false;
+}
+
+function rvnSerialize() {
+ var r = [];
+ var l = byName(byId('vn_tbl'), 'tr');
+ for(var i=0; i<l.length; i++)
+ if(l[i].rvn_id)
+ r[r.length] = l[i].rvn_id + ',' + getText(byName(byClass(l[i], 'td', 'tc_title')[0], 'a')[0]);
+ byId('vn').value = r.join('|||');
+}
+
+if(byId('jt_box_rel_vn'))
+ rvnLoad();
+
+
+
+
+/* R E L E A S E -> P R O D U C E R L I N K I N G (/r+/edit) */
+
+function rprLoad() {
+ var ps = byId('producers').value.split('|||');
+ for(var i=0; i<ps.length && ps[i].length>1; i++) {
+ var val = ps[i].split(',',3);
+ rprAdd(val[0], val[1], val[2]);
+ }
+ rprEmpty();
+
+ dsInit(byId('producer_input'), '/xml/producers.xml?q=',
+ function(item, tr) {
+ tr.appendChild(tag('td', {style:'text-align: right; padding-right: 5px'}, 'p'+item.getAttribute('id')));
+ tr.appendChild(tag('td', shorten(item.firstChild.nodeValue, 40)));
+ }, function(item) {
+ return 'p'+item.getAttribute('id')+':'+item.firstChild.nodeValue;
+ },
+ rprFormAdd
+ );
+ byId('producer_add').onclick = rprFormAdd;
+}
+
+function rprAdd(id, role, name) {
+ var roles = byId('producer_role').options;
+ var rl = tag('select', {onchange:rprSerialize});
+ for(var i=0; i<roles.length; i++)
+ rl.appendChild(tag('option', {value: roles[i].value, selected:role==roles[i].value}, getText(roles[i])));
+
+ byId('producer_tbl').appendChild(tag('tr', {id:'rpr_'+id, rpr_id:id},
+ tag('td', {'class':'tc_name'}, 'p'+id+':', tag('a', {href:'/p'+id}, shorten(name, 40))),
+ tag('td', {'class':'tc_role'}, rl),
+ tag('td', {'class':'tc_rm'}, tag('a', {href:'#', onclick:rprDel}, mt('_redit_form_prod_remove')))
+ ));
+ rprEmpty();
+}
+
+function rprDel() {
+ var tr = this;
+ while(tr.nodeName.toLowerCase() != 'tr')
+ tr = tr.parentNode;
+ tr.parentNode.removeChild(tr);
+ rprEmpty();
+ rprSerialize();
+ return false;
+}
+
+function rprEmpty() {
+ var tbl = byId('producer_tbl');
+ if(byName(tbl, 'tr').length < 1)
+ tbl.appendChild(tag('tr', {id:'rpr_tr_none'}, tag('td', {colspan:2}, mt('_redit_form_prod_none'))));
+ else if(byId('rpr_tr_none'))
+ tbl.removeChild(byId('rpr_tr_none'));
+}
+
+function rprFormAdd() {
+ var txt = byId('producer_input');
+ var lnk = byId('producer_add');
+ var val = txt.value;
+
+ if(!val.match(/^p[0-9]+/)) {
+ alert(mt('_redit_form_prod_pformat'));
+ return false;
+ }
+
+ txt.disabled = true;
+ txt.value = mt('_js_loading');
+ setText(lnk, mt('_js_loading'));
+
+ ajax('/xml/producers.xml?q='+encodeURIComponent(val), function(hr) {
+ txt.disabled = false;
+ txt.value = '';
+ setText(lnk, mt('_redit_form_prod_addbut'));
+
+ var items = hr.responseXML.getElementsByTagName('item');
+ if(items.length < 1)
+ return alert(mt('_redit_form_prod_notfound'));
+
+ var id = items[0].getAttribute('id');
+ if(byId('rpr_'+id))
+ return alert(mt('_redit_form_prod_double'));
+
+ var role = byId('producer_role');
+ role = role[role.selectedIndex].value;
+
+ rprAdd(id, role, items[0].firstChild.nodeValue);
+ rprSerialize();
+ });
+ return false;
+}
+
+function rprSerialize() {
+ var r = [];
+ var l = byName(byId('producer_tbl'), 'tr');
+ for(var i=0; i<l.length; i++)
+ if(l[i].rpr_id) {
+ var role = byName(byClass(l[i], 'td', 'tc_role')[0], 'select')[0];
+ r[r.length] = [
+ l[i].rpr_id,
+ role.options[role.selectedIndex].value,
+ getText(byName(byClass(l[i], 'td', 'tc_name')[0], 'a')[0])
+ ].join(',');
+ }
+ byId('producers').value = r.join('|||');
+}
+
+if(byId('jt_box_rel_prod'))
+ rprLoad();
+
+
+
+
+/* P R O D U C E R R E L A T I O N S (/p+/edit) */
+
+function prrLoad() {
+ // read the current relations
+ var rels = byId('prodrelations').value.split('|||');
+ for(var i=0; i<rels.length && rels[0].length>1; i++) {
+ var rel = rels[i].split(',', 3);
+ prrAdd(rel[0], rel[1], rel[2]);
+ }
+ prrEmpty();
+
+ // bind the add-link
+ byName(byClass(byId('relation_new'), 'td', 'tc_add')[0], 'a')[0].onclick = prrFormAdd;
+
+ // dropdown
+ dsInit(byName(byClass(byId('relation_new'), 'td', 'tc_prod')[0], 'input')[0], '/xml/producers.xml?q=', function(item, tr) {
+ tr.appendChild(tag('td', { style: 'text-align: right; padding-right: 5px'}, 'p'+item.getAttribute('id')));
+ tr.appendChild(tag('td', shorten(item.firstChild.nodeValue, 40)));
+ }, function(item) {
+ return 'p'+item.getAttribute('id')+':'+item.firstChild.nodeValue;
+ }, prrFormAdd);
+}
+
+function prrAdd(rel, pid, title) {
+ var sel = tag('select', {onchange: prrSerialize});
+ var ops = byName(byClass(byId('relation_new'), 'td', 'tc_rel')[0], 'select')[0].options;
+ for(var i=0; i<ops.length; i++)
+ sel.appendChild(tag('option', {value: ops[i].value, selected: ops[i].value==rel}, getText(ops[i])));
+
+ byId('relation_tbl').appendChild(tag('tr', {id:'relation_tr_'+pid},
+ tag('td', {'class':'tc_prod' }, 'p'+pid+':', tag('a', {href:'/p'+pid}, shorten(title, 40))),
+ tag('td', {'class':'tc_rel' }, sel),
+ tag('td', {'class':'tc_add' }, tag('a', {href:'#', onclick:prrDel}, mt('_pedit_rel_del')))
+ ));
+
+ prrEmpty();
+}
+
+function prrEmpty() {
+ var tbl = byId('relation_tbl');
+ if(byName(tbl, 'tr').length < 1)
+ tbl.appendChild(tag('tr', {id:'relation_tr_none'}, tag('td', {colspan:4}, mt('_pedit_rel_none'))));
+ else if(byId('relation_tr_none'))
+ tbl.removeChild(byId('relation_tr_none'));
+}
+
+function prrSerialize() {
+ var r = [];
+ var trs = byName(byId('relation_tbl'), 'tr');
+ for(var i=0; i<trs.length; i++) {
+ if(trs[i].id == 'relation_tr_none')
+ continue;
+ var rel = byName(byClass(trs[i], 'td', 'tc_rel')[0], 'select')[0];
+ r[r.length] = [
+ rel.options[rel.selectedIndex].value,
+ trs[i].id.substr(12),
+ getText(byName(byClass(trs[i], 'td', 'tc_prod')[0], 'a')[0])
+ ].join(',');
+ }
+ byId('prodrelations').value = r.join('|||');
+}
+
+function prrDel() {
+ var tr = this;
+ while(tr.nodeName.toLowerCase() != 'tr')
+ tr = tr.parentNode;
+ byId('relation_tbl').removeChild(tr);
+ prrSerialize();
+ prrEmpty();
+ return false;
+}
+
+function prrFormAdd() {
+ var relnew = byId('relation_new');
+ var txt = byName(byClass(relnew, 'td', 'tc_prod')[0], 'input')[0];
+ var sel = byName(byClass(relnew, 'td', 'tc_rel')[0], 'select')[0];
+ var lnk = byName(byClass(relnew, 'td', 'tc_add')[0], 'a')[0];
+ var input = txt.value;
+
+ if(!input.match(/^p[0-9]+/)) {
+ alert('_pedit_rel_findformat');
+ return false;
+ }
+
+ txt.disabled = sel.disabled = true;
+ txt.value = mt('_js_loading');
+ setText(lnk, mt('_js_loading'));
+
+ ajax('/xml/producers.xml?q='+encodeURIComponent(input), function(hr) {
+ txt.disabled = sel.disabled = false;
+ txt.value = '';
+ setText(lnk, mt('_pedit_rel_addbut'));
+
+ var items = hr.responseXML.getElementsByTagName('item');
+ if(items.length < 1)
+ return alert(mt('_pedit_rel_notfound'));
+
+ var id = items[0].getAttribute('id');
+ if(byId('relation_tr_'+id))
+ return alert(mt('_pedit_rel_double'));
+
+ prrAdd(sel.options[sel.selectedIndex].value, id, items[0].firstChild.nodeValue);
+ sel.selectedIndex = 0;
+ prrSerialize();
+ });
+ return false;
+}
+
+if(byId('prodrelations'))
+ prrLoad();
+
+
+
+
+/* M I S C S T U F F */
+
+// search box
+{
+ var i = byId('sq');
+ i.onfocus = function () {
+ if(this.value == mt('_menu_emptysearch')) {
+ this.value = '';
+ this.style.fontStyle = 'normal'
+ }
+ };
+ i.onblur = function () {
+ if(this.value.length < 1) {
+ this.value = mt('_menu_emptysearch');
+ this.style.fontStyle = 'italic'
+ }
+ };
+}
+
+// VN Voting (/v+)
+if(byId('votesel')) {
+ byId('votesel').onchange = function() {
+ var s = this.options[this.selectedIndex].value;
+ if(s == 1 && !confirm(mt('_vnpage_uopt_1vote')))
+ return;
+ if(s == 10 && !confirm(mt('_vnpage_uopt_10vote')))
+ return;
+ if(s)
+ location.href = location.href.replace(/\.[0-9]+/, '')+'/vote?v='+s;
+ };
+}
+
+// Advanced search (/v/*, /r)
+if(byId('advselect')) {
+ byId('advselect').onclick = function() {
+ var box = byId('advoptions');
+ var hidden = !hasClass(box, 'hidden');
+ setClass(box, 'hidden', hidden);
+ setText(byName(this, 'i')[0], hidden ? collapsed_icon : expanded_icon);
+ return false;
+ };
+}
+
+// Spoiler filters -> cookie (/v/*)
+if(byId('sp_0')) {
+ byId('sp_0').onclick = function() { setCookie('tagspoil', 0) };
+ byId('sp_1').onclick = function() { setCookie('tagspoil', 1) };
+ byId('sp_2').onclick = function() { setCookie('tagspoil', 2) };
+ var spoil = getCookie('tagspoil');
+ byId('sp_'+(spoil == null ? 0 : spoil)).checked = true;
+}
+
+// NSFW VN image toggle (/v+)
+if(byId('nsfw_show')) {
+ var msg = byId('nsfw_show');
+ var img = byId('nsfw_hid');
+ byName(msg, 'a')[0].onclick = function() {
+ msg.style.display = 'none';
+ img.style.display = 'block';
+ return false;
+ };
+ img.onclick = function() {
+ msg.style.display = 'block';
+ img.style.display = 'none';
+ };
+}
+
+// NSFW toggle for screenshots (/v+)
+if(byId('nsfwhide')) {
+ byId('nsfwhide').onclick = function() {
+ var shown = 0;
+ var l = byName(byId('screenshots'), 'div');
+ for(var i=0; i<l.length; i++) {
+ if(hasClass(l[i], 'nsfw')) {
+ var hidden = !hasClass(l[i], 'hidden');
+ setClass(l[i], 'hidden', hidden);
+ setClass(byName(l[i], 'a')[0], 'hidden', hidden); // for the image viewer
+ if(!hidden)
+ shown++;
+ } else
+ shown++;
+ }
+ setText(byId('nsfwshown'), shown);
+ return false;
+ };
+}
+
+// VN Wishlist dropdown box (/v+)
+if(byId('wishsel')) {
+ byId('wishsel').onchange = function() {
+ if(this.selectedIndex != 0)
+ location.href = location.href.replace(/\.[0-9]+/, '')
+ +'/wish?s='+this.options[this.selectedIndex].value;
+ };
+}
+
+// Release list dropdown box (/r+)
+if(byId('listsel')) {
+ byId('listsel').onchange = function() {
+ if(this.selectedIndex != 0)
+ location.href = location.href.replace(/\.[0-9]+/, '')
+ +'/list?e='+this.options[this.selectedIndex].value;
+ };
+}
+
+// BBCode spoiler tags
+{
+ var l = byClass('b', 'spoiler');
+ for(var i=0; i<l.length; i++) {
+ l[i].onmouseover = function() { setClass(this, 'spoiler', false); setClass(this, 'spoiler_shown', true) };
+ l[i].onmouseout = function() { setClass(this, 'spoiler', true); setClass(this, 'spoiler_shown', false) };
+ }
+}
+
+// vndb.org domain check
+// (let's just keep this untranslatable, nobody cares anyway ^^)
+if(location.hostname != 'vndb.org') {
+ addBody(tag('div', {id:'debug'},
+ tag('h2', 'This is not VNDB!'),
+ 'The real VNDB is ',
+ tag('a', {href:'http://vndb.org/'}, 'here'),
+ '.'
+ ));
+}
+
+// make some fields readonly when patch flag is set (/r+/edit)
+if(byId('jt_box_rel_geninfo')) {
+ var func = function() {
+ byId('doujin').disabled =
+ byId('resolution').disabled =
+ byId('voiced').disabled =
+ byId('ani_story').disabled =
+ byId('ani_ero').disabled =
+ byId('patch').checked;
+ };
+ func();
+ byId('patch').onclick = func;
+}
+
+// Batch edit wishlist dropdown box (/u+/wish)
+if(byId('batchedit')) {
+ byId('batchedit').onchange = function() {
+ if(this.selectedIndex == 0)
+ return true;
+ var frm = this;
+ while(frm.nodeName.toLowerCase() != 'form')
+ frm = frm.parentNode;
+ frm.submit();
+ };
+}
+
+// expand/collapse listings (/*/hist, /u+/posts)
+if(byId('expandlist')) {
+ var lnk = byId('expandlist');
+ setexpand = function() {
+ var exp = getCookie('histexpand') == 1;
+ setText(lnk, mt(exp ? '_js_collapse' : '_js_expand'));
+ var tbl = lnk;
+ while(tbl.nodeName.toLowerCase() != 'table')
+ tbl = tbl.parentNode;
+ var l = byClass(tbl, 'tr', 'collapse');
+ for(var i=0; i<l.length; i++)
+ setClass(l[i], 'hidden', !exp);
+ };
+ setexpand();
+ lnk.onclick = function () {
+ setCookie('histexpand', getCookie('histexpand') == 1 ? 0 : 1);
+ setexpand();
+ return false;
+ };
+}
+
+// collapse/expand row groups (/u+/tags, /u+/list) (limited to one table on a page)
+if(byId('expandall')) {
+ var table = byId('expandall');
+ while(table.nodeName.toLowerCase() != 'table')
+ table = table.parentNode;
+ var heads = byClass(table, 'td', 'collapse_but');
+ var allhid = false;
+
+ var alltoggle = function() {
+ allhid = !allhid;
+ var l = byClass(table, 'tr', 'collapse');
+ for(var i=0; i<l.length; i++)
+ setClass(l[i], 'hidden', allhid);
+ setText(byName(byId('expandall'), 'i')[0], allhid ? collapsed_icon : expanded_icon);
+ for(var i=0; i<heads.length; i++)
+ setText(byName(heads[i], 'i')[0], allhid ? collapsed_icon : expanded_icon);
+ return false;
+ }
+ byId('expandall').onclick = alltoggle;
+ alltoggle();
+
+ var singletoggle = function() {
+ var l = byClass(table, 'tr', 'collapse_'+this.id);
+ if(l.length < 1)
+ return;
+ var hid = !hasClass(l[0], 'hidden');
+ for(var i=0; i<l.length; i++)
+ setClass(l[i], 'hidden', hid);
+ setText(byName(this, 'i')[0], hid ? collapsed_icon : expanded_icon);
+ };
+ for(var i=0; i<heads.length; i++)
+ heads[i].onclick = singletoggle;
+}
+
+// auto-complete tag search (/v/*)
+if(byId('advselect') && byId('ti')) {
+ var trfunc = function(item, tr) {
+ tr.appendChild(tag('td', shorten(item.firstChild.nodeValue, 40),
+ item.getAttribute('meta') == 'yes' ? tag('b', {'class': 'grayedout'}, ' '+mt('_js_ds_tag_meta')) : null,
+ item.getAttribute('state') == 0 ? tag('b', {'class': 'grayedout'}, ' '+mt('_js_ds_tag_mod')) : null
+ ));
+ };
+ var serfunc = function(item, obj) {
+ var tags = obj.value.split(/ *, */);
+ tags[tags.length-1] = item.firstChild.nodeValue;
+ return tags.join(', ');
+ };
+ var retfunc = function() { false; };
+ var parfunc = function(val) {
+ return (val.split(/, */))[val.split(/, */).length-1];
+ };
+ dsInit(byId('ti'), '/xml/tags.xml?q=', trfunc, serfunc, retfunc, parfunc);
+ dsInit(byId('te'), '/xml/tags.xml?q=', trfunc, serfunc, retfunc, parfunc);
+}
+
+// Language selector
+if(byId('lang_select')) {
+ var d = byId('lang_select');
+ ddInit(d, 'bottom', function(lnk) {
+ var lst = tag('ul', null);
+ for(var i=0; i<L10N_LANG.length; i++) {
+ var ln = L10N_LANG[i];
+ var icon = tag('acronym', {'class':'icons lang '+ln}, ' ');
+ lst.appendChild(tag('li', {'class':'lang_selector'}, mt_curlang == ln
+ ? tag('i', icon, mt('_lang_'+ln))
+ : tag('a', {href:'?l10n='+ln}, icon, L10N_STR['_lang_'+ln][ln]||mt('_lang_'+ln))
+ ));
+ }
+ return lst;
+ });
+ d.onclick = function() {return false};
+}
+
+// spam protection on all forms
+setTimeout(function() {
+ for(i=1; i<document.forms.length; i++)
+ document.forms[i].action = document.forms[i].action.replace(/\/nospam\?/,'');
+}, 500);
+
+
diff --git a/data/style.css b/data/style.css
index 53255c74..6bd0c547 100644
--- a/data/style.css
+++ b/data/style.css
@@ -239,7 +239,7 @@ td.field label {
p.center {
text-align: center;
}
-b.future, b.standout {
+b.future, b.standout, a.standout {
font-weight: normal;
color: $standout$;
}
@@ -272,7 +272,7 @@ b.future, b.standout {
font-size: 11px;
color: $maintext$;
}
-#menulist h2 span {
+#menulist h2 #lang_select {
float: right;
padding-top: 1px;
}
@@ -295,8 +295,8 @@ b.future, b.standout {
margin-left: 15px;
}
#menulist input.submit {
- width: 40px;
- margin-left: 45px;
+ width: 70px;
+ margin-left: 30px;
}
#menulist #search input.text {
width: 133px;
@@ -306,6 +306,7 @@ b.future, b.standout {
#menulist #search input.submit {
display: none;
}
+#dd_box acronym { margin: 2px 5px 2px 0!important; }
@@ -479,13 +480,13 @@ div.mainbox.history td.tc1_2 {
}
div.mainbox.history td.tc2 { width: 65px; }
div.mainbox.history td.tc3 { width: 90px }
-tr.editsum td {
+tr.msgsum td {
color: $grayedout$;
padding-top: 0;
text-align: right;
}
-a#history_comments { float: right }
-a#history_comments:hover { border-bottom: none }
+a#expandlist { float: right }
+a#expandlist:hover { border-bottom: none }
@@ -694,14 +695,16 @@ a.addnew {
margin: 0;
}
-#vldd { position: absolute; left: -500px; border: 1px solid $border$; background-color: $secbg$; width: 180px; }
-#vldd ul { float: left; width: 90px; list-style-type: none; margin: 0; padding: 0 }
-#vldd li b { display: block; font-weight: normal; padding-left: 5px; }
-#vldd li i { display: block; font-style: normal; padding-left: 10px; }
-#vldd li a { display: block; padding-left: 10px; color: $link$; border: 0; }
-#vldd li a:hover { background: url($_boxbg$) repeat }
-#vldd ul.full { width: 180px; text-align: center; }
-#vldd ul.full li a { padding: 0 }
+#dd_box { position: absolute; left: -500px; border: 1px solid $border$; background-color: $secbg$; }
+#dd_box ul { list-style-type: none; margin: 0; padding: 0 }
+#dd_box li b { display: block; font-weight: normal; padding-left: 5px; }
+#dd_box li i { display: block; font-style: normal; padding-left: 10px; padding-right: 5px }
+#dd_box li a { display: block; padding-left: 10px; color: $link$; border: 0; padding-right: 5px }
+#dd_box li a:hover { background: url($_boxbg$) repeat }
+#dd_box .vrdd { width: 180px; }
+#dd_box .vrdd ul { float: left; width: 90px; }
+#dd_box .vrdd ul.full { width: 180px; text-align: center; }
+#dd_box .vrdd ul.full li a { padding: 0 }
@@ -727,12 +730,12 @@ a.addnew {
#jt_box_vn_rel table { margin-bottom: 10px; }
#jt_box_vn_rel h2 { margin: 0 0 3px 0px; }
#jt_box_vn_rel td { padding: 1px 2px; vertical-align: middle; }
-#jt_box_vn_rel td.tc1 { width: 300px; text-align: right }
-#jt_box_vn_rel td.tc2 { width: 170px; white-space: nowrap }
-#jt_box_vn_rel td.tc3 { width: 200px; }
-#jt_box_vn_rel td.tc4 { width: 40px; text-align: right }
-#jt_box_vn_rel td.tc1 input { width: 280px; }
-#jt_box_vn_rel td.tc2 select { width: 130px; }
+#jt_box_vn_rel td.tc_vn { width: 300px; text-align: right }
+#jt_box_vn_rel td.tc_rel { width: 170px; white-space: nowrap }
+#jt_box_vn_rel td.tc_title { width: 200px; }
+#jt_box_vn_rel td.tc_add { width: 40px; text-align: right }
+#jt_box_vn_rel td.tc_vn input { width: 280px; }
+#jt_box_vn_rel td.tc_rel select { width: 130px; }
#ds_box {
border: 1px solid $border$;
@@ -763,6 +766,7 @@ a.addnew {
#scr_table td { height: 108px; border-top: 1px solid #258; padding: 0; padding-right: 5px }
#scr_table td.thumb { width: 136px; vertical-align: middle }
#scr_table select { width: 400px; }
+div.scr_uploader { visibility: hidden; overflow: hidden; width: 1px; height: 1px; position: absolute; left: -500px; top: -500px; }
@@ -840,6 +844,16 @@ a.addnew {
padding-bottom: 10px!important
}
+/***** Producer edit *****/
+
+#jt_box_pedit_rel table { margin-bottom: 10px; }
+#jt_box_pedit_rel h2 { margin: 0 0 3px 0px; }
+#jt_box_pedit_rel td { padding: 1px 2px; vertical-align: middle; }
+#jt_box_pedit_rel td.tc_prod { width: 290px; padding-left: 10px }
+#jt_box_pedit_rel td.tc_add { width: 40px; text-align: left }
+#jt_box_pedit_rel td.tc_prod input { width: 280px; }
+#jt_box_pedit_rel td.tc_rel select { width: 130px; }
+
@@ -863,13 +877,12 @@ a.addnew {
#media_div { padding-left: 20px; }
#media_div span { display: block }
-#jt_box_rel_vn h2, #jt_box_rel_prod h2 { clear: left; padding-top: 10px; }
-#jt_box_rel_vn div, #jt_box_rel_prod div { padding-left: 20px }
-#jt_box_rel_vn input, #jt_box_rel_prod input { margin-right: 10px; width: 300px }
-#jt_box_rel_vn span, #jt_box_rel_prod span { clear: left; display: block; padding: 2px; }
-#jt_box_rel_vn span.odd, #jt_box_rel_prod span.odd { background: url($_boxbg$) repeat; }
-#jt_box_rel_vn i, #jt_box_rel_prod i { font-style: normal; display: block; float: left; width: 310px }
-#jt_box_rel_vn b, #jt_box_rel_prod b { font-weight: normal; }
+#jt_box_rel_vn h2, #jt_box_rel_prod h2 { clear: left; padding-top: 10px; }
+#jt_box_rel_vn div, #jt_box_rel_vn table,
+#jt_box_rel_prod div, #jt_box_rel_prod table { margin-left: 20px }
+#jt_box_rel_vn input, #jt_box_rel_prod input { margin-right: 10px; width: 295px }
+#jt_box_rel_vn .tc_title, #jt_box_rel_prod .tc_name { width: 310px; padding: 2px }
+#jt_box_rel_prod .tc_role select { width: 100px; margin-right: 10px; }
@@ -905,8 +918,8 @@ a.addnew {
/***** User VN list browser ******/
-.relhid_but, #relhidall { cursor: pointer }
-.relhid_but i, #relhidall i { font-style: normal }
+#expandall, .collapse_but { cursor: pointer }
+#expandall i, .collapse_but i { font-style: normal }
.browse.rlist .tc2 { width: 100px; }
.browse.rlist .tc3 { width: 70px; }
.browse.rlist .relhid .tc1 { padding-left: 40px; width: 70px; }
@@ -971,15 +984,15 @@ div.browse.uposts td.tc1 {
/***** VN tagmod *****/
#jt_box_tagmod .formtable table td { padding: 1px 5px }
-#tagtable tfoot td { padding-top: 20px!important; }
-#tagtable .tc2_1 { border-right: 1px solid $border$; border-left: 1px solid $border$; width: 150px; text-align: center }
-#tagtable .tc3_1 { border-left: 1px solid $border$; width: 150px; text-align: center }
-#tagtable .tc1 { min-width: 200px; border-right: 1px solid $border$ }
-#tagtable .tc2 { padding-left: 30px!important }
-#tagtable .tc3 { border-right: 1px solid $border$; padding-right: 30px!important; text-align: right; padding-left: 10px!important }
-#tagtable .tc4 { padding-left: 30px!important; }
-#tagtable .tc4 i { font-style: normal; font-size: 8px }
-#tagtable .tc5 { text-align: right; padding-right: 30px!important; }
+table.tgl tfoot td { padding-top: 20px!important; }
+table.tgl .tc_you { border-right: 1px solid $border$; border-left: 1px solid $border$; width: 150px; text-align: center }
+table.tgl .tc_others { border-left: 1px solid $border$; width: 150px; text-align: center }
+table.tgl .tc_tagname { min-width: 200px; border-right: 1px solid $border$ }
+table.tgl .tc_myvote { padding-left: 30px!important }
+table.tgl .tc_myspoil { border-right: 1px solid $border$; padding-right: 30px!important; text-align: right; padding-left: 10px!important; cursor: pointer }
+table.tgl .tc_allvote { padding-left: 30px!important; }
+table.tgl .tc_allvote i { font-style: normal; font-size: 8px }
+table.tgl .tc_allspoil { text-align: right; padding-right: 30px!important; }
.taglvl { display: block; float: left; width: 8px; height: 12px; border: 1px solid $border$; font-size: 1px; color: $maintext$!important }
.taglvl0 { width: 15px; border: none!important; font-size: 10px; text-align: center; }
div.taglvl0 { font-size: 8px; width: 20px!important }
@@ -991,9 +1004,6 @@ a.taglvl:hover { border-bottom: 1px solid transparent!important }
.taglvlsel.taglvl1 { background-color: #cf0; border-color: #cf0 }
.taglvlsel.taglvl2 { background-color: #8f0; border-color: #8f0 }
.taglvlsel.taglvl3 { background-color: #0f0; border-color: #0f0 }
-#jt_box_tagmod #tagtable .tc3 { padding: 0 }
-#tagtable .tc3 select { width: 90px; height: 15px; border: 0; margin: 0; font-size: 11px; background-color: $_blendbg$; text-align: right }
-#tagtable .odd .tc3 select { background-color: $secbg$ }
@@ -1131,7 +1141,7 @@ div#iv_view {
height: 11px;
opacity: 0.5;
}
-.icons.rt0, .icons.rt1, .icons.rt2 { width: 11px; }
+.icons.rtcomplete, .icons.rtpartial, .icons.rttrial { width: 11px; }
acronym.icons, acronym.uicons { cursor: default; }
a .icons { cursor: pointer }
.icons.oth { background: none; }
@@ -1153,9 +1163,9 @@ a .icons { cursor: pointer }
.icons.xb3 { background-position: -16px -84px; }
.icons.sat { background-position: -16px -98px; }
-.icons.rt0 { background-position: -32px 0px; }
-.icons.rt1 { background-position: -32px -14px; }
-.icons.rt2 { background-position: -32px -28px; }
+.icons.rtcomplete { background-position: -32px 0px; }
+.icons.rtpartial { background-position: -32px -14px; }
+.icons.rttrial { background-position: -32px -28px; }
.icons.ext { background-position: -32px -42px; }
.icons.msx { background-position: -32px -56px; }
.icons.nes { background-position: -32px -70px; }
@@ -1170,6 +1180,7 @@ a .icons { cursor: pointer }
.icons.fr { background-position: -48px -66px; }
.icons.it { background-position: -48px -77px; }
.icons.ja { background-position: -48px -88px; }
+.icons.hu { background-position: -48px -99px; }
.icons.nl { background-position: -61px 0px; }
.icons.no { background-position: -61px -11px; }
@@ -1182,3 +1193,11 @@ a .icons { cursor: pointer }
.icons.ko { background-position: -61px -88px; }
.icons.vi { background-position: -61px -99px; }
+
+/* Relation graph colors */
+svg .border { fill: none; stroke: $border$ }
+svg .edge polygon.border { fill: $border$ }
+svg .nodebg { fill: $tabbg$; stroke: $tabbg$ }
+svg text { fill: $maintext$ }
+svg .edge text { font: 8px "Tahoma" }
+
diff --git a/lib/LangFile.pm b/lib/LangFile.pm
new file mode 100644
index 00000000..e81f7f7a
--- /dev/null
+++ b/lib/LangFile.pm
@@ -0,0 +1,145 @@
+
+
+package LangFile;
+
+use strict;
+use warnings;
+use Fcntl qw(LOCK_SH LOCK_EX SEEK_SET);
+
+
+sub new {
+ my($class, $action, $file) = @_;
+ open my $F, $action eq 'read' ? '<:utf8' : '>:utf8', $file or die "Opening $file: $!";
+ flock($F, $action eq 'read' ? LOCK_SH : LOCK_EX) or die "Locking $file: $!";
+ seek($F, 0, SEEK_SET) or die "Seeking $file: $!";
+ return bless {
+ act => $action,
+ FH => $F,
+ # status vars for reading
+ intro => 1,
+ last => [],
+ }, $class;
+}
+
+
+sub read {
+ my $self = shift;
+ my $FH = $self->{FH};
+ my @lines;
+ my $state = '';
+ my($lang, $sync);
+
+ while((my $l = shift(@{$self->{last}}) || <$FH>)) {
+ $l =~ s/[\r\n\t\s]+$//;
+
+ # header
+ if($self->{intro}) {
+ push @lines, $l;
+ next if $l ne '/intro';
+ $self->{intro} = 0;
+ return [ 'space', @lines ];
+ }
+
+ # key
+ if(!$state && $l =~ /^:(.+)$/) {
+ return [ 'key', $1 ];
+ }
+
+ # space
+ if((!$state || $state eq 'space') && ($l =~ /^#/ || $l eq '')) {
+ $state = 'space';
+ push @lines, $l;
+ } elsif($state eq 'space') {
+ push @{$self->{last}}, "$l\n";
+ return [ 'space', @lines ];
+ }
+
+ # tl
+ if(!$state && $l =~ /^([a-z_-]{2})([ *]):(?: (.+)|)$/) {
+ $lang = $1;
+ $sync = $2 eq '*' ? 0 : 1;
+ push @lines, $3||'';
+ $state = 'tl';
+ } elsif($state eq 'tl' && $l =~ /^\s{5}(.+)$/) {
+ push @lines, $1;
+ } elsif($state eq 'tl' && $l eq '') {
+ push @lines, $l;
+ } elsif($state eq 'tl') {
+ my $trans = join "\n", @lines;
+ push @{$self->{last}}, "\n" while $trans =~ s/\n$//;
+ push @{$self->{last}}, $l;
+ return [ 'tl', $lang, $sync, $trans ];
+ }
+
+ die "Don\'t know what to do with \"$l\"" if !$state;
+ }
+ if($state eq 'space') {
+ return [ 'space', @lines ];
+ }
+ if($state eq 'tl') {
+ my $trans = join "\n", @lines;
+ push @{$self->{last}}, "\n" while $trans =~ s/\n$//;
+ return [ 'tl', $lang, $sync, $trans ];
+ }
+ return undef;
+}
+
+
+sub write {
+ my($self, @line) = @_;
+ my $FH = $self->{FH};
+
+ my $t = shift @line;
+
+ if($t eq 'space') {
+ print $FH "$_\n" for @line;
+ }
+
+ if($t eq 'key') {
+ print $FH ":$line[0]\n";
+ }
+
+ if($t eq 'tl') {
+ my($lang, $sync, $text) = @line;
+ $text =~ s/\n([^\n])/\n $1/g;
+ $text = " $text" if $text ne '';
+ printf $FH "%s%s:%s\n", $lang, $sync ? ' ' : '*', $text;
+ }
+}
+
+
+sub close {
+ my $self = shift;
+ close $self->{FH};
+}
+
+1;
+
+__END__
+=pod
+
+=head1 NAME
+
+LangFile - Simple object oriented interface for the parsing and creation of lang.txt
+
+=head1 USAGE
+
+ use LangFile;
+ my $read = LangFile->new(read => "data/lang.txt");
+ my $write = LangFile->new(write => "lang-copy.txt");
+
+ while((my $line = $read->read())) {
+ # $line is an arrayref in one of the following formats:
+ # [ 'space', @lines ]
+ # unparsed lines, like the header, newlines and comments
+ # [ 'key', $key ]
+ # key line, $key is key name
+ # [ 'tl', $lang, $sync, $text ]
+ # translation line(s), $lang = language tag, $sync = 1/0, $text = translation (can include newlines)
+ # $line is undef on EOF, $read->next() die()s on a parsing error
+
+ # create an identical copy of $read in $write
+ $write->write(@$line);
+ }
+ $write->close;
+
diff --git a/lib/Multi/Anime.pm b/lib/Multi/Anime.pm
index 03134925..df2f6424 100644
--- a/lib/Multi/Anime.pm
+++ b/lib/Multi/Anime.pm
@@ -70,6 +70,17 @@ sub spawn {
lm => 0, # timestamp of last outgoing message, 0=no running msg
aid => 0, # anime ID of the last sent ANIME command
tag => int(rand()*50000),
+ # anime types as returned by AniDB (lowercased)
+ anime_types => {
+ 'unknown' => undef, # NULL
+ 'tv series' => 'tv',
+ 'ova' => 'ova',
+ 'movie' => 'mov',
+ 'other' => 'oth',
+ 'web' => 'web',
+ 'tv special' => 'spe',
+ 'music video' => 'mv',
+ },
},
);
}
@@ -224,7 +235,7 @@ sub receivepacket { # input, wheelid
$col[2] = undef if !$col[2] || $col[2] =~ /^0,/;
$col[3] = $1 if $col[3] =~ /^([0-9]+)/; # remove multi-year stuff
$col[3] = undef if !$col[3];
- $col[4] = (grep lc($VNDB::S{anime_types}[$_]) eq lc($col[4]), 0..$#{$VNDB::S{anime_types}})[0];
+ $col[4] = $_[HEAP]{anime_types}{ lc($col[4]) };
$col[5] = undef if !$col[5];
$col[6] = undef if !$col[6];
$_[KERNEL]->post(pg => do => 'UPDATE anime
diff --git a/lib/Multi/IRC.pm b/lib/Multi/IRC.pm
index 5018d2aa..4225e6b8 100644
--- a/lib/Multi/IRC.pm
+++ b/lib/Multi/IRC.pm
@@ -266,12 +266,12 @@ sub notify { # name, pid, payload
return if !$_[HEAP]{$k};
my $q = $_[ARG0] eq 'newrevision' ? q|SELECT
- CASE WHEN c.type = 0 THEN 'v' WHEN c.type = 1 THEN 'r' ELSE 'p' END AS type, c.rev, c.comments, c.id AS lastrev,
+ CASE WHEN c.type, c.rev, c.comments, c.id AS lastrev,
COALESCE(vr.vid, rr.rid, pr.pid) AS id, COALESCE(vr.title, rr.title, pr.name) AS title, u.username
FROM changes c
- LEFT JOIN vn_rev vr ON c.type = 0 AND c.id = vr.id
- LEFT JOIN releases_rev rr ON c.type = 1 AND c.id = rr.id
- LEFT JOIN producers_rev pr ON c.type = 2 AND c.id = pr.id
+ LEFT JOIN vn_rev vr ON c.type = 'v' AND c.id = vr.id
+ LEFT JOIN releases_rev rr ON c.type = 'r' AND c.id = rr.id
+ LEFT JOIN producers_rev pr ON c.type = 'p' AND c.id = pr.id
JOIN users u ON u.id = c.requester
WHERE c.id > ? AND c.requester <> 1
ORDER BY c.added|
diff --git a/lib/Multi/Maintenance.pm b/lib/Multi/Maintenance.pm
index e29c7b52..4f816e56 100644
--- a/lib/Multi/Maintenance.pm
+++ b/lib/Multi/Maintenance.pm
@@ -17,13 +17,13 @@ sub spawn {
package_states => [
$p => [qw|
_start shutdown set_daily daily set_monthly monthly log_stats
- vncache tagcache vnpopularity
- usercache statscache revcache logrotate
+ vncache tagcache vnpopularity cleangraphs
+ usercache statscache logrotate
|],
],
heap => {
- daily => [qw|vncache tagcache vnpopularity|],
- monthly => [qw|usercache statscache revcache logrotate|],
+ daily => [qw|vncache tagcache vnpopularity cleangraphs|],
+ monthly => [qw|usercache statscache logrotate|],
@_,
},
);
@@ -50,7 +50,7 @@ sub set_daily {
# (GMT because we're calculating on the UNIX timestamp, I can easily add an
# offset if necessary, but it doesn't really matter what time this cron
# runs, as long as it's run on a daily basis)
- $_[KERNEL]->alarm(daily => int(time/86400+1)*86400);
+ $_[KERNEL]->alarm(daily => int((time+3)/86400+1)*86400);
}
@@ -70,7 +70,7 @@ sub set_monthly {
# We do this by simply incrementing the timestamp with one day and checking gmtime()
# for a month change. This might not be very reliable, but should be enough for
# our purposes.
- my $nextday = int(time/86400+1)*86400;
+ my $nextday = int((time+3)/86400+1)*86400;
my $thismonth = (gmtime)[5]*100+(gmtime)[4]; # year*100 + month, for easy comparing
$nextday += 86400 while (gmtime $nextday)[5]*100+(gmtime $nextday)[4] <= $thismonth;
$_[KERNEL]->alarm(monthly => $nextday);
@@ -99,24 +99,33 @@ sub log_stats { # num, res, action, time
sub vncache {
- # this takes about 30s to complete. We really need to search for an alternative
+ # this takes about 40s to complete. We really need to search for an alternative
# method of keeping the c_* columns in the vn table up-to-date.
$_[KERNEL]->post(pg => do => 'SELECT update_vncache(0)', undef, 'log_stats', 'vncache');
}
sub tagcache {
- # this still takes "only" about 3 seconds max. Let's hope that doesn't increase too much.
+ # takes about 18 seconds max. ouch, but still kind-of acceptable
$_[KERNEL]->post(pg => do => 'SELECT tag_vn_calc()', undef, 'log_stats', 'tagcache');
}
sub vnpopularity {
- # still takes at most 2 seconds. Againt, let's hope that doesn't increase...
+ # still takes at most 2 seconds. let's hope that doesn't increase...
$_[KERNEL]->post(pg => do => 'SELECT update_vnpopularity()', undef, 'log_stats', 'vnpopularity');
}
+sub cleangraphs {
+ # should be pretty fast
+ $_[KERNEL]->post(pg => do => q|
+ DELETE FROM relgraphs vg
+ WHERE NOT EXISTS(SELECT 1 FROM vn WHERE rgraph = vg.id)
+ AND NOT EXISTS(SELECT 1 FROM producers WHERE rgraph = vg.id)
+ |, undef, 'log_stats', 'cleangraphs');
+}
+
#
# M O N T H L Y J O B S
@@ -164,14 +173,6 @@ sub statscache {
}
-sub revcache {
- # This -really- shouldn't be necessary...
- # Currently takes about 25 seconds to complete
- $_[KERNEL]->post(pg => do => q|SELECT update_rev('vn', ''), update_rev('releases', ''), update_rev('producers', '')|,
- undef, 'log_stats', 'revcache');
-}
-
-
sub logrotate {
my $dir = sprintf '%s/old', $VNDB::M{log_dir};
mkdir $dir if !-d $dir;
diff --git a/lib/Multi/RG.pm b/lib/Multi/RG.pm
index e427caf2..b3f9bb46 100644
--- a/lib/Multi/RG.pm
+++ b/lib/Multi/RG.pm
@@ -9,6 +9,8 @@ use strict;
use warnings;
use POE 'Wheel::Run', 'Filter::Stream';
use Encode 'encode_utf8';
+use XML::Parser;
+use XML::Writer;
use Time::HiRes 'time';
@@ -17,15 +19,13 @@ sub spawn {
POE::Session->create(
package_states => [
$p => [qw|
- _start shutdown check_rg creategraph getrel builddot buildgraph savegraph
+ _start shutdown check_rg creategraph getrel builddot savegraph finish
proc_stdin proc_stdout proc_stderr proc_closed proc_child
|],
],
heap => {
font => 'Arial',
fsize => [ 9, 7, 10 ], # nodes, edges, node_title
- imgdir => '/www/vndb/static/rg',
- moy => [qw| Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec |],
dot => '/usr/bin/dot',
check_delay => 3600,
@_,
@@ -51,10 +51,12 @@ sub shutdown {
sub check_rg {
- return if $_[HEAP]{vid};
- $_[KERNEL]->call(pg => query =>
- 'SELECT v.id FROM vn v JOIN vn_relations vr ON vr.vid1 = v.latest WHERE rgraph IS NULL AND hidden = FALSE LIMIT 1',
- undef, 'creategraph');
+ return if $_[HEAP]{id};
+ $_[KERNEL]->call(pg => query => q|
+ SELECT 'v' AS type, v.id FROM vn v JOIN vn_relations vr ON vr.vid1 = v.latest WHERE rgraph IS NULL AND hidden = FALSE
+ UNION
+ SELECT 'p', p.id FROM producers p JOIN producers_relations pr ON pr.pid1 = p.latest WHERE rgraph IS NULL AND hidden = FALSE
+ LIMIT 1|, undef, 'creategraph');
}
@@ -62,148 +64,138 @@ sub creategraph { # num, res
return $_[KERNEL]->delay('check_rg', $_[HEAP]{check_delay}) if $_[ARG0] == 0;
$_[HEAP]{start} = time;
- $_[HEAP]{vid} = $_[ARG1][0]{id};
- $_[HEAP]{rels} = {}; # relations (key=vid1-vid2, value=relation)
- $_[HEAP]{nodes} = {}; # nodes (key=vid, value= 0:found, 1:processed)
-
- $_[KERNEL]->post(pg => query =>
- 'SELECT vid2 AS id, relation FROM vn v JOIN vn_relations vr ON vr.vid1 = v.latest WHERE v.id = ?',
- [ $_[HEAP]{vid} ], 'getrel', $_[HEAP]{vid});
+ $_[HEAP]{id} = $_[ARG1][0]{id};
+ $_[HEAP]{type} = $_[ARG1][0]{type};
+ $_[HEAP]{rels} = {}; # relations (key=id1-id2, value=relation)
+ $_[HEAP]{nodes} = {}; # nodes (key=id, value= 0:found, 1:processed)
+
+ $_[KERNEL]->post(pg => query => $_[HEAP]{type} eq 'v'
+ ? 'SELECT vid2 AS id, relation FROM vn v JOIN vn_relations vr ON vr.vid1 = v.latest WHERE v.id = ?'
+ : 'SELECT pid2 AS id, relation FROM producers p JOIN producers_relations pr ON pr.pid1 = p.latest WHERE p.id = ?',
+ [ $_[HEAP]{id} ], 'getrel', $_[HEAP]{id});
}
-sub getrel { # num, res, vid
+sub getrel { # num, res, id
my $id = $_[ARG2];
$_[HEAP]{nodes}{$id} = 1;
for($_[ARG0] > 0 ? @{$_[ARG1]} : ()) {
- $_[HEAP]{rels}{$id.'-'.$_->{id}} = reverserel($_->{relation}) if $id < $_->{id};
- $_[HEAP]{rels}{$_->{id}.'-'.$id} = $_->{relation} if $id > $_->{id};
+ $_[HEAP]{rels}{$id.'-'.$_->{id}} = $VNDB::S{ $_[HEAP]{type} eq 'v' ? 'vn_relations' : 'prod_relations' }{$_->{relation}}[1] if $id < $_->{id};
+ $_[HEAP]{rels}{$_->{id}.'-'.$id} = $_->{relation} if $id > $_->{id};
if(!exists $_[HEAP]{nodes}{$_->{id}}) {
$_[HEAP]{nodes}{$_->{id}} = 0;
- $_[KERNEL]->post(pg => query =>
- 'SELECT vid2 AS id, relation FROM vn v JOIN vn_relations vr ON vr.vid1 = v.latest WHERE v.id = ?',
+ $_[KERNEL]->post(pg => query => $_[HEAP]{type} eq 'v'
+ ? 'SELECT vid2 AS id, relation FROM vn v JOIN vn_relations vr ON vr.vid1 = v.latest WHERE v.id = ?'
+ : 'SELECT pid2 AS id, relation FROM producers p JOIN producers_relations pr ON pr.pid1 = p.latest WHERE p.id = ?',
[ $_->{id} ], 'getrel', $_->{id});
}
}
- # do we have all relations now? get VN info
+ # do we have all relations now? get node info
if(!grep !$_, values %{$_[HEAP]{nodes}}) {
- $_[KERNEL]->post(pg => query =>
- 'SELECT v.id, vr.title, v.c_released AS date, v.c_languages AS lang
- FROM vn v JOIN vn_rev vr ON vr.id = v.latest
- WHERE v.id IN('.join(', ', map '?', keys %{$_[HEAP]{nodes}}).')',
+ my $ids = join(', ', map '?', keys %{$_[HEAP]{nodes}});
+ $_[KERNEL]->post(pg => query => $_[HEAP]{type} eq 'v'
+ ? "SELECT v.id, vr.title, v.c_released AS date, v.c_languages AS lang FROM vn v JOIN vn_rev vr ON vr.id = v.latest WHERE v.id IN($ids) ORDER BY v.c_released"
+ : "SELECT p.id, pr.name, pr.lang, pr.type FROM producers p JOIN producers_rev pr ON pr.id = p.latest WHERE p.id IN($ids) ORDER BY pr.name",
[ keys %{$_[HEAP]{nodes}} ], 'builddot');
}
}
sub builddot { # num, res
- my $vns = $_[ARG1];
+ my $nodes = $_[ARG1];
my $gv =
qq|graph rgraph {\n|.
- qq|\tratio = "compress"\n|.
- qq|\tgraph [ bgcolor="#ffffff00" ]\n|.
qq|\tnode [ fontname = "$_[HEAP]{font}", shape = "plaintext",|.
- qq| fontsize = $_[HEAP]{fsize}[0], style = "setlinewidth(0.5)", fontcolor = "#cccccc", color = "#225588" ]\n|.
+ qq| fontsize = $_[HEAP]{fsize}[0], fontcolor = "#333333", color = "#111111" ]\n|.
qq|\tedge [ labeldistance = 2.5, labelangle = -20, labeljust = 1, minlen = 2, dir = "both",|.
- qq| fontname = $_[HEAP]{font}, fontsize = $_[HEAP]{fsize}[1], arrowsize = 0.7, color = "#225588", fontcolor = "#cccccc" ]\n|;
-
- # insert all nodes, ordered by release date
- for (sort { $a->{date} <=> $b->{date} } @$vns) {
- my $date = sprintf '%08d', $_->{date};
- $date =~ s#^([0-9]{4})([0-9]{2}).+#$1==0?'N/A':$1==9999?'TBA':(($2&&$2<13?($_[HEAP]{moy}[$2-1].' '):'').$1)#e;
-
- my $title = $_->{title};
- $title = substr($title, 0, 27).'...' if length($title) > 30;
- $title =~ s/&/&amp;/g;
- $title =~ s/>/&gt;/g;
- $title =~ s/</&lt;/g;
-
- my $tooltip = $_->{title};
- $tooltip =~ s/\\/\\\\/g;
- $tooltip =~ s/"/\\"/g;
-
- $gv .= sprintf
- qq|\tv%d [ URL = "/v%d", tooltip = "%s" label=<|.
- q|<TABLE CELLSPACING="0" CELLPADDING="1" BORDER="0" CELLBORDER="1" BGCOLOR="#00000033">|.
- q|<TR><TD COLSPAN="2" ALIGN="CENTER" CELLPADDING="2"><FONT POINT-SIZE="%d"> %s </FONT></TD></TR>|.
- q|<TR><TD> %s </TD><TD> %s </TD></TR>|.
- qq|</TABLE>> ]\n|,
- $_->{id}, $_->{id}, encode_utf8($tooltip), $_[HEAP]{fsize}[2], encode_utf8($title), $date, $_->{lang}||'N/A';
- }
+ qq| fontname = $_[HEAP]{font}, fontsize = $_[HEAP]{fsize}[1], arrowsize = 0.7, color = "#111111", fontcolor = "#333333" ]\n|;
- # @rels = ([ vid1, vid2, relation, date1, date2 ], ..), for easier processing
- my @rels = map {
- /^([0-9]+)-([0-9]+)$/;
- my $vn1 = (grep $1 == $_->{id}, @$vns)[0];
- my $vn2 = (grep $2 == $_->{id}, @$vns)[0];
- [ $1, $2, $_[HEAP]{rels}{$_}, $vn1->{date}, $vn2->{date} ]
- } keys %{$_[HEAP]{rels}};
+ # insert all nodes
+ $gv .= $_[HEAP]{type} eq 'v' ? _vnnode($_, $_[HEAP]) : _prodnode($_, $_[HEAP]) for @$nodes;
- # insert all edges, ordered by release date again
- for (sort { ($a->[3]>$a->[4]?$a->[4]:$a->[3]) <=> ($b->[3]>$b->[4]?$b->[4]:$b->[3]) } @rels) {
- # [older game] -> [newer game]
- if($_->[4] > $_->[3]) {
- ($_->[0], $_->[1]) = ($_->[1], $_->[0]);
- $_->[2] = reverserel($_->[2]);
- }
- my $label =
- $VNDB::S{vn_relations}[$_->[2]][1]
- ? qq|headlabel = "$VNDB::S{vn_relations}[$_->[2]][0]", taillabel = "$VNDB::S{vn_relations}[$_->[2]-1][0]"| :
- $VNDB::S{vn_relations}[$_->[2]+1][1]
- ? qq|headlabel = "$VNDB::S{vn_relations}[$_->[2]][0]", taillabel = "$VNDB::S{vn_relations}[$_->[2]+1][0]"|
- : qq|label = " $VNDB::S{vn_relations}[$_->[2]][0]"|;
- $gv .= qq|\tv$$_[1] -- v$$_[0] [ $label ]\n|;
- }
+ # ...and relations
+ $gv .= $_[HEAP]{type} eq 'v' ? _vnrels($_[HEAP]{rels}, $nodes) : _prodrels($_[HEAP]{rels}, $nodes);
$gv .= "}\n";
- # get ID
- $_[KERNEL]->post(pg => query => 'INSERT INTO relgraph (cmap) VALUES (\'\') RETURNING id', undef, 'buildgraph', \$gv);
-}
-
-
-sub buildgraph { # num, res, \$gv
- $_[HEAP]{gid} = $_[ARG1][0]{id};
- $_[HEAP]{graph} = sprintf('%s/%02d/%d.png', $_[HEAP]{imgdir}, $_[ARG1][0]{id} % 100, $_[ARG1][0]{id});
- $_[HEAP]{cmap} = '';
-
- # roughly equivalent to:
- # cat layout.txt | dot -Tpng -o graph.png -Tcmapx
+ # Pass our dot file to graphviz
+ $_[HEAP]{svg} = '';
$_[HEAP]{proc} = POE::Wheel::Run->new(
Program => $_[HEAP]{dot},
- ProgramArgs => [ '-Tpng', '-o', $_[HEAP]{graph}, '-Tcmapx' ],
+ ProgramArgs => [ '-Tsvg' ],
StdioFilter => POE::Filter::Stream->new(),
StdinEvent => 'proc_stdin',
StdoutEvent => 'proc_stdout',
StderrEvent => 'proc_stderr',
CloseEvent => 'proc_closed',
);
- $_[HEAP]{proc}->put(${$_[ARG2]});
+ $_[HEAP]{proc}->put($gv);
}
sub savegraph {
- my $vids = join ',', sort map int, keys %{$_[HEAP]{nodes}};
+ # Before saving the SVG output, we'll modify it a little:
+ # - Remove comments
+ # - Add svg: prefix to all tags
+ # - Remove xmlns declarations (this is set in the html)
+ # - Remove <title> elements (unused)
+ # - Remove id attributes (unused)
+ # - Remove first <polygon> element (emulates the background color)
+ # - Replace stroke and fill attributes with classes (so that coloring is done in CSS)
+ my $svg = '';
+ my $w = XML::Writer->new(OUTPUT => \$svg);
+ my $p = XML::Parser->new;
+ $p->setHandlers(
+ Start => sub {
+ my($expat, $el, %attr) = @_;
+ return if $el eq 'title' || $expat->in_element('title');
+ return if $el eq 'polygon' && $expat->depth == 2;
+
+ $attr{class} = 'border' if $attr{stroke} && $attr{stroke} eq '#111111';
+ $attr{class} = 'nodebg' if $attr{fill} && $attr{fill} eq '#222222';
+
+ delete @attr{qw|stroke fill id xmlns xmlns:xlink|};
+ $el eq 'path' || $el eq 'polygon'
+ ? $w->emptyTag("svg:$el", %attr)
+ : $w->startTag("svg:$el", %attr);
+ },
+ End => sub {
+ my($expat, $el) = @_;
+ return if $el eq 'title' || $expat->in_element('title');
+ return if $el eq 'polygon' && $expat->depth == 2;
+ $w->endTag("svg:$el") if $el ne 'path' && $el ne 'polygon';
+ },
+ Char => sub {
+ my($expat, $str) = @_;
+ return if $expat->in_element('title');
+ $w->characters($str) if $str !~ /^[\s\t\r\n]*$/s;
+ }
+ );
+ $p->parsestring($_[HEAP]{svg});
+ $w->end();
+
+ # save the processed SVG in the database and fetch graph ID
+ $_[KERNEL]->post(pg => query => 'INSERT INTO relgraphs (svg) VALUES (?) RETURNING id', [ $svg ], 'finish');
+}
- # chmod graph
- chmod 0666, $_[HEAP]{graph};
- # save the image map in the database
- $_[KERNEL]->post(pg => do => 'UPDATE relgraph SET cmap = ? WHERE id = ?',
- [ "<!-- V:$vids -->\n$_[HEAP]{cmap}", $_[HEAP]{gid} ]);
+sub finish { # num, res
+ my $id = $_[ARG1][0]{id};
+ my $ids = join ',', sort map int, keys %{$_[HEAP]{nodes}};
+ my $table = $_[HEAP]{type} eq 'v' ? 'vn' : 'producers';
- # update the VN table
- $_[KERNEL]->post(pg => do => "UPDATE vn SET rgraph = ? WHERE id IN($vids)", [ $_[HEAP]{gid} ]);
+ # update the table
+ $_[KERNEL]->post(pg => do => "UPDATE $table SET rgraph = ? WHERE id IN($ids)", [ $id ]);
# log
- $_[KERNEL]->call(core => log => 'Generated relation graph in %.2fs, V: %s', time-$_[HEAP]{start}, $vids);
+ $_[KERNEL]->call(core => log => 'Generated relation graph #%d in %.2fs, %s: %s', $id, time-$_[HEAP]{start}, uc $_[HEAP]{type}, $ids);
# clean up
- delete @{$_[HEAP]}{qw| start vid nodes rels gid graph cmap proc |};
+ delete @{$_[HEAP]}{qw| start id type nodes rels svg proc |};
# check for more things to do
$_[KERNEL]->yield('check_rg');
@@ -216,7 +208,7 @@ sub proc_stdin {
$_[HEAP]{proc}->shutdown_stdin;
}
sub proc_stdout {
- $_[HEAP]{cmap} .= $_[ARG0];
+ $_[HEAP]{svg} .= $_[ARG0];
}
sub proc_stderr {
$_[KERNEL]->call(core => log => 'GraphViz STDERR: %s', $_[ARG0]);
@@ -230,9 +222,107 @@ sub proc_child {
-# non-POE helper function
-sub reverserel { # relation
- return $VNDB::S{vn_relations}[$_[0]][1] ? $_[0]-1 : $VNDB::S{vn_relations}[$_[0]+1][1] ? $_[0]+1 : $_[0];
+# non-POE helper functions
+
+sub _vnnode {
+ my($n, $heap) = @_;
+
+ my $date = sprintf '%08d', $n->{date};
+ $date =~ s{^([0-9]{4})([0-9]{2})([0-9]{2})$}{
+ $1 == 0 ? 'unknown'
+ : $1 == 9999 ? 'TBA'
+ : $2 == 99 ? $1
+ : $3 == 99 ? "$1-$2" : "$1-$2-$3"
+ }e;
+
+ my $title = $n->{title};
+ $title = substr($title, 0, 27).'...' if length($title) > 30;
+ $title =~ s/&/&amp;/g;
+ $title =~ s/>/&gt;/g;
+ $title =~ s/</&lt;/g;
+
+ my $tooltip = $n->{title};
+ $tooltip =~ s/\\/\\\\/g;
+ $tooltip =~ s/"/\\"/g;
+
+ return sprintf
+ qq|\tv%d [ URL = "/v%d", tooltip = "%s", label=<|.
+ q|<TABLE CELLSPACING="0" CELLPADDING="1" BORDER="0" CELLBORDER="1" BGCOLOR="#222222">|.
+ q|<TR><TD COLSPAN="2" ALIGN="CENTER" CELLPADDING="2"><FONT POINT-SIZE="%d"> %s </FONT></TD></TR>|.
+ q|<TR><TD> %s </TD><TD> %s </TD></TR>|.
+ qq|</TABLE>> ]\n|,
+ $_->{id}, $_->{id}, encode_utf8($tooltip), $heap->{fsize}[2], encode_utf8($title), $date, $n->{lang}||'N/A';
+}
+
+
+sub _vnrels {
+ my($rels, $vns) = @_;
+ my $r = '';
+
+ # @rels = ([ vid1, vid2, relation, date1, date2 ], ..), for easier processing
+ my @rels = map {
+ /^([0-9]+)-([0-9]+)$/;
+ my $vn1 = (grep $1 == $_->{id}, @$vns)[0];
+ my $vn2 = (grep $2 == $_->{id}, @$vns)[0];
+ [ $1, $2, $rels->{$_}, $vn1->{date}, $vn2->{date} ]
+ } keys %$rels;
+
+ # insert all edges, ordered by release date again
+ for (sort { ($a->[3]>$a->[4]?$a->[4]:$a->[3]) <=> ($b->[3]>$b->[4]?$b->[4]:$b->[3]) } @rels) {
+ # [older game] -> [newer game]
+ if($_->[4] > $_->[3]) {
+ ($_->[0], $_->[1]) = ($_->[1], $_->[0]);
+ $_->[2] = $VNDB::S{vn_relations}{$_->[2]}[1];
+ }
+ my $rev = $VNDB::S{vn_relations}{$_->[2]}[1];
+ my $label = $rev ne $_->[2]
+ ? qq|headlabel = "\$____vnrel_$_->[2]____\$", taillabel = "\$____vnrel_${rev}____\$"|
+ : qq|label = "\$____vnrel_$_->[2]____\$"|;
+ $r .= qq|\tv$$_[1] -- v$$_[0] [ $label ]\n|;
+ }
+ return $r;
+}
+
+
+sub _prodnode {
+ my($n, $heap) = @_;
+
+ my $name = $n->{name};
+ $name = substr($name, 0, 27).'...' if length($name) > 30;
+ $name =~ s/&/&amp;/g;
+ $name =~ s/>/&gt;/g;
+ $name =~ s/</&lt;/g;
+
+ my $tooltip = $n->{name};
+ $tooltip =~ s/\\/\\\\/g;
+ $tooltip =~ s/"/\\"/g;
+
+ return sprintf
+ qq|\tp%d [ URL = "/p%d", tooltip = "%s", label=<|.
+ q|<TABLE CELLSPACING="0" CELLPADDING="1" BORDER="0" CELLBORDER="1" BGCOLOR="#222222">|.
+ q|<TR><TD COLSPAN="2" ALIGN="CENTER" CELLPADDING="2"><FONT POINT-SIZE="%d"> %s </FONT></TD></TR>|.
+ q|<TR><TD ALIGN="CENTER"> $_lang_%s_$ </TD><TD ALIGN="CENTER"> $_ptype_%s_$ </TD></TR>|.
+ qq|</TABLE>> ]\n|,
+ $_->{id}, $_->{id}, encode_utf8($tooltip), $heap->{fsize}[2], encode_utf8($name), $n->{lang}, $n->{type};
+}
+
+
+sub _prodrels {
+ my($rels, $prods) = @_;
+ my $r = '';
+
+ for (keys %$rels) {
+ /^([0-9]+)-([0-9]+)$/;
+ my $p1 = (grep $1 == $_->{id}, @$prods)[0];
+ my $p2 = (grep $2 == $_->{id}, @$prods)[0];
+
+ my $rev = $VNDB::S{prod_relations}{$rels->{$_}}[1];
+ my $label = $rev ne $rels->{$_}
+ ? qq|headlabel = "\$____prodrel_${rev}____\$", taillabel = "\$____prodrel_$rels->{$_}____\$"|
+ : qq|label = "\$____prodrel_$rels->{$_}____\$"|;
+ $r .= qq|\tp$p1->{id} -- p$p2->{id} [ $label ]\n|;
+ }
+ return $r;
}
diff --git a/lib/VNDB/DB/Discussions.pm b/lib/VNDB/DB/Discussions.pm
index 26beaaee..60487098 100644
--- a/lib/VNDB/DB/Discussions.pm
+++ b/lib/VNDB/DB/Discussions.pm
@@ -5,7 +5,7 @@ use strict;
use warnings;
use Exporter 'import';
-our @EXPORT = qw|dbThreadGet dbThreadEdit dbThreadAdd dbPostGet dbPostEdit dbPostAdd dbThreadCount|;
+our @EXPORT = qw|dbThreadGet dbThreadEdit dbThreadAdd dbPostGet dbPostEdit dbPostAdd dbThreadCount dbPostRead|;
# Options: id, type, iid, results, page, what, notusers
@@ -243,5 +243,15 @@ sub dbPostAdd {
}
+sub dbPostRead { # thread id, user id, last post number
+ my($s, $tid, $uid, $num) = @_;
+ $s->dbExec(q|
+ UPDATE threads_boards
+ SET lastread = ?
+ WHERE tid = ? AND type = 'u' AND iid = ?|,
+ $num, $tid, $uid);
+}
+
+
1;
diff --git a/lib/VNDB/DB/Misc.pm b/lib/VNDB/DB/Misc.pm
index eeb860b0..b3522d72 100644
--- a/lib/VNDB/DB/Misc.pm
+++ b/lib/VNDB/DB/Misc.pm
@@ -6,7 +6,7 @@ use warnings;
use Exporter 'import';
our @EXPORT = qw|
- dbStats dbRevisionInsert dbItemInsert dbRevisionGet dbItemMod dbLanguages dbRandomQuote
+ dbStats dbRevisionInsert dbItemInsert dbRevisionGet dbItemMod dbRandomQuote
|;
@@ -24,12 +24,12 @@ sub dbStats {
# This function leaves the DB in an inconsistent state, the actual revision
# will have to be inserted directly after calling this function, otherwise
# the commit will fail.
-# Arguments: type [0..2], item ID, edit summary
+# Arguments: type [vrp], item ID, edit summary
# Returns: local revision, global revision
sub dbRevisionInsert {
my($self, $type, $iid, $editsum, $uid) = @_;
- my $table = [qw|vn releases producers|]->[$type];
+ my $table = {qw|v vn r releases p producers|}->{$type};
my $c = $self->dbRow(q|
INSERT INTO changes (type, requester, ip, comments, rev)
@@ -43,7 +43,7 @@ sub dbRevisionInsert {
))
RETURNING id, rev|,
$type, $uid||$self->authInfo->{id}, $self->reqIP, $editsum,
- $table, [qw|v r p|]->[$type], $iid
+ $table, $type, $iid
);
$self->dbExec(q|UPDATE !s SET latest = ? WHERE id = ?|, $table, $c->{id}, $iid);
@@ -54,7 +54,7 @@ sub dbRevisionInsert {
# Comparable to RevisionInsert, but creates a new item with a corresponding
# change. Same things about inconsistent state, etc.
-# Argumments: type [0..2], edit summary, [uid]
+# Argumments: type [vrp], edit summary, [uid]
# Returns: item id, global revision
sub dbItemInsert {
my($self, $type, $editsum, $uid) = @_;
@@ -70,7 +70,7 @@ sub dbItemInsert {
INSERT INTO !s (latest)
VALUES (?)
RETURNING id|,
- [qw|vn releases producers|]->[$type], $cid
+ {qw|v vn r releases p producers|}->{$type}, $cid
)->{id};
return ($iid, $cid);
@@ -94,7 +94,7 @@ sub dbRevisionGet {
'((c.type = ? AND vr.vid = ?) OR (c.type = ? AND rv.vid = ?))' => [0, $o{iid}, 1, $o{iid}],
) : (
$o{type} ? (
- 'c.type = ?' => { v=>0, r=>1, p=>2 }->{$o{type}} ) : (),
+ 'c.type = ?' => $o{type} ) : (),
$o{iid} ? (
'!sr.!sid = ?' => [ $o{type}, $o{type}, $o{iid} ] ) : (),
),
@@ -113,14 +113,14 @@ sub dbRevisionGet {
my @join = (
$o{iid} || $o{what} =~ /item/ || $o{hidden} || $o{releases} ? (
- 'LEFT JOIN vn_rev vr ON c.type = 0 AND c.id = vr.id',
- 'LEFT JOIN releases_rev rr ON c.type = 1 AND c.id = rr.id',
- 'LEFT JOIN producers_rev pr ON c.type = 2 AND c.id = pr.id',
+ q{LEFT JOIN vn_rev vr ON c.type = 'v' AND c.id = vr.id},
+ q{LEFT JOIN releases_rev rr ON c.type = 'r' AND c.id = rr.id},
+ q{LEFT JOIN producers_rev pr ON c.type = 'p' AND c.id = pr.id},
) : (),
$o{hidden} || $o{releases} ? (
- 'LEFT JOIN vn v ON c.type = 0 AND vr.vid = v.id',
- 'LEFT JOIN releases r ON c.type = 1 AND rr.rid = r.id',
- 'LEFT JOIN producers p ON c.type = 2 AND pr.pid = p.id',
+ q{LEFT JOIN vn v ON c.type = 'v' AND vr.vid = v.id},
+ q{LEFT JOIN releases r ON c.type = 'r' AND rr.rid = r.id},
+ q{LEFT JOIN producers p ON c.type = 'p' AND pr.pid = p.id},
) : (),
$o{what} =~ /user/ ? 'JOIN users u ON c.requester = u.id' : (),
$o{releases} ? 'LEFT JOIN releases_vn rv ON c.id = rv.rid' : (),
@@ -160,20 +160,6 @@ sub dbItemMod {
}
-# Returns a list of languages actually in use
-sub dbLanguages {
- my $self = shift;
- return [
- map $_->{lang}, @{$self->dbAll(q|
- SELECT DISTINCT rl.lang
- FROM releases r
- JOIN releases_lang rl ON rl.rid = r.latest
- WHERE r.hidden = FALSE|
- )}
- ];
-}
-
-
# Returns a random quote (hashref with keys = vid, quote)
sub dbRandomQuote {
return $_[0]->dbRow(q|
diff --git a/lib/VNDB/DB/Producers.pm b/lib/VNDB/DB/Producers.pm
index 65d1fbca..7c7da3ba 100644
--- a/lib/VNDB/DB/Producers.pm
+++ b/lib/VNDB/DB/Producers.pm
@@ -9,7 +9,7 @@ our @EXPORT = qw|dbProducerGet dbProducerEdit dbProducerAdd|;
# options: results, page, id, search, char, rev
-# what: extended, changes, vn
+# what: extended changes vn relations relgraph
sub dbProducerGet {
my $self = shift;
my %o = (
@@ -40,10 +40,12 @@ sub dbProducerGet {
push @join, $o{rev} ? 'JOIN producers p ON p.id = pr.pid' : 'JOIN producers p ON pr.id = p.latest';
push @join, 'JOIN changes c ON c.id = pr.id' if $o{what} =~ /changes/ || $o{rev};
push @join, 'JOIN users u ON u.id = c.requester' if $o{what} =~ /changes/;
+ push @join, 'JOIN relgraphs pg ON pg.id = p.rgraph' if $o{what} =~ /relgraph/;
- my $select = 'p.id, pr.type, pr.name, pr.original, pr.lang';
+ my $select = 'p.id, pr.type, pr.name, pr.original, pr.lang, pr.id AS cid, p.rgraph';
$select .= ', pr.desc, pr.alias, pr.website, p.hidden, p.locked' if $o{what} =~ /extended/;
$select .= q|, extract('epoch' from c.added) as added, c.requester, c.comments, p.latest, pr.id AS cid, u.username, c.rev| if $o{what} =~ /changes/;
+ $select .= ', pg.svg' if $o{what} =~ /relgraph/;
my($r, $np) = $self->dbPage(\%o, q|
SELECT !s
@@ -61,7 +63,8 @@ sub dbProducerGet {
} 0..$#$r;
push @{$r->[$r{$_->{pid}}]{vn}}, $_ for (@{$self->dbAll(q|
- SELECT MAX(vp.pid) AS pid, v.id, MAX(vr.title) AS title, MAX(vr.original) AS original, MIN(rr.released) AS date
+ SELECT MAX(vp.pid) AS pid, v.id, MAX(vr.title) AS title, MAX(vr.original) AS original, MIN(rr.released) AS date,
+ MAX(CASE WHEN vp.developer = true THEN 1 ELSE 0 END) AS developer, MAX(CASE WHEN vp.publisher = true THEN 1 ELSE 0 END) AS publisher
FROM releases_producers vp
JOIN releases_rev rr ON rr.id = vp.rid
JOIN releases r ON r.latest = rr.id
@@ -77,6 +80,22 @@ sub dbProducerGet {
)});
}
+ if(@$r && $o{what} =~ /relations/) {
+ my %r = map {
+ $r->[$_]{relations} = [];
+ ($r->[$_]{cid}, $_)
+ } 0..$#$r;
+
+ push @{$r->[$r{$_->{pid1}}]{relations}}, $_ for(@{$self->dbAll(q|
+ SELECT rel.pid1, rel.pid2 AS id, rel.relation, pr.name, pr.original
+ FROM producers_relations rel
+ JOIN producers p ON rel.pid2 = p.id
+ JOIN producers_rev pr ON p.latest = pr.id
+ WHERE rel.pid1 IN(!l)|,
+ [ keys %r ]
+ )});
+ }
+
return wantarray ? ($r, $np) : $r;
}
@@ -85,7 +104,7 @@ sub dbProducerGet {
# returns: ( local revision, global revision )
sub dbProducerEdit {
my($self, $pid, %o) = @_;
- my($rev, $cid) = $self->dbRevisionInsert(2, $pid, $o{editsum}, $o{uid});
+ my($rev, $cid) = $self->dbRevisionInsert('p', $pid, $o{editsum}, $o{uid});
insert_rev($self, $cid, $pid, \%o);
return ($rev, $cid);
}
@@ -95,14 +114,14 @@ sub dbProducerEdit {
# returns: ( item id, global revision )
sub dbProducerAdd {
my($self, %o) = @_;
- my($pid, $cid) = $self->dbItemInsert(2, $o{editsum}, $o{uid});
+ my($pid, $cid) = $self->dbItemInsert('p', $o{editsum}, $o{uid});
insert_rev($self, $cid, $pid, \%o);
return ($pid, $cid);
}
# helper function, inserts a producer revision
-# Arguments: global revision, item id, { columns in producers_rev }
+# Arguments: global revision, item id, { columns in producers_rev }, relations
sub insert_rev {
my($self, $cid, $pid, $o) = @_;
$self->dbExec(q|
@@ -110,6 +129,12 @@ sub insert_rev {
VALUES (!l)|,
[ $cid, $pid, @$o{qw| name original website type lang desc alias|} ]
);
+
+ $self->dbExec(q|
+ INSERT INTO producers_relations (pid1, pid2, relation)
+ VALUES (?, ?, ?)|,
+ $cid, $_->[1], $_->[0]
+ ) for (@{$o->{relations}});
}
diff --git a/lib/VNDB/DB/Releases.pm b/lib/VNDB/DB/Releases.pm
index 672596da..9260b787 100644
--- a/lib/VNDB/DB/Releases.pm
+++ b/lib/VNDB/DB/Releases.pm
@@ -117,7 +117,7 @@ sub dbReleaseGet {
if($o{what} =~ /producers/) {
push(@{$r->[$r{$_->{rid}}]{producers}}, $_) for (@{$self->dbAll(q|
- SELECT rp.rid, p.id, pr.name, pr.original, pr.type
+ SELECT rp.rid, rp.developer, rp.publisher, p.id, pr.name, pr.original, pr.type
FROM releases_producers rp
JOIN producers p ON rp.pid = p.id
JOIN producers_rev pr ON pr.id = p.latest
@@ -137,7 +137,7 @@ sub dbReleaseGet {
}
if($o{what} =~ /media/) {
- ($_->{medium}=~s/\s+//||1)&&push(@{$r->[$r{$_->{rid}}]{media}}, $_) for (@{$self->dbAll(q|
+ push(@{$r->[$r{$_->{rid}}]{media}}, $_) for (@{$self->dbAll(q|
SELECT rid, medium, qty
FROM releases_media
WHERE rid IN(!l)|,
@@ -154,7 +154,7 @@ sub dbReleaseGet {
# returns: ( local revision, global revision )
sub dbReleaseEdit {
my($self, $rid, %o) = @_;
- my($rev, $cid) = $self->dbRevisionInsert(1, $rid, $o{editsum}, $o{uid});
+ my($rev, $cid) = $self->dbRevisionInsert('r', $rid, $o{editsum}, $o{uid});
insert_rev($self, $cid, $rid, \%o);
return ($rev, $cid);
}
@@ -164,7 +164,7 @@ sub dbReleaseEdit {
# returns: ( item id, global revision )
sub dbReleaseAdd {
my($self, %o) = @_;
- my($rid, $cid) = $self->dbItemInsert(1, $o{editsum}, $o{uid});
+ my($rid, $cid) = $self->dbItemInsert('r', $o{editsum}, $o{uid});
insert_rev($self, $cid, $rid, \%o);
return ($rid, $cid);
}
@@ -189,9 +189,9 @@ sub insert_rev {
) for (@{$o->{languages}});
$self->dbExec(q|
- INSERT INTO releases_producers (rid, pid)
- VALUES (?, ?)|,
- $cid, $_
+ INSERT INTO releases_producers (rid, pid, developer, publisher)
+ VALUES (?, ?, ?, ?)|,
+ $cid, $_->[0], $_->[1]?1:0, $_->[2]?1:0
) for (@{$o->{producers}});
$self->dbExec(q|
diff --git a/lib/VNDB/DB/Users.pm b/lib/VNDB/DB/Users.pm
index b2cd1a31..593c6415 100644
--- a/lib/VNDB/DB/Users.pm
+++ b/lib/VNDB/DB/Users.pm
@@ -5,11 +5,11 @@ use strict;
use warnings;
use Exporter 'import';
-our @EXPORT = qw|dbUserGet dbUserEdit dbUserAdd dbUserDel dbSessionAdd dbSessionDel dbSessionCheck|;
+our @EXPORT = qw|dbUserGet dbUserEdit dbUserAdd dbUserDel dbUserMessageCount dbSessionAdd dbSessionDel dbSessionCheck|;
# %options->{ username passwd mail order uid ip registered search results page what }
-# what: stats mymessages
+# what: stats extended
sub dbUserGet {
my $s = shift;
my %o = (
@@ -43,8 +43,12 @@ sub dbUserGet {
);
my @select = (
- qw|id username mail rank salt c_votes c_changes show_nsfw show_list skin customcss ip c_tags ign_votes|,
- q|encode(passwd, 'hex') AS passwd|, q|extract('epoch' from registered) as registered|,
+ qw|id username c_votes c_changes show_list c_tags|,
+ q|extract('epoch' from registered) as registered|,
+ $o{what} =~ /extended/ ? (
+ qw|mail rank salt skin customcss show_nsfw ign_votes|,
+ q|encode(passwd, 'hex') AS passwd|
+ ) : (),
$o{what} =~ /stats/ ? (
'(SELECT COUNT(*) FROM rlists WHERE uid = u.id) AS releasecount',
'(SELECT COUNT(DISTINCT rv.vid) FROM rlists rl JOIN releases r ON rl.rid = r.id JOIN releases_vn rv ON rv.rid = r.latest WHERE uid = u.id) AS vncount',
@@ -53,8 +57,6 @@ sub dbUserGet {
'(SELECT COUNT(DISTINCT tag) FROM tags_vn WHERE uid = u.id) AS tagcount',
'(SELECT COUNT(DISTINCT vid) FROM tags_vn WHERE uid = u.id) AS tagvncount',
) : (),
- $o{what} =~ /mymessages/ ?
- '(SELECT COUNT(*) FROM threads_boards tb JOIN threads t ON t.id = tb.tid WHERE tb.type = \'u\' AND tb.iid = u.id AND t.hidden = FALSE) AS mymessages' : (),
);
my($r, $np) = $s->dbPage(\%o, q|
@@ -110,6 +112,20 @@ sub dbUserDel {
}
+# Returns number of unread messages
+sub dbUserMessageCount { # uid
+ my($s, $uid) = @_;
+ return $s->dbRow(q{
+ SELECT SUM(tbi.count) AS cnt FROM (
+ SELECT t.count-COALESCE(tb.lastread,0)
+ FROM threads_boards tb
+ JOIN threads t ON t.id = tb.tid AND NOT t.hidden
+ WHERE tb.type = 'u' AND tb.iid = ?
+ ) AS tbi (count)
+ }, $uid)->{cnt}||0;
+}
+
+
# Adds a session to the database
# If no expiration is supplied the database default is used
# uid, 40 character session token, expiration time (timestamp)
diff --git a/lib/VNDB/DB/VN.pm b/lib/VNDB/DB/VN.pm
index b3603985..bb2c1275 100644
--- a/lib/VNDB/DB/VN.pm
+++ b/lib/VNDB/DB/VN.pm
@@ -5,6 +5,7 @@ use strict;
use warnings;
use Exporter 'import';
use VNDB::Func 'gtintype';
+use Encode 'decode_utf8';
our @EXPORT = qw|dbVNGet dbVNAdd dbVNEdit dbVNImageId dbVNCache dbScreenshotAdd dbScreenshotGet dbScreenshotRandom|;
@@ -76,7 +77,7 @@ sub dbVNGet {
$o{what} =~ /changes/ ?
'JOIN users u ON u.id = c.requester' : (),
$o{what} =~ /relgraph/ ?
- 'JOIN relgraph rg ON rg.id = v.rgraph' : (),
+ 'JOIN relgraphs vg ON vg.id = v.rgraph' : (),
);
my $tag_ids = $o{tags_include} && join ',', @{$o{tags_include}[1]};
@@ -86,7 +87,7 @@ sub dbVNGet {
qw|vr.alias vr.image vr.img_nsfw vr.length vr.desc vr.l_wp vr.l_encubed vr.l_renai vr.l_vnn| ) : (),
$o{what} =~ /changes/ ? (
qw|c.requester c.comments v.latest u.username c.rev c.causedby|, q|extract('epoch' from c.added) as added|) : (),
- $o{what} =~ /relgraph/ ? 'rg.cmap' : (),
+ $o{what} =~ /relgraph/ ? 'vg.svg' : (),
$o{what} =~ /ranking/ ? '(SELECT COUNT(*)+1 FROM vn iv WHERE iv.hidden = false AND iv.c_popularity > v.c_popularity) AS ranking' : (),
$tag_ids ?
qq|(SELECT AVG(tvb.rating) FROM tags_vn_bayesian tvb WHERE tvb.tag IN($tag_ids) AND tvb.vid = v.id AND spoiler <= $o{tags_include}[0] GROUP BY tvb.vid) AS tagscore| : (),
@@ -101,6 +102,10 @@ sub dbVNGet {
join(', ', @select), join(' ', @join), \%where, $o{order},
);
+ if($o{what} =~ /relgraph/) {
+ $_->{svg} = decode_utf8($_->{svg}) for @$r;
+ }
+
if(@$r && $o{what} =~ /(anime|relations|screenshots)/) {
my %r = map {
$r->[$_]{anime} = [];
@@ -155,7 +160,7 @@ sub dbVNGet {
# returns: ( local revision, global revision )
sub dbVNEdit {
my($self, $id, %o) = @_;
- my($rev, $cid) = $self->dbRevisionInsert(0, $id, $o{editsum}, $o{uid});
+ my($rev, $cid) = $self->dbRevisionInsert('v', $id, $o{editsum}, $o{uid});
insert_rev($self, $cid, $id, \%o);
return ($rev, $cid);
}
@@ -165,7 +170,7 @@ sub dbVNEdit {
# returns: ( item id, global revision )
sub dbVNAdd {
my($self, %o) = @_;
- my($id, $cid) = $self->dbItemInsert(0, $o{editsum}, $o{uid});
+ my($id, $cid) = $self->dbItemInsert('v', $o{editsum}, $o{uid});
insert_rev($self, $cid, $id, \%o);
return ($id, $cid);
}
diff --git a/lib/VNDB/Func.pm b/lib/VNDB/Func.pm
index cd4c4b62..ad38215e 100644
--- a/lib/VNDB/Func.pm
+++ b/lib/VNDB/Func.pm
@@ -149,10 +149,10 @@ sub gtintype {
sub liststat {
my $l = shift;
return '' if !$l;
- my $rs = $YAWF::OBJ->{vn_rstat}[$l->{rstat}];
+ my $rs = mt('_rlst_rstat_'.$l->{rstat});
$rs = qq|<b class="done">$rs</b>| if $l->{rstat} == 2; # Obtained
$rs = qq|<b class="todo">$rs</b>| if $l->{rstat} < 2; # Unknown/pending
- my $vs = $YAWF::OBJ->{vn_vstat}[$l->{vstat}];
+ my $vs = mt('_rlst_vstat_'.$l->{vstat});
$vs = qq|<b class="done">$vs</b>| if $l->{vstat} == 2; # Finished
$vs = qq|<b class="todo">$vs</b>| if $l->{vstat} == 0 || $l->{vstat} == 4; # Unknown/dropped
return "$rs / $vs";
diff --git a/lib/VNDB/Handler/Discussions.pm b/lib/VNDB/Handler/Discussions.pm
index 1d74ac6a..12b55029 100644
--- a/lib/VNDB/Handler/Discussions.pm
+++ b/lib/VNDB/Handler/Discussions.pm
@@ -29,6 +29,11 @@ sub thread {
my $p = $self->dbPostGet(tid => $tid, results => 25, page => $page, what => 'user');
return 404 if !$p->[0];
+ # mark as read when this thread is posted in the board of the currently logged in user
+ my $uid = $self->authInfo->{id};
+ $self->dbPostRead($t->{id}, $uid, $p->[$#$p]{num})
+ if $uid && grep $_->{type} eq 'u' && $_->{iid} == $uid, @{$t->{boards}};
+
$self->htmlHeader(title => $t->{title});
div class => 'mainbox';
@@ -293,7 +298,7 @@ sub board {
order => $type eq 'an' ? 't.id DESC' : 'tpl.date DESC',
);
- $self->htmlHeader(title => $title, noindex => !@$list);
+ $self->htmlHeader(title => $title, noindex => !@$list || $type eq 'u');
$self->htmlMainTabs($type, $obj, 'disc') if $iid;
div class => 'mainbox';
diff --git a/lib/VNDB/Handler/Misc.pm b/lib/VNDB/Handler/Misc.pm
index 80a9e9ea..9625372b 100644
--- a/lib/VNDB/Handler/Misc.pm
+++ b/lib/VNDB/Handler/Misc.pm
@@ -66,10 +66,9 @@ sub homepage {
my $changes = $self->dbRevisionGet(what => 'item user', results => 10, auto => 1, hidden => 1);
ul;
for (@$changes) {
- my $t = (qw|v r p|)[$_->{type}];
li;
- lit mt '_home_recentchanges_item', $t,
- sprintf('<a href="%s" title="%s">%s</a>', "/$t$_->{iid}.$_->{rev}",
+ lit mt '_home_recentchanges_item', $_->{type},
+ sprintf('<a href="%s" title="%s">%s</a>', "/$_->{type}$_->{iid}.$_->{rev}",
xml_escape($_->{ioriginal}||$_->{ititle}), xml_escape shorten $_->{ititle}, 33),
$_;
end;
@@ -295,6 +294,12 @@ sub docpage {
close $F;
$ii;
}eg;
+ s{^:TOP5CONTRIB:$}{
+ my $l = $self->dbUserGet(results => 6, order => 'c_changes DESC');
+ '<dl>'.join('', map $_->{id} == 1 ? () :
+ sprintf('<dt><a href="/u%d">%s</a></dt><dd>%d</dd>', $_->{id}, $_->{username}, $_->{c_changes}),
+ @$l).'</dl>';
+ }eg;
}
$self->htmlHeader(title => $title);
@@ -331,7 +336,7 @@ sub itemmod {
my $obj = $type eq 'v' ? $self->dbVNGet(id => $iid)->[0] :
$type eq 'r' ? $self->dbReleaseGet(id => $iid, what => 'vn extended')->[0] :
- $self->dbProducerGet(id => $iid)->[0];
+ $self->dbProducerGet(id => $iid, what => 'extended')->[0];
return 404 if !$obj->{id};
$self->dbItemMod($type, $iid, $act eq 'hide' ? (hidden => !$obj->{hidden}) : (locked => !$obj->{locked}));
diff --git a/lib/VNDB/Handler/Producers.pm b/lib/VNDB/Handler/Producers.pm
index e6477567..4e75962b 100644
--- a/lib/VNDB/Handler/Producers.pm
+++ b/lib/VNDB/Handler/Producers.pm
@@ -3,11 +3,12 @@ package VNDB::Handler::Producers;
use strict;
use warnings;
-use YAWF ':html', ':xml';
+use YAWF ':html', ':xml', 'xml_escape';
use VNDB::Func;
YAWF::register(
+ qr{p([1-9]\d*)/rg} => \&rg,
qr{p([1-9]\d*)(?:\.([1-9]\d*))?} => \&page,
qr{p(?:([1-9]\d*)(?:\.([1-9]\d*))?/edit|/new)}
=> \&edit,
@@ -16,12 +17,34 @@ YAWF::register(
);
+sub rg {
+ my($self, $pid) = @_;
+
+ my $p = $self->dbProducerGet(id => $pid, what => 'relgraph')->[0];
+ return 404 if !$p->{id} || !$p->{rgraph};
+
+ my $title = mt '_prodrg_title', $p->{name};
+ return if $self->htmlRGHeader($title, 'p', $p);
+
+ $p->{svg} =~ s/\$___(_prodrel_[a-z]+)____\$/mt $1/eg;
+ $p->{svg} =~ s/\$(_lang_[a-z]+)_\$/mt $1/eg;
+ $p->{svg} =~ s/\$(_ptype_[a-z]+)_\$/mt $1/eg;
+
+ div class => 'mainbox';
+ h1 $title;
+ p class => 'center';
+ lit $p->{svg};
+ end;
+ end;
+ $self->htmlFooter;
+}
+
sub page {
my($self, $pid, $rev) = @_;
my $p = $self->dbProducerGet(
id => $pid,
- what => 'vn extended'.($rev ? ' changes' : ''),
+ what => 'vn extended relations'.($rev ? ' changes' : ''),
$rev ? ( rev => $rev ) : ()
)->[0];
return 404 if !$p->{id};
@@ -31,7 +54,7 @@ sub page {
return if $self->htmlHiddenMessage('p', $p);
if($rev) {
- my $prev = $rev && $rev > 1 && $self->dbProducerGet(id => $pid, rev => $rev-1, what => 'changes extended')->[0];
+ my $prev = $rev && $rev > 1 && $self->dbProducerGet(id => $pid, rev => $rev-1, what => 'changes extended relations')->[0];
$self->htmlRevision('p', $prev, $p,
[ type => serialize => sub { mt "_ptype_$_[0]" } ],
[ name => diff => 1 ],
@@ -40,6 +63,12 @@ sub page {
[ lang => serialize => sub { "$_[0] (".mt("_lang_$_[0]").')' } ],
[ website => diff => 1 ],
[ desc => diff => 1 ],
+ [ relations => join => '<br />', split => sub {
+ my @r = map sprintf('%s: <a href="/p%d" title="%s">%s</a>',
+ mt("_prodrel_$_->{relation}"), $_->{id}, xml_escape($_->{original}||$_->{name}), xml_escape shorten $_->{name}, 40
+ ), sort { $a->{id} <=> $b->{id} } @{$_[0]};
+ return @r ? @r : (mt '_proddiff_none');
+ }],
);
}
@@ -56,6 +85,23 @@ sub page {
}
end;
+ if(@{$p->{relations}}) {
+ my %rel;
+ push @{$rel{$_->{relation}}}, $_
+ for (sort { $a->{name} cmp $b->{name} } @{$p->{relations}});
+ p class => 'center';
+ txt "\n";
+ for my $r (sort keys %rel) {
+ txt mt("_prodrel_$r").': ';
+ for (@{$rel{$r}}) {
+ a href => "/p$_->{id}", title => $_->{original}||$_->{name}, shorten $_->{name}, 40;
+ txt ', ' if $_ ne $rel{$r}[$#{$rel{$r}}];
+ }
+ txt "\n";
+ }
+ end;
+ }
+
if($p->{desc}) {
p class => 'description';
lit bb2html $p->{desc};
@@ -75,6 +121,8 @@ sub page {
lit $self->{l10n}->datestr($_->{date});
end;
a href => "/v$_->{id}", title => $_->{original}, $_->{title};
+ b class => 'grayedout', ' ('.join(', ',
+ $_->{developer} ? mt '_prodpage_dev' : (), $_->{publisher} ? mt '_prodpage_pub' : ()).')';
end;
}
end;
@@ -89,36 +137,55 @@ sub page {
sub edit {
my($self, $pid, $rev) = @_;
- my $p = $pid && $self->dbProducerGet(id => $pid, what => 'changes extended', $rev ? (rev => $rev) : ())->[0];
+ my $p = $pid && $self->dbProducerGet(id => $pid, what => 'changes extended relations', $rev ? (rev => $rev) : ())->[0];
return 404 if $pid && !$p->{id};
$rev = undef if !$p || $p->{cid} == $p->{latest};
return $self->htmlDenied if !$self->authCan('edit')
|| $pid && ($p->{locked} && !$self->authCan('lock') || $p->{hidden} && !$self->authCan('del'));
- my %b4 = !$pid ? () : map { $_ => $p->{$_} } qw|type name original lang website desc alias|;
+ my %b4 = !$pid ? () : (
+ (map { $_ => $p->{$_} } qw|type name original lang website desc alias|),
+ prodrelations => join('|||', map $_->{relation}.','.$_->{id}.','.$_->{name}, sort { $a->{id} <=> $b->{id} } @{$p->{relations}}),
+ );
my $frm;
if($self->reqMethod eq 'POST') {
$frm = $self->formValidate(
- { name => 'type', enum => $self->{producer_types} },
- { name => 'name', maxlength => 200 },
- { name => 'original', required => 0, maxlength => 200, default => '' },
- { name => 'alias', required => 0, maxlength => 500, default => '' },
- { name => 'lang', enum => $self->{languages} },
- { name => 'website', required => 0, template => 'url', default => '' },
- { name => 'desc', required => 0, maxlength => 5000, default => '' },
- { name => 'editsum', maxlength => 5000 },
+ { name => 'type', enum => $self->{producer_types} },
+ { name => 'name', maxlength => 200 },
+ { name => 'original', required => 0, maxlength => 200, default => '' },
+ { name => 'alias', required => 0, maxlength => 500, default => '' },
+ { name => 'lang', enum => $self->{languages} },
+ { name => 'website', required => 0, template => 'url', default => '' },
+ { name => 'desc', required => 0, maxlength => 5000, default => '' },
+ { name => 'prodrelations', required => 0, maxlength => 5000, default => '' },
+ { name => 'editsum', maxlength => 5000 },
);
if(!$frm->{_err}) {
+ # parse
+ my $relations = [ map { /^([a-z]+),([0-9]+),(.+)$/ && (!$pid || $2 != $pid) ? [ $1, $2, $3 ] : () } split /\|\|\|/, $frm->{prodrelations} ];
+
+ # normalize
+ $frm->{prodrelations} = join '|||', map $_->[0].','.$_->[1].','.$_->[2], sort { $a->[1] <=> $b->[1]} @{$relations};
+
return $self->resRedirect("/p$pid", 'post')
if $pid && !grep $frm->{$_} ne $b4{$_}, keys %b4;
+ $frm->{relations} = $relations;
$rev = 1;
+ my $cid;
if($pid) {
- ($rev) = $self->dbProducerEdit($pid, %$frm);
+ ($rev, $cid) = $self->dbProducerEdit($pid, %$frm);
} else {
- ($pid) = $self->dbProducerAdd(%$frm);
+ ($pid, $cid) = $self->dbProducerAdd(%$frm);
+ }
+
+ # update reverse relations
+ if(!$pid && $#$relations >= 0 || $pid && $frm->{prodrelations} ne $b4{prodrelations}) {
+ my %old = $pid ? (map { $_->{id} => $_->{relation} } @{$p->{relations}}) : ();
+ my %new = map { $_->[1] => $_->[0] } @$relations;
+ _updreverse($self, \%old, \%new, $pid, $cid, $rev);
}
return $self->resRedirect("/p$pid.$rev", 'post');
@@ -133,7 +200,8 @@ sub edit {
$self->htmlHeader(title => $title, noindex => 1);
$self->htmlMainTabs('p', $p, 'edit') if $pid;
$self->htmlEditMessage('p', $p, $title);
- $self->htmlForm({ frm => $frm, action => $pid ? "/p$pid/edit" : '/p/new', editsum => 1 }, 'pedit_geninfo' => [mt('_pedit_form_generalinfo'),
+ $self->htmlForm({ frm => $frm, action => $pid ? "/p$pid/edit" : '/p/new', editsum => 1 },
+ 'pedit_geninfo' => [ mt('_pedit_form_generalinfo'),
[ select => name => mt('_pedit_form_type'), short => 'type',
options => [ map [ $_, mt "_ptype_$_" ], sort @{$self->{producer_types}} ] ],
[ input => name => mt('_pedit_form_name'), short => 'name' ],
@@ -145,10 +213,70 @@ sub edit {
options => [ map [ $_, "$_ (".mt("_lang_$_").')' ], sort @{$self->{languages}} ] ],
[ input => name => mt('_pedit_form_website'), short => 'website' ],
[ text => name => mt('_pedit_form_desc').'<br /><b class="standout">'.mt('_inenglish').'</b>', short => 'desc', rows => 6 ],
+ ], 'pedit_rel' => [ mt('_pedit_form_rel'),
+ [ hidden => short => 'prodrelations' ],
+ [ static => nolabel => 1, content => sub {
+ h2 mt '_pedit_rel_sel';
+ table;
+ tbody id => 'relation_tbl';
+ # to be filled using javascript
+ end;
+ end;
+
+ h2 mt '_pedit_rel_add';
+ table;
+ Tr id => 'relation_new';
+ td class => 'tc_prod';
+ input type => 'text', class => 'text';
+ end;
+ td class => 'tc_rel';
+ Select;
+ option value => $_, mt "_prodrel_$_"
+ for (sort { $self->{prod_relations}{$a}[0] <=> $self->{prod_relations}{$b}[0] } keys %{$self->{prod_relations}});
+ end;
+ end;
+ td class => 'tc_add';
+ a href => '#', mt '_pedit_rel_addbut';
+ end;
+ end;
+ end;
+ }],
]);
$self->htmlFooter;
}
+# !IMPORTANT!: Don't forget to update this function when
+# adding/removing fields to/from producer entries!
+sub _updreverse {
+ my($self, $old, $new, $pid, $cid, $rev) = @_;
+ my %upd;
+
+ # compare %old and %new
+ for (keys %$old, keys %$new) {
+ if(exists $$old{$_} and !exists $$new{$_}) {
+ $upd{$_} = undef;
+ } elsif((!exists $$old{$_} and exists $$new{$_}) || ($$old{$_} ne $$new{$_})) {
+ $upd{$_} = $self->{prod_relations}{$$new{$_}}[1];
+ }
+ }
+
+ return if !keys %upd;
+
+ # edit all related producers
+ for my $i (keys %upd) {
+ my $r = $self->dbProducerGet(id => $i, what => 'extended relations')->[0];
+ my @newrel = map $_->{id} != $pid ? [ $_->{relation}, $_->{id} ] : (), @{$r->{relations}};
+ push @newrel, [ $upd{$i}, $pid ] if $upd{$i};
+ $self->dbProducerEdit($i,
+ relations => \@newrel,
+ editsum => "Reverse relation update caused by revision p$pid.$rev",
+ causedby => $cid,
+ uid => 1, # Multi - hardcoded
+ ( map { $_ => $r->{$_} } qw|type name original lang website desc alias| )
+ );
+ }
+}
+
sub list {
my($self, $char) = @_;
diff --git a/lib/VNDB/Handler/Releases.pm b/lib/VNDB/Handler/Releases.pm
index d916e056..7365f235 100644
--- a/lib/VNDB/Handler/Releases.pm
+++ b/lib/VNDB/Handler/Releases.pm
@@ -54,17 +54,16 @@ sub page {
[ notes => diff => 1 ],
[ platforms => join => ', ', split => sub { map mt("_plat_$_"), @{$_[0]} } ],
[ media => join => ', ', split => sub {
- map {
- my $med = $self->{media}{$_->{medium}};
- $med->[1] ? sprintf('%d %s%s', $_->{qty}, $med->[0], $_->{qty}>1?'s':'') : $med->[0]
- } @{$_[0]};
+ map $self->{media}{$_->{medium}} ? $_->{qty}.' '.mt("_med_$_->{medium}", $_->{qty}) : mt("_med_$_->{medium}",1), @{$_[0]}
} ],
[ resolution => serialize => sub { $self->{resolutions}[$_[0]][0] } ],
[ voiced => serialize => sub { mt '_voiced_'.$_[0] } ],
[ ani_story => serialize => sub { mt '_animated_'.$_[0] } ],
[ ani_ero => serialize => sub { mt '_animated_'.$_[0] } ],
[ producers => join => '<br />', split => sub {
- map sprintf('<a href="/p%d" title="%s">%s</a>', $_->{id}, $_->{original}||$_->{name}, shorten $_->{name}, 50), @{$_[0]};
+ map sprintf('<a href="/p%d" title="%s">%s</a> (%s)', $_->{id}, $_->{original}||$_->{name}, shorten($_->{name}, 50),
+ join(', ', $_->{developer} ? mt '_reldiff_developer' :(), $_->{publisher} ? mt '_reldiff_publisher' :())
+ ), @{$_[0]};
} ],
);
}
@@ -154,11 +153,9 @@ sub _infotable {
if(@{$r->{media}}) {
Tr ++$i % 2 ? (class => 'odd') : ();
td mt '_relinfo_media', scalar @{$r->{media}};
- # TODO: TL the media
- td join ', ', map {
- my $med = $self->{media}{$_->{medium}};
- $med->[1] ? sprintf('%d %s%s', $_->{qty}, $med->[0], $_->{qty}>1?'s':'') : $med->[0]
- } @{$r->{media}};
+ td join ', ', map
+ $self->{media}{$_->{medium}} ? $_->{qty}.' '.mt("_med_$_->{medium}", $_->{qty}) : mt("_med_$_->{medium}",1),
+ @{$r->{media}};
end;
}
@@ -199,16 +196,19 @@ sub _infotable {
end;
}
- if(@{$r->{producers}}) {
- Tr ++$i % 2 ? (class => 'odd') : ();
- td mt '_relinfo_producer', scalar @{$r->{producers}};
- td;
- for (@{$r->{producers}}) {
- a href => "/p$_->{id}", title => $_->{original}||$_->{name}, shorten $_->{name}, 60;
- br if $_ != $r->{producers}[$#{$r->{producers}}];
- }
- end;
- end;
+ for my $t (qw|developer publisher|) {
+ my @prod = grep $_->{$t}, @{$r->{producers}};
+ if(@prod) {
+ Tr ++$i % 2 ? (class => 'odd') : ();
+ td mt "_relinfo_$t", scalar @prod;
+ td;
+ for (@prod) {
+ a href => "/p$_->{id}", title => $_->{original}||$_->{name}, shorten $_->{name}, 60;
+ br if $_ != $prod[$#prod];
+ }
+ end;
+ end;
+ }
}
if($r->{gtin}) {
@@ -241,14 +241,14 @@ sub _infotable {
td;
Select id => 'listsel', name => 'listsel';
option mt !$rl ? '_relinfo_user_notlist' :
- ('_relinfo_user_inlist', $self->{vn_rstat}[$rl->{rstat}], $self->{vn_vstat}[$rl->{vstat}]);
+ ('_relinfo_user_inlist', mt('_rlst_rstat_'.$rl->{rstat}), mt('_rlst_vstat_'.$rl->{vstat}));
optgroup label => mt '_relinfo_user_setr';
- option value => "r$_", $self->{vn_rstat}[$_]
- for (0..$#{$self->{vn_rstat}});
+ option value => "r$_", mt '_rlst_rstat_'.$_
+ for (@{$self->{rlst_rstat}});
end;
optgroup label => mt '_relinfo_user_setv';
- option value => "v$_", $self->{vn_vstat}[$_]
- for (0..$#{$self->{vn_vstat}});
+ option value => "v$_", mt '_rlst_vstat_'.$_
+ for (@{$self->{rlst_vstat}});
end;
option value => 'del', mt '_relinfo_user_del' if $rl;
end;
@@ -289,7 +289,10 @@ sub edit {
(map { $_ => $r->{$_} } qw|type title original gtin catalog languages website released
notes minage platforms patch resolution voiced freeware doujin ani_story ani_ero|),
media => join(',', sort map "$_->{medium} $_->{qty}", @{$r->{media}}),
- producers => join('|||', map "$_->{id},$_->{name}", sort { $a->{id} <=> $b->{id} } @{$r->{producers}}),
+ producers => join('|||', map
+ sprintf('%d,%d,%s', $_->{id}, ($_->{developer}?1:0)+($_->{publisher}?2:0), $_->{name}),
+ sort { $a->{id} <=> $b->{id} } @{$r->{producers}}
+ ),
);
$b4{vn} = join('|||', map "$_->{vid},$_->{title}", @$vn);
my $frm;
@@ -325,7 +328,7 @@ sub edit {
if(!$frm->{_err}) {
# de-serialize
$media = [ map [ split / / ], split /,/, $frm->{media} ];
- $producers = [ map { /^([0-9]+)/ ? $1 : () } split /\|\|\|/, $frm->{producers} ];
+ $producers = [ map { /^([0-9]+),([1-3])/ ? [ $1, $2&1?1:0, $2&2?1:0] : () } split /\|\|\|/, $frm->{producers} ];
$new_vn = [ map { /^([0-9]+)/ ? $1 : () } split /\|\|\|/, $frm->{vn} ];
$frm->{platforms} = [ grep $_, @{$frm->{platforms}} ];
$frm->{$_} = $frm->{$_} ? 1 : 0 for (qw|patch freeware doujin|);
@@ -335,7 +338,7 @@ sub edit {
my $same = $rid &&
(join(',', sort @{$b4{platforms}}) eq join(',', sort @{$frm->{platforms}})) &&
- (join(',', sort @$producers) eq join(',', sort map $_->{id}, @{$r->{producers}})) &&
+ (join(',', map join(' ', @$_), sort { $a->[0] <=> $b->[0] } @$producers) eq join(',', sort map sprintf('%d %d %d',$_->{id}, $_->{developer}?1:0, $_->{publisher}?1:0), sort { $a->{id} <=> $b->{id} } @{$r->{producers}})) &&
(join(',', sort @$new_vn) eq join(',', sort map $_->{vid}, @$vn)) &&
(join(',', sort @{$b4{languages}}) eq join(',', sort @{$frm->{languages}})) &&
!grep !/^(platforms|producers|vn|languages)$/ && $frm->{$_} ne $b4{$_}, keys %b4;
@@ -370,7 +373,7 @@ sub edit {
$frm->{original} = $v->{original} if !defined $frm->{original} && !$r;
my $title = mt $rid ? ($copy ? '_redit_title_copy' : '_redit_title_edit', $r->{title}) : ('_redit_title_add', $v->{title});
- $self->htmlHeader(js => 'forms', title => $title, noindex => 1);
+ $self->htmlHeader(title => $title, noindex => 1);
$self->htmlMainTabs('r', $r, $copy ? 'copy' : 'edit') if $rid;
$self->htmlMainTabs('v', $v, 'edit') if $vid;
$self->htmlEditMessage('r', $r, $title, $copy);
@@ -435,7 +438,7 @@ sub _form {
h2 mt '_redit_form_media';
div id => 'media_div';
Select;
- option value => $_, class => $self->{media}{$_}[1] ? 'qty' : 'noqty', $self->{media}{$_}[0]
+ option value => $_, class => $self->{media}{$_} ? 'qty' : 'noqty', mt "_med_$_", 1
for (sort keys %{$self->{media}});
end;
end;
@@ -446,13 +449,17 @@ sub _form {
[ hidden => short => 'producers' ],
[ static => nolabel => 1, content => sub {
h2 mt('_redit_form_prod_sel');
- div id => 'producerssel';
- end;
+ table; tbody id => 'producer_tbl'; end; end;
h2 mt('_redit_form_prod_add');
- div;
- input type => 'text', class => 'text';
- a href => '#', 'add';
- end;
+ table; Tr;
+ td class => 'tc_name'; input id => 'producer_input', type => 'text', class => 'text'; end;
+ td class => 'tc_role'; Select id => 'producer_role';
+ option value => 1, mt '_redit_form_prod_dev';
+ option value => 2, selected => 'selected', mt '_redit_form_prod_pub';
+ option value => 3, mt '_redit_form_prod_both';
+ end; end;
+ td class => 'tc_add'; a id => 'producer_add', href => '#', mt '_redit_form_prod_addbut'; end;
+ end; end;
}],
],
@@ -460,12 +467,11 @@ sub _form {
[ hidden => short => 'vn' ],
[ static => nolabel => 1, content => sub {
h2 mt('_redit_form_vn_sel');
- div id => 'vnsel';
- end;
+ table; tbody id => 'vn_tbl'; end; end;
h2 mt('_redit_form_vn_add');
div;
- input type => 'text', class => 'text';
- a href => '#', 'add';
+ input id => 'vn_input', type => 'text', class => 'text';
+ a href => '#', id => 'vn_add', mt '_redit_form_vn_addbut';
end;
}],
],
@@ -484,7 +490,7 @@ sub browse {
{ name => 'ln', required => 0, multi => 1, default => '', enum => $self->{languages} },
{ name => 'pl', required => 0, multi => 1, default => '', enum => $self->{platforms} },
{ name => 'me', required => 0, multi => 1, default => '', enum => [ keys %{$self->{media}} ] },
- { name => 'tp', required => 0, default => -1, enum => [ -1, @{$self->{release_types}} ] },
+ { name => 'tp', required => 0, default => '', enum => [ '', @{$self->{release_types}} ] },
{ name => 'pa', required => 0, default => 0, enum => [ 0..2 ] },
{ name => 'fw', required => 0, default => 0, enum => [ 0..2 ] },
{ name => 'do', required => 0, default => 0, enum => [ 0..2 ] },
@@ -503,7 +509,7 @@ sub browse {
$f->{ln}[0] ? (languages => $f->{ln}) : (),
$f->{me}[0] ? (media => $f->{me}) : (),
$f->{re}[0] ? (resolutions => $f->{re} ) : (),
- $f->{tp} >= 0 ? (type => $f->{tp}) : (),
+ $f->{tp} ? (type => $f->{tp}) : (),
$f->{ma_a} || $f->{ma_m} ? (minage => [$f->{ma_m}, $f->{ma_a}]) : (),
$f->{pa} ? (patch => $f->{pa}) : (),
$f->{fw} ? (freeware => $f->{fw}) : (),
@@ -614,7 +620,7 @@ sub _filters {
end;
end;
$self->htmlFormPart($f, [ select => short => 'tp', name => mt('_rbrowse_type'),
- options => [ [-1, mt '_rbrowse_all'], map [ $_, mt "_rtype_$_" ], @{$self->{release_types}} ]]);
+ options => [ ['', mt '_rbrowse_all'], map [ $_, mt "_rtype_$_" ], @{$self->{release_types}} ]]);
$self->htmlFormPart($f, [ select => short => 'pa', name => mt('_rbrowse_patch'),
options => [ [0, mt '_rbrowse_all' ], [1, mt '_rbrowse_patchonly'], [2, mt '_rbrowse_patchnone']]]);
$self->htmlFormPart($f, [ select => short => 'fw', name => mt('_rbrowse_freeware'),
@@ -629,7 +635,7 @@ sub _filters {
txt mt '_rbrowse_languages';
b ' ('.mt('_rbrowse_boolor').')';
end;
- for my $i (sort @{$self->dbLanguages}) {
+ for my $i (@{$self->{languages}}) {
span;
input type => 'checkbox', name => 'ln', value => $i, id => "lang_$i", grep($_ eq $i, @{$f->{ln}}) ? (checked => 'checked') : ();
label for => "lang_$i";
@@ -660,7 +666,7 @@ sub _filters {
for my $i (sort keys %{$self->{media}}) {
span;
input type => 'checkbox', name => 'me', value => $i, id => "med_$i", grep($_ eq $i, @{$f->{me}}) ? (checked => 'checked') : ();
- label for => "med_$i", $self->{media}{$i}[0];
+ label for => "med_$i", mt "_med_$i", 1;
end;
}
diff --git a/lib/VNDB/Handler/Tags.pm b/lib/VNDB/Handler/Tags.pm
index ae240d38..4c969436 100644
--- a/lib/VNDB/Handler/Tags.pm
+++ b/lib/VNDB/Handler/Tags.pm
@@ -36,7 +36,7 @@ sub tagpage {
);
return 404 if $f->{_err};
my $tagspoil = $self->reqCookie('tagspoil');
- $f->{m} = $tagspoil =~ /^[0-2]$/ ? $tagspoil : 1 if $f->{m} == -1;
+ $f->{m} = $tagspoil =~ /^[0-2]$/ ? $tagspoil : 0 if $f->{m} == -1;
my($list, $np) = $t->{meta} || $t->{state} != 2 ? ([],0) : $self->dbTagVNs(
tag => $tag,
@@ -422,7 +422,7 @@ sub vntagmod {
my $frm;
my $title = mt '_tagv_title', $v->{title};
- $self->htmlHeader(title => $title, noindex => 1, js => 'forms');
+ $self->htmlHeader(title => $title, noindex => 1);
$self->htmlMainTabs('v', $v, 'tagmod');
div class => 'mainbox';
h1 $title;
@@ -438,43 +438,44 @@ sub vntagmod {
$self->htmlForm({ frm => $frm, action => "/v$vid/tagmod", nosubmit => 1 }, tagmod => [ mt('_tagv_frm_title'),
[ hidden => short => 'taglinks', value => '' ],
[ static => nolabel => 1, content => sub {
- table id => 'tagtable';
+ table class => 'tgl';
thead;
Tr;
td '';
- td colspan => 2, class => 'tc2_1', mt '_tagv_col_you';
- td colspan => 2, class => 'tc3_1', mt '_tagv_col_others';
+ td colspan => 2, class => 'tc_you', mt '_tagv_col_you';
+ td colspan => 2, class => 'tc_others', mt '_tagv_col_others';
end;
Tr;
- my $i=0;
- td class => 'tc'.++$i, mt '_tagv_col_'.$_ for(qw|tag rating spoiler rating spoiler|);
+ td class => 'tc_tagname', mt '_tagv_col_tag';
+ td class => 'tc_myvote', mt '_tagv_col_rating';
+ td class => 'tc_myspoil', mt '_tagv_col_spoiler';
+ td class => 'tc_allvote', mt '_tagv_col_rating';
+ td class => 'tc_allspoil', mt '_tagv_col_spoiler';
end;
end;
tfoot; Tr;
td colspan => 5;
input type => 'submit', class => 'submit', value => mt('_tagv_save'), style => 'float: right';
- input type => 'text', class => 'text', name => 'addtag', value => '';
- input type => 'button', class => 'submit', value => mt '_tagv_add';
+ input id => 'tagmod_tag', type => 'text', class => 'text', value => '';
+ input id => 'tagmod_add', type => 'button', class => 'submit', value => mt '_tagv_add';
br;
p;
lit mt '_tagv_addmsg';
end;
end;
end; end;
- tbody;
+ tbody id => 'tagtable';
for my $t (sort { $a->{name} cmp $b->{name} } @$tags) {
my $m = (grep $_->{tag} == $t->{id}, @$my)[0] || {};
- Tr;
- td class => 'tc1';
- a href => "/g$t->{id}", $t->{name};
- end;
- td class => 'tc2', $m->{vote}||0;
- td class => 'tc3', defined $m->{spoiler} ? $m->{spoiler} : -1;
- td class => 'tc4';
+ Tr id => "tgl_$t->{id}";
+ td class => 'tc_tagname'; a href => "/g$t->{id}", $t->{name}; end;
+ td class => 'tc_myvote', $m->{vote}||0;
+ td class => 'tc_myspoil', defined $m->{spoiler} ? $m->{spoiler} : -1;
+ td class => 'tc_allvote';
tagscore !$m->{vote} ? $t->{rating} : $t->{cnt} == 1 ? 0 : ($t->{rating}*$t->{cnt} - $m->{vote}) / ($t->{cnt}-1);
i ' ('.($t->{cnt} - ($m->{vote} ? 1 : 0)).')';
end;
- td class => 'tc5', sprintf '%.2f', $t->{spoiler};
+ td class => 'tc_allspoil', sprintf '%.2f', $t->{spoiler};
end;
}
end;
@@ -529,7 +530,7 @@ sub usertags {
header => [
sub {
td class => 'tc1';
- b id => 'relhidall';
+ b id => 'expandall';
lit '<i>&#9656;</i> '.mt('_tagu_col_num').' ';
end;
lit $f->{s} eq 'cnt' && $f->{o} eq 'a' ? "\x{25B4}" : qq|<a href="/u$u->{id}/tags?o=a;s=cnt">\x{25B4}</a>|;
@@ -542,7 +543,7 @@ sub usertags {
row => sub {
my($s, $n, $l) = @_;
Tr $n % 2 ? (class => 'odd') : ();
- td class => 'tc1 relhid_but', id => "tag$l->{id}";
+ td class => 'tc1 collapse_but', id => "tag$l->{id}";
lit "<i>&#9656;</i> $l->{cnt}";
end;
td class => 'tc2', colspan => 2;
@@ -550,7 +551,7 @@ sub usertags {
end;
end;
for(@{$l->{vns}}) {
- Tr class => "relhid tag$l->{id}";
+ Tr class => "collapse collapse_tag$l->{id}";
td class => 'tc1_1';
tagscore $_->{vote};
end;
@@ -652,7 +653,7 @@ sub tagxml {
my($list, $np) = $self->dbTagGet(
$q =~ /^g([1-9]\d*)/ ? (id => $1) : $q =~ /^name:(.+)$/ ? (name => $1) : (search => $q),
- results => 10,
+ results => 15,
page => 1,
);
diff --git a/lib/VNDB/Handler/ULists.pm b/lib/VNDB/Handler/ULists.pm
index 7d1a9304..6a0d6c9e 100644
--- a/lib/VNDB/Handler/ULists.pm
+++ b/lib/VNDB/Handler/ULists.pm
@@ -69,7 +69,7 @@ sub rlist {
return $self->htmlDenied() if !$uid;
my $f = $self->formValidate(
- { name => 'e', required => 1, enum => [ 'del', map("r$_", 0..$#{$self->{vn_rstat}}), map("v$_", 0..$#{$self->{vn_vstat}}) ] },
+ { name => 'e', required => 1, enum => [ 'del', map("r$_", @{$self->{rlst_rstat}}), map("v$_", @{$self->{rlst_vstat}}) ] },
);
return 404 if $f->{_err};
@@ -212,7 +212,7 @@ sub vnlist {
if($own && $self->reqMethod eq 'POST') {
my $frm = $self->formValidate(
{ name => 'sel', required => 0, default => 0, multi => 1, template => 'int' },
- { name => 'batchedit', required => 1, enum => [ 'del', map("r$_", 0..$#{$self->{vn_rstat}}), map("v$_", 0..$#{$self->{vn_vstat}}) ] },
+ { name => 'batchedit', required => 1, enum => [ 'del', map("r$_", @{$self->{rlst_rstat}}), map("v$_", @{$self->{rlst_vstat}}) ] },
);
if(!$frm->{_err} && @{$frm->{sel}} && $frm->{sel}[0]) {
$self->dbVNListDel($uid, $frm->{sel}) if $frm->{batchedit} eq 'del';
@@ -229,7 +229,7 @@ sub vnlist {
uid => $uid,
results => 50,
page => $f->{p},
- order => $f->{s}.' '.($f->{o} eq 'd' ? 'DESC' : 'ASC'),
+ order => $f->{s}.' '.($f->{o} eq 'd' ? 'DESC' : 'ASC').($f->{s} eq 'vote' ? ', title ASC' : ''),
voted => $f->{v},
$f->{c} ne 'all' ? (char => $f->{c}) : (),
);
@@ -285,7 +285,7 @@ sub _vnlist_browse {
pageurl => $url->('page'),
header => [
[ mt('_rlist_col_title') => 'title', 3 ],
- sub { td class => 'tc2', id => 'relhidall'; lit '<i>&#9656;</i>'.mt('_rlist_col_releases').'*'; end; },
+ sub { td class => 'tc2', id => 'expandall'; lit '<i>&#9656;</i>'.mt('_rlist_col_releases').'*'; end; },
[ mt('_rlist_col_vote') => 'vote' ],
],
row => sub {
@@ -294,7 +294,7 @@ sub _vnlist_browse {
td class => 'tc1', colspan => 3;
a href => "/v$i->{vid}", title => $i->{original}||$i->{title}, shorten $i->{title}, 70;
end;
- td class => 'tc2'.(@{$i->{rels}} ? ' relhid_but' : ''), id => 'vid'.$i->{vid};
+ td class => 'tc2'.(@{$i->{rels}} ? ' collapse_but' : ''), id => 'vid'.$i->{vid};
lit '<i>&#9656;</i>';
my $obtained = grep $_->{rstat}==2, @{$i->{rels}};
my $finished = grep $_->{vstat}==2, @{$i->{rels}};
@@ -307,7 +307,7 @@ sub _vnlist_browse {
end;
for (@{$i->{rels}}) {
- Tr class => "relhid vid$i->{vid}";
+ Tr class => "collapse relhid collapse_vid$i->{vid}";
td class => 'tc1'.($own ? ' own' : '');
input type => 'checkbox', name => 'sel', value => $_->{rid}
if $own;
@@ -333,12 +333,12 @@ sub _vnlist_browse {
Select id => 'batchedit', name => 'batchedit';
option mt '_rlist_selection';
optgroup label => mt '_rlist_changerel';
- option value => "r$_", $self->{vn_rstat}[$_]
- for (0..$#{$self->{vn_rstat}});
+ option value => "r$_", mt "_rlst_rstat_$_"
+ for (@{$self->{rlst_rstat}});
end;
optgroup label => mt '_rlist_changeplay';
- option value => "v$_", $self->{vn_vstat}[$_]
- for (0..$#{$self->{vn_vstat}});
+ option value => "v$_", mt "_rlst_vstat_$_"
+ for (@{$self->{rlst_vstat}});
end;
option value => 'del', mt '_rlist_del';
end;
diff --git a/lib/VNDB/Handler/Users.pm b/lib/VNDB/Handler/Users.pm
index d6193ddc..39aecbfc 100644
--- a/lib/VNDB/Handler/Users.pm
+++ b/lib/VNDB/Handler/Users.pm
@@ -267,7 +267,7 @@ sub edit {
return $self->htmlDenied if !$self->authInfo->{id} || $self->authInfo->{id} != $uid && !$self->authCan('usermod');
# fetch user info (cached if uid == loggedin uid)
- my $u = $self->authInfo->{id} == $uid ? $self->authInfo : $self->dbUserGet(uid => $uid)->[0];
+ my $u = $self->authInfo->{id} == $uid ? $self->authInfo : $self->dbUserGet(uid => $uid, what => 'extended')->[0];
return 404 if !$u->{id};
# check POST data
@@ -386,7 +386,7 @@ sub posts {
[ '' ],
[ '' ],
[ mt '_uposts_col_date' ],
- sub { td; a href => '#', id => 'history_comments', 'expand'; txt mt '_uposts_col_title'; end; }
+ sub { td; a href => '#', id => 'expandlist', mt '_js_expand'; txt mt '_uposts_col_title'; end; }
],
row => sub {
my($s, $n, $l) = @_;
@@ -396,7 +396,7 @@ sub posts {
td class => 'tc3', $self->{l10n}->date($l->{date});
td class => 'tc4'; a href => "/t$l->{tid}.$l->{num}", $l->{title}; end;
end;
- Tr class => $n % 2 ? 'editsum odd hidden' : 'editsum hidden';
+ Tr class => $n % 2 ? 'collapse msgsum odd hidden' : 'collapse msgsum hidden';
td colspan => 4;
lit bb2html $l->{msg}, 150;
end;
diff --git a/lib/VNDB/Handler/VNBrowse.pm b/lib/VNDB/Handler/VNBrowse.pm
index 2a6d6cd7..bca21151 100644
--- a/lib/VNDB/Handler/VNBrowse.pm
+++ b/lib/VNDB/Handler/VNBrowse.pm
@@ -25,7 +25,7 @@ sub list {
{ name => 'pl', required => 0, multi => 1, enum => $self->{platforms}, default => '' },
{ name => 'ti', required => 0, default => '', maxlength => 200 },
{ name => 'te', required => 0, default => '', maxlength => 200 },
- { name => 'sp', required => 0, default => $self->reqCookie('tagspoil') =~ /^([0-2])$/ ? $1 : 1, enum => [0..2] },
+ { name => 'sp', required => 0, default => $self->reqCookie('tagspoil') =~ /^([0-2])$/ ? $1 : 0, enum => [0..2] },
);
return 404 if $f->{_err};
$f->{q} ||= $f->{sq};
@@ -70,7 +70,7 @@ sub list {
$self->resRedirect('/v'.$list->[0]{id}, 'temp')
if $f->{q} && @$list == 1;
- $self->htmlHeader(title => mt('_vnbrowse_title'), search => $f->{q}, js => 'forms');
+ $self->htmlHeader(title => mt('_vnbrowse_title'), search => $f->{q});
_filters($self, $f, $char, \@ignored);
my $url = "/v/$char?q=$f->{q};ti=$f->{ti};te=$f->{te}";
@@ -162,7 +162,7 @@ sub _filters {
txt mt '_vnbrowse_lang';
b ' ('.mt('_vnbrowse_boolor').')';
end;
- for my $i (sort @{$self->dbLanguages}) {
+ for my $i (@{$self->{languages}}) {
span;
input type => 'checkbox', name => 'ln', value => $i, id => "lang_$i",
(scalar grep $_ eq $i, @{$f->{ln}}) ? (checked => 'checked') : ();
diff --git a/lib/VNDB/Handler/VNEdit.pm b/lib/VNDB/Handler/VNEdit.pm
index f09d6ddd..ed7068dc 100644
--- a/lib/VNDB/Handler/VNEdit.pm
+++ b/lib/VNDB/Handler/VNEdit.pm
@@ -28,7 +28,7 @@ sub edit {
my %b4 = !$vid ? () : (
(map { $_ => $v->{$_} } qw|title original desc alias length l_wp l_encubed l_renai l_vnn img_nsfw|),
anime => join(' ', sort { $a <=> $b } map $_->{id}, @{$v->{anime}}),
- relations => join('|||', map $_->{relation}.','.$_->{id}.','.$_->{title}, sort { $a->{id} <=> $b->{id} } @{$v->{relations}}),
+ vnrelations => join('|||', map $_->{relation}.','.$_->{id}.','.$_->{title}, sort { $a->{id} <=> $b->{id} } @{$v->{relations}}),
screenshots => join(' ', map sprintf('%d,%d,%d', $_->{id}, $_->{nsfw}?1:0, $_->{rid}), @{$v->{screenshots}}),
);
@@ -46,7 +46,7 @@ sub edit {
{ name => 'l_vnn', required => 0, default => $b4{l_vnn}||0, template => 'int' },
{ name => 'anime', required => 0, default => '' },
{ name => 'img_nsfw', required => 0, default => 0 },
- { name => 'relations', required => 0, default => '', maxlength => 5000 },
+ { name => 'vnrelations', required => 0, default => '', maxlength => 5000 },
{ name => 'screenshots', required => 0, default => '', maxlength => 1000 },
{ name => 'editsum', maxlength => 5000 },
);
@@ -57,11 +57,11 @@ sub edit {
if(!$frm->{_err}) {
# parse and re-sort fields that have multiple representations of the same information
my $anime = { map +($_=>1), grep /^[0-9]+$/, split /[ ,]+/, $frm->{anime} };
- my $relations = [ map { /^([0-9]+),([0-9]+),(.+)$/ && (!$vid || $2 != $vid) ? [ $1, $2, $3 ] : () } split /\|\|\|/, $frm->{relations} ];
+ my $relations = [ map { /^([a-z]+),([0-9]+),(.+)$/ && (!$vid || $2 != $vid) ? [ $1, $2, $3 ] : () } split /\|\|\|/, $frm->{vnrelations} ];
my $screenshots = [ map /^[0-9]+,[01],[0-9]+$/ ? [split /,/] : (), split / +/, $frm->{screenshots} ];
$frm->{anime} = join ' ', sort { $a <=> $b } keys %$anime;
- $frm->{relations} = join '|||', map $_->[0].','.$_->[1].','.$_->[2], sort { $a->[1] <=> $b->[1]} @{$relations};
+ $frm->{vnrelations} = join '|||', map $_->[0].','.$_->[1].','.$_->[2], sort { $a->[1] <=> $b->[1]} @{$relations};
$frm->{img_nsfw} = $frm->{img_nsfw} ? 1 : 0;
$frm->{screenshots} = join ' ', map sprintf('%d,%d,%d', $_->[0], $_->[1]?1:0, $_->[2]), sort { $a->[0] <=> $b->[0] } @$screenshots;
@@ -83,7 +83,7 @@ sub edit {
($nvid, $cid) = $self->dbVNAdd(%args) if !$vid;
# update reverse relations & relation graph
- if(!$vid && $#$relations >= 0 || $vid && $frm->{relations} ne $b4{relations}) {
+ if(!$vid && $#$relations >= 0 || $vid && $frm->{vnrelations} ne $b4{vnrelations}) {
my %old = $vid ? (map { $_->{id} => $_->{relation} } @{$v->{relations}}) : ();
my %new = map { $_->[1] => $_->[0] } @$relations;
_updreverse($self, \%old, \%new, $nvid, $cid, $nrev);
@@ -97,7 +97,7 @@ sub edit {
$frm->{editsum} = sprintf 'Reverted to revision v%d.%d', $vid, $rev if $rev && !defined $frm->{editsum};
my $title = $vid ? mt('_vnedit_title_edit', $v->{title}) : mt '_vnedit_title_add';
- $self->htmlHeader(js => 'forms', title => $title, noindex => 1);
+ $self->htmlHeader(title => $title, noindex => 1);
$self->htmlMainTabs('v', $v, 'edit') if $vid;
$self->htmlEditMessage('v', $v, $title);
_form($self, $v, $frm);
@@ -183,7 +183,7 @@ sub _form {
],
vn_rel => [ mt('_vnedit_rel'),
- [ hidden => short => 'relations' ],
+ [ hidden => short => 'vnrelations' ],
[ static => nolabel => 1, content => sub {
h2 mt '_vnedit_rel_sel';
table;
@@ -193,22 +193,22 @@ sub _form {
end;
h2 mt '_vnedit_rel_add';
- # TODO: localize JS relartion selector
table;
Tr id => 'relation_new';
- td class => 'tc1';
+ td class => 'tc_vn';
input type => 'text', class => 'text';
end;
- td class => 'tc2';
- txt ' is a ';
+ td class => 'tc_rel';
+ txt mt('_vnedit_rel_isa').' ';
Select;
- option value => $_, $self->{vn_relations}[$_][0] for (0..$#{$self->{vn_relations}});
+ option value => $_, mt "_vnrel_$_"
+ for (sort { $self->{vn_relations}{$a}[0] <=> $self->{vn_relations}{$b}[0] } keys %{$self->{vn_relations}});
end;
- txt ' of';
+ txt ' '.mt '_vnedit_rel_of';
end;
- td class => 'tc3', $v ? $v->{title} : '';
- td class => 'tc4';
- a href => '#', 'add';
+ td class => 'tc_title', $v ? $v->{title} : '';
+ td class => 'tc_add';
+ a href => '#', mt '_vnedit_rel_addbut';
end;
end;
end;
@@ -219,10 +219,9 @@ sub _form {
[ hidden => short => 'screenshots' ],
[ static => nolabel => 1, content => sub {
div class => 'warning';
- lit mt '_vnedit_scr_msg';
+ lit mt '_vnedit_scrmsg';
end;
br;
- # TODO: localize screenshot uploader
table;
tbody id => 'scr_table', '';
end;
@@ -250,11 +249,9 @@ sub _updreverse {
# compare %old and %new
for (keys %$old, keys %$new) {
if(exists $$old{$_} and !exists $$new{$_}) {
- $upd{$_} = -1;
- } elsif((!exists $$old{$_} and exists $$new{$_}) || ($$old{$_} != $$new{$_})) {
- $upd{$_} = $$new{$_};
- if ($self->{vn_relations}[$upd{$_} ][1]) { $upd{$_}-- }
- elsif($self->{vn_relations}[$upd{$_}+1][1]) { $upd{$_}++ }
+ $upd{$_} = undef;
+ } elsif((!exists $$old{$_} and exists $$new{$_}) || ($$old{$_} ne $$new{$_})) {
+ $upd{$_} = $self->{vn_relations}{$$new{$_}}[1];
}
}
@@ -264,7 +261,7 @@ sub _updreverse {
for my $i (keys %upd) {
my $r = $self->dbVNGet(id => $i, what => 'extended relations anime screenshots')->[0];
my @newrel = map $_->{id} != $vid ? [ $_->{relation}, $_->{id} ] : (), @{$r->{relations}};
- push @newrel, [ $upd{$i}, $vid ] if $upd{$i} != -1;
+ push @newrel, [ $upd{$i}, $vid ] if $upd{$i};
$self->dbVNEdit($i,
relations => \@newrel,
editsum => "Reverse relation update caused by revision v$vid.$rev",
diff --git a/lib/VNDB/Handler/VNPage.pm b/lib/VNDB/Handler/VNPage.pm
index 361963a8..ec5f4afc 100644
--- a/lib/VNDB/Handler/VNPage.pm
+++ b/lib/VNDB/Handler/VNPage.pm
@@ -27,16 +27,17 @@ sub rg {
return 404 if !$v->{id} || !$v->{rgraph};
my $title = mt '_vnrg_title', $v->{title};
- $self->htmlHeader(title => $title);
- $self->htmlMainTabs('v', $v, 'rg');
+ return if $self->htmlRGHeader($title, 'v', $v);
+
+ $v->{svg} =~ s/\$___(_vnrel_[a-z]+)____\$/mt $1/eg;
+
div class => 'mainbox';
h1 $title;
- lit $v->{cmap};
p class => 'center';
- img src => sprintf('%s/rg/%02d/%d.png', $self->{url_static}, $v->{rgraph}%100, $v->{rgraph}),
- alt => $title, usemap => '#rgraph';
+ lit $v->{svg};
end;
end;
+ $self->htmlFooter;
}
@@ -148,15 +149,16 @@ sub page {
my $t = $self->dbTagStats(vid => $v->{id}, order => 'avg(tv.vote) DESC', minrating => 0, results => 999);
if(@$t) {
div id => 'tagops';
- a href => '#', mt '_vnpage_tags_spoil0';
- a href => '#', class => 'tsel', mt '_vnpage_tags_spoil1';
+ # NOTE: order of these links is hardcoded in JS
+ a href => '#', class => 'tsel', mt '_vnpage_tags_spoil0';
+ a href => '#', mt '_vnpage_tags_spoil1';
a href => '#', mt '_vnpage_tags_spoil2';
a href => '#', class => 'sec', mt '_vnpage_tags_summary';
a href => '#', mt '_vnpage_tags_all';
end;
div id => 'vntags';
for (@$t) {
- span class => sprintf 'tagspl%.0f %s', $_->{spoiler}, $_->{spoiler} > 1 ? 'hidden' : '';
+ span class => sprintf 'tagspl%.0f %s', $_->{spoiler}, $_->{spoiler} > 0 ? 'hidden' : '';
a href => "/g$_->{id}", style => sprintf('font-size: %dpx', $_->{rating}*3.5+6), $_->{name};
b class => 'grayedout', sprintf ' %.1f', $_->{rating};
end;
@@ -199,7 +201,7 @@ sub _revision {
}],
[ relations => join => '<br />', split => sub {
my @r = map sprintf('%s: <a href="/v%d" title="%s">%s</a>',
- $self->{vn_relations}[$_->{relation}][0], $_->{id}, xml_escape($_->{original}||$_->{title}), xml_escape shorten $_->{title}, 40
+ mt("_vnrel_$_->{relation}"), $_->{id}, xml_escape($_->{original}||$_->{title}), xml_escape shorten $_->{title}, 40
), sort { $a->{id} <=> $b->{id} } @{$_[0]};
return @r ? @r : (mt '_vndiff_none');
}],
@@ -229,27 +231,42 @@ sub _revision {
sub _producers {
my($self, $i, $r) = @_;
- return if !grep @{$_->{producers}}, @$r;
my %lang;
my @lang = grep !$lang{$_}++, map @{$_->{languages}}, @$r;
- Tr ++$$i % 2 ? (class => 'odd') : ();
- td mt '_vnpage_producers';
- td;
- for my $l (@lang) {
- my %p = map { $_->{id} => $_ } map @{$_->{producers}}, grep grep($_ eq $l, @{$_->{languages}}), @$r;
- my @p = values %p;
- next if !@p;
- cssicon "lang $l", mt "_lang_$l";
- for (@p) {
+ if(grep $_->{developer}, map @{$_->{producers}}, @$r) {
+ my %dev = map $_->{developer} ? ($_->{id} => $_) : (), map @{$_->{producers}}, @$r;
+ my @dev = values %dev;
+ Tr ++$$i % 2 ? (class => 'odd') : ();
+ td mt "_vnpage_developer";
+ td;
+ for (@dev) {
a href => "/p$_->{id}", title => $_->{original}||$_->{name}, shorten $_->{name}, 30;
- txt ' & ' if $_ != $p[$#p];
+ txt ' & ' if $_ != $dev[$#dev];
}
- txt "\n";
- }
- end;
- end;
+ end;
+ end;
+ }
+
+ if(grep $_->{publisher}, map @{$_->{producers}}, @$r) {
+ Tr ++$$i % 2 ? (class => 'odd') : ();
+ td mt "_vnpage_publisher";
+ td;
+ for my $l (@lang) {
+ my %p = map $_->{publisher} ? ($_->{id} => $_) : (), map @{$_->{producers}}, grep grep($_ eq $l, @{$_->{languages}}), @$r;
+ my @p = values %p;
+ next if !@p;
+ cssicon "lang $l", mt "_lang_$l";
+ for (@p) {
+ a href => "/p$_->{id}", title => $_->{original}||$_->{name}, shorten $_->{name}, 30;
+ txt ' & ' if $_ != $p[$#p];
+ }
+ txt "\n";
+ }
+ end;
+ end;
+ }
}
@@ -266,7 +283,7 @@ sub _relations {
td class => 'relations';
dl;
for(sort keys %rel) {
- dt $self->{vn_relations}[$_][0];
+ dt mt "_vnrel_$_";
dd;
for (@{$rel{$_}}) {
a href => "/v$_->{id}", title => $_->{original}||$_->{title}, shorten $_->{title}, 40;
@@ -306,7 +323,7 @@ sub _anime {
txt '] ';
end;
acronym title => $_->{title_kanji}||$_->{title_romaji}, shorten $_->{title_romaji}, 50;
- b ' ('.(defined $_->{type} ? $self->{anime_types}[$_->{type}].', ' : '').$_->{year}.')';
+ b ' ('.(defined $_->{type} ? mt("_animetype_$_->{type}").', ' : '').$_->{year}.')';
txt "\n";
}
}
@@ -395,7 +412,7 @@ sub _releases {
end;
td class => 'tc5';
if($self->authInfo->{id}) {
- a href => "/r$rel->{id}", id => "rlsel_$rel->{id}";
+ a href => "/r$rel->{id}", id => "rlsel_$rel->{id}", class => 'vnrlsel';
lit $rel->{ulist} ? liststat $rel->{ulist} : '--';
end;
} else {
diff --git a/lib/VNDB/L10N.pm b/lib/VNDB/L10N.pm
index d4ff872c..8698cb74 100644
--- a/lib/VNDB/L10N.pm
+++ b/lib/VNDB/L10N.pm
@@ -5,12 +5,13 @@ use warnings;
{
package VNDB::L10N;
use base 'Locale::Maketext';
+ use LangFile;
sub fallback_languages { ('en') };
# used for the language switch interface, language tags must
# be the same as in the languages hash in global.pl
- sub languages { ('en', 'ru') }
+ sub languages { qw{ cs en hu ru } }
sub maketext {
my $r = eval { shift->SUPER::maketext(@_) };
@@ -21,59 +22,29 @@ use warnings;
# can be called as either a subroutine or a method
sub loadfile {
- my %lang = (
- en => \%VNDB::L10N::en::Lexicon,
- ru => \%VNDB::L10N::ru::Lexicon,
- );
-
- open my $F, '<:utf8', $VNDB::ROOT.'/data/lang.txt' or die "Opening language file: $!\n";
- my($empty, $line, $key, $lang) = (0, 0);
- while(<$F>) {
- chomp;
- $line++;
-
- # ignore intro
- if(!defined $key) {
- $key = 0 if /^\/intro$/;
- next;
+ my %lang = do {
+ no strict 'refs';
+ map +($_, \%{"VNDB::L10N::${_}::Lexicon"}), languages
+ };
+ my $r = LangFile->new(read => "$VNDB::ROOT/data/lang.txt");
+ my $key;
+ while(my $l = $r->read) {
+ my($t, @l) = @$l;
+ $key = $l[0] if $t eq 'key';
+ if($t eq 'tl') {
+ my($lang, undef, $text) = @l;
+ next if !$text;
+ die "Unknown language \"$l->[1]\"\n" if !$lang{$lang};
+ die "Unknown key for translation \"$lang: $text\"\n" if !$key;
+ $lang{$lang}{$key} = $text;
}
- # ignore comments
- next if /^#/;
- # key
- if(/^:(.+)$/) {
- $key = $1;
- $lang = undef;
- $empty = 0;
- next;
- }
- # locale string
- if(/^([a-z_-]{2,7})[ *]: (.+)$/) {
- $lang = $1;
- die "Unknown language on #$line: $lang\n" if !$lang{$lang};
- die "Unknown key for locale on #$line\n" if !$key;
- $lang{$lang}{$key} = $2;
- $empty = 0;
- next;
- }
- # multi-line locale string
- if($lang && /^\s+([^\s].*)$/) {
- $lang{$lang}{$key} .= ''.("\n"x$empty)."\n$1";
- $empty = 0;
- next;
- }
- # empty string (count them in case they're part of a multi-line locale string)
- if(/^\s*$/) {
- $empty++;
- next;
- }
- # something we didn't expect
- die "Don't know what to do with line $line\n" unless /^([a-z_-]{2,7})[ *]:/;
}
- close $F;
+ $r->close;
}
}
+
{
package VNDB::L10N::en;
use base 'VNDB::L10N';
@@ -88,8 +59,9 @@ use warnings;
# Argument: unix timestamp
# Returns: age
sub age {
- my $a = time-$_[1];
- return sprintf '%d %s ago',
+ my($self, $time) = @_;
+ my $a = time-$time;
+ my @f =
$a > 60*60*24*365*2 ? ( $a/60/60/24/365, 'years' ) :
$a > 60*60*24*(365/12)*2 ? ( $a/60/60/24/(365/12), 'months' ) :
$a > 60*60*24*7*2 ? ( $a/60/60/24/7, 'weeks' ) :
@@ -97,6 +69,7 @@ use warnings;
$a > 60*60*2 ? ( $a/60/60, 'hours' ) :
$a > 60*2 ? ( $a/60, 'min' ) :
( $a, 'sec' );
+ return $self->maketext("_age_$f[1]", int $f[0]);
}
# argument: unix timestamp and optional format (compact/full)
@@ -155,6 +128,30 @@ use warnings;
{
+ package VNDB::L10N::cs;
+ use base 'VNDB::L10N::en';
+ our %Lexicon;
+
+ sub quant {
+ my($self, $num, $single, $couple, $lots) = @_;
+ return $lots if ($num % 100) >= 11 && ($num % 100) <= 14;
+ return $single if ($num % 10) == 1;
+ return $couple if ($num % 10) >= 2 && ($num % 10) <= 4;
+ return $lots;
+ }
+}
+
+
+
+{
+ package VNDB::L10N::hu;
+ use base 'VNDB::L10N::en';
+ our %Lexicon;
+}
+
+
+
+{
package VNDB::L10N::ru;
use base 'VNDB::L10N::en';
our %Lexicon;
@@ -165,25 +162,9 @@ use warnings;
return $couple if ($num % 10) >= 2 && ($num % 10) <= 4 && !(($num % 100) >= 12 && ($num % 100) <= 14);
return $lots;
}
-
- sub age {
- my $self = shift;
- my $a = time-shift;
- use utf8;
- my @l = (
- $a > 60*60*24*365*2 ? ( $a/60/60/24/365, 'год', 'года', 'лет' ) :
- $a > 60*60*24*(365/12)*2 ? ( $a/60/60/24/(365/12), 'месяц', 'месяца', 'месяцев' ) :
- $a > 60*60*24*7*2 ? ( $a/60/60/24/7, 'неделя', 'недели', 'недель' ) :
- $a > 60*60*24*2 ? ( $a/60/60/24, 'день', 'дня', 'дней' ) :
- $a > 60*60*2 ? ( $a/60/60, 'час', 'часа', 'часов' ) :
- $a > 60*2 ? ( $a/60, 'минута', 'минуты', 'минут' ) :
- ( $a, 'секунда', 'секунды', 'секунд' )
- );
- return sprintf '%d %s назад', $l[0], $self->quant(@l);
- }
-
}
+
1;
diff --git a/lib/VNDB/Plugin/TransAdmin.pm b/lib/VNDB/Plugin/TransAdmin.pm
new file mode 100644
index 00000000..ed5ebb36
--- /dev/null
+++ b/lib/VNDB/Plugin/TransAdmin.pm
@@ -0,0 +1,349 @@
+# This plugin provides a quick and dirty user interface to editing lang.txt,
+# to use it, add the following to your data/config.pl:
+#
+# if($INC{"YAWF.pm"}) {
+# require VNDB::Plugin::TransAdmin;
+# $VNDB::S{transadmin} = {
+# <userid> => 'all' || <language> || <arrayref with languages>
+# };
+# }
+#
+# And then open /tladmin in your browser.
+# Also make sure data/lang.txt and data/docs/* are writable by the httpd process.
+# English is considered the 'main' language, and cannot be edited using this interface.
+
+package VNDB::Plugin::TransAdmin;
+
+use strict;
+use warnings;
+use YAWF ':html';
+use LangFile;
+use VNDB::Func;
+
+
+my $langfile = "$VNDB::ROOT/data/lang.txt";
+
+
+YAWF::register(
+ qr{tladmin(?:/([a-z]+))?} => \&tladmin
+);
+
+
+sub uri_escape {
+ local $_ = shift;
+ s/ /%20/g;
+ s/\?/%3F/g;
+ s/;/%3B/g;
+ s/&/%26/g;
+ return $_;
+}
+
+
+sub _allowed {
+ my($self, $lang) = @_;
+ my $a = $self->{transadmin}{ $self->authInfo->{id} };
+ return $a eq 'all' || $a eq $lang || ref($a) eq 'ARRAY' && grep $_ eq $lang, @$a;
+}
+
+
+sub tladmin {
+ my($self, $lang) = @_;
+
+ $lang ||= '';
+ my $intro = $lang =~ s/intro//;
+ return 404 if $lang && ($lang eq 'en' || !grep $_ eq $lang, $self->{l10n}->languages);
+ my $sect = $self->reqParam('sect')||'';
+ my $doc = $self->reqParam('doc')||'';
+
+ my $uid = $self->authInfo->{id};
+ return $self->htmlDenied if !$uid || !$self->{transadmin}{$uid};
+
+ if(!-w $langfile || !-w "$VNDB::ROOT/data/docs" || grep /\.[a-z]{2}$/ && !-w $_, glob "$VNDB::ROOT/data/docs/*") {
+ $self->htmlHeader(title => 'Language file not writable', noindex => 1);
+ div class => 'mainbox';
+ h1 'Language file not writable';
+ div class => 'warning', 'Sorry, I do not have enough permission to write to the language files.';
+ end;
+ $self->htmlFooter;
+ return;
+ }
+
+ _savelang($self, $lang) if $lang && $sect && $self->reqMethod eq 'POST' && _allowed($self, $lang);
+ _savedoc($self, $lang, $doc) if $lang && $doc && $self->reqMethod eq 'POST' && _allowed($self, $lang);
+ my($sects, $page) = _readlang($lang, $sect) if $lang;
+
+ $self->htmlHeader(title => 'Quick-and-dirty Translation Editor', noindex => 1);
+ div class => 'mainbox';
+ a class => 'addnew', href => '/tladmin/intro', 'README';
+ h1 'Quick-and-dirty Translation Editor';
+ h2 class => 'alttitle', 'Step #1: Choose a language';
+ p class => 'browseopts';
+ a $lang eq $_ ? (class => 'optselected') : (), href => "/tladmin/$_", mt "_lang_$_"
+ for grep !/en/, $self->{l10n}->languages;
+ end;
+ _sections($self, $lang, $sect, $sects) if $lang;
+ _docs($lang, $doc) if $lang;
+ end;
+
+ _intro() if $intro;
+ _section($self, $lang, $sect, $page) if $lang && $sect;
+ _doc($self, $lang, $doc) if $lang && $doc;
+
+ $self->htmlFooter;
+}
+
+
+sub _savelang {
+ my($self, $lang) = @_;
+
+ # do everything in-memory, so we don't need write access to a temporary file
+ # (this has the downside that in the event something goes wrong, everything will be wiped)
+ my $f = LangFile->new(read => $langfile);
+ my @read;
+ push @read, $_ while (local $_ = $f->read);
+ $f->close;
+
+ my @keys = $self->reqParam;
+ $f = LangFile->new(write => $langfile);
+ my $key;
+ for my $l (@read) {
+ $key = $l->[1] if $l->[0] eq 'key';
+ if($l->[0] eq 'tl' && $l->[1] eq $lang && grep $key eq $_, @keys) {
+ $l->[2] = !$self->reqParam("check$key");
+ $l->[3] = $self->reqParam($key);
+ $l->[3] =~ s/\r?\n/\n/g;
+ $l->[3] =~ s/\s+$//g;
+ }
+ $f->write(@$l);
+ }
+ $f->close;
+
+ # re-read the file and regenerate the JS in case we're not running as CGI
+ if($INC{"FCGI.pm"}) {
+ VNDB::L10N::loadfile();
+ VNDB::checkjs();
+ }
+}
+
+
+sub _readlang {
+ my($lang, $sect) = @_;
+ my @sect; # [ title, count, unsync ]
+ my @page; # [ 'comment'||'line', <comment>|| ( <key>, <en>, <sync>, <tl> ) ]
+
+ my $f = LangFile->new(read => $langfile);
+ my($key, $insect);
+ while(my $l = $f->read) {
+ my $t = shift @$l;
+
+ if($t eq 'space') {
+ if(join("\n", @$l) =~ /((#{30,90}\n)## +(.+) +##\n\2.+)^/ms) {
+ my $header = $1;
+ (my $title = $3) =~ s/\s+$//;
+ $title =~ s/\s+\([^)]+\)$//;
+ push @sect, [ $title, 0, 0 ];
+ $insect = $title eq $sect;
+ push @page, [ 'comment', $header ] if $insect;
+ } elsif($insect) {
+ push @page, [ 'comment', join "\n", @$l ];
+ }
+ }
+
+ $sect[$#sect][1]++ if $t eq 'key';
+ $sect[$#sect][2]++ if $t eq 'tl' && $l->[0] eq $lang && !$l->[1];
+
+ next if !$insect;
+ push @page, [ 'line', $l->[0] ] if $t eq 'key';
+ $page[$#page][2] = $l->[2] if $t eq 'tl' && $l->[0] eq 'en';
+ if($t eq 'tl' && $l->[0] eq $lang) {
+ $page[$#page][3] = $l->[1];
+ $page[$#page][4] = $l->[2];
+ }
+ }
+ $f->close;
+ return (\@sect, \@page);
+}
+
+
+sub _intro {
+ my $f = LangFile->new(read => $langfile);
+ my $intro = $f->read;
+ $intro = join "\n", @$intro[1..$#$intro];
+ $f->close;
+ div class => 'mainbox';
+ h1 'Introduction to the language file';
+ pre $intro;
+ end;
+}
+
+
+sub _sections {
+ my($self, $lang, $sect, $list) = @_;
+
+ br;
+ h2 class => 'alttitle', 'Step #2: Choose a section';
+ div style => 'margin: 0 40px';
+ for (@$list) {
+ div style => 'float: left; width: 200px;';
+ a href => "/tladmin/$lang?sect=".uri_escape($_->[0]), $_->[0] if $sect ne $_->[0];
+ txt $sect if $sect eq $_->[0];
+ txt " ";
+ txt "0/$_->[1]" if !$_->[2];
+ b class => 'standout', "$_->[2]/$_->[1]" if $_->[2];
+ end;
+ }
+ clearfloat;
+ end;
+ br;
+ br;
+}
+
+
+sub _section {
+ my($self, $lang, $sect, $page) = @_;
+
+ form action => "/tladmin/$lang?sect=".uri_escape($sect), method => 'POST', 'accept-charset' => 'utf-8';
+ div class => 'mainbox';
+ h1 $sect;
+
+ if(_allowed($self, $lang)) {
+ h2 class => 'alttitle', "Don't forget to hit the 'save' button to make your changes permament!";
+ } else {
+ div class => 'warning';
+ h2 'Read-only';
+ p "You can't edit this language.";
+ end;
+ }
+
+ for my $l (@$page) {
+ if($l->[0] eq 'comment') {
+ pre;
+ b class => 'grayedout', $l->[1]."\n";
+ end;
+ next;
+ }
+
+ my(undef, $key, $en, $sync, $tl) = @$l;
+ b class => $sync ? 'grayedout' : 'standout', ":$key";
+ br;
+ div style => 'margin-left: 25px; font: 12px Tahoma; width: 700px; overflow-x: auto; white-space: nowrap', $en;
+ my $multi = $en =~ y/\n//;
+
+ div style => 'width: 23px; float: left; text-align: right';
+ input type => 'checkbox', name => "check$key", id => "check$key", !$sync ? (checked => 'checked') : ();
+ end;
+ div style => 'float: left';
+ if($multi) {
+ $tl =~ s/&/&amp;/g;
+ $tl =~ s/</&lt;/g;
+ $tl =~ s/>/&gt;/g;
+ textarea name => $key, id => $key, rows => $multi+2, style => 'width: 700px; height: auto; white-space: nowrap; border: none', wrap => 'off';
+ lit $tl;
+ end;
+ } else {
+ input type => 'text', class => 'text', name => $key, id => $key, value => $tl, style => 'width: 700px; border: none';
+ }
+ end;
+ clearfloat;
+ }
+ if(_allowed($self, $lang)) {
+ br;br;
+ fieldset class => 'submit';
+ input type => 'submit', value => 'Save', class => 'submit';
+ end;
+ }
+ end;
+ end;
+}
+
+
+sub _savedoc {
+ my($self, $lang, $doc) = @_;
+
+ my $file = "$VNDB::ROOT/data/docs/$doc.$lang";
+
+ open my $f, '<:utf8', "$VNDB::ROOT/data/docs/$doc" or die $!;
+ my $en = join '', <$f>;
+ close $f;
+
+ my $tl = $self->reqParam('tl');
+ $tl =~ s/\r?\n/\n/g;
+
+ return -e $file && unlink $file if $tl eq $en;
+
+ open $f, '>:utf8', $file or die $!;
+ print $f $tl;
+ close $f;
+ chmod 0666, $file;
+}
+
+
+sub _docs {
+ my($lang, $doc) = @_;
+
+ my @d = map /\.[a-z]{2}$/ || /\/8$/ ? () : s{^.+\/([^/]+)$}{$1} && $_, glob "$VNDB::ROOT/data/docs/*";
+
+ h2 class => 'alttitle', '...or a doc page';
+ div style => 'margin: 0 40px';
+ for (sort { $a =~ /^\d+$/ && $b =~ /^\d+$/ ? $a <=> $b : $a cmp $b } @d) {
+ div style => 'float: left; width: 60px;';
+ a href => "/tladmin/$lang?doc=$_", $_ if $_ ne $doc;
+ txt $_ if $_ eq $doc;
+ end;
+ }
+ clearfloat;
+ end;
+}
+
+
+sub _doc {
+ my($self, $lang, $doc) = @_;
+
+ open my $f, '<:utf8', "$VNDB::ROOT/data/docs/$doc" or die $!;
+ my $en = join '', <$f>;
+ close $f;
+
+ my $tl = $en;
+ if(open $f, '<:utf8', "$VNDB::ROOT/data/docs/$doc.$lang") {
+ $tl = join '', <$f>;
+ close $f;
+ }
+ $tl =~ s/&/&amp;/g;
+ $tl =~ s/</&lt;/g;
+ $tl =~ s/>/&gt;/g;
+
+
+ form action => "/tladmin/$lang?doc=$doc", method => 'POST', 'accept-charset' => 'utf-8';
+ div class => 'mainbox';
+ a class => 'addnew', href => "/d$doc", "View current page" if $doc =~ /^\d+$/;
+ h1 "Translating page $doc";
+ h2 class => 'alttitle', 'Left = English, Right = translation';
+
+ if(!_allowed($self, $lang)) {
+ div class => 'warning';
+ h2 'Read-only';
+ p "You can't edit this language.";
+ end;
+ }
+
+ div style => 'width: 48%; margin-right: 10px; overflow-y: auto; float: left';
+ pre style => 'font: 12px Tahoma', $en;
+ end;
+ textarea name => 'tl', id => 'tl', rows => ($en =~ y/\n//),
+ style => 'border: none; float: left; width: 49%; white-space: nowrap', wrap => 'off';
+ lit $tl;
+ end;
+ clearfloat;
+ if(_allowed($self, $lang)) {
+ br;
+ fieldset class => 'submit';
+ input type => 'submit', value => 'Save', class => 'submit';
+ end;
+ }
+ end;
+ end;
+}
+
+
+1;
+
diff --git a/lib/VNDB/Util/Auth.pm b/lib/VNDB/Util/Auth.pm
index c4daffd9..a3bf7c29 100644
--- a/lib/VNDB/Util/Auth.pm
+++ b/lib/VNDB/Util/Auth.pm
@@ -26,7 +26,7 @@ sub authInit {
my $token = substr($cookie, 0, 40);
my $uid = substr($cookie, 40);
return _rmcookie($self) if $uid !~ /^\d+$/ || !$self->dbSessionCheck($uid, $token);
- $self->{_auth} = $self->dbUserGet(uid => $uid, what => 'mymessages')->[0];
+ $self->{_auth} = $self->dbUserGet(uid => $uid, what => 'extended')->[0];
}
@@ -95,7 +95,7 @@ sub _authCheck {
return 0 if !$user || length($user) > 15 || length($user) < 2 || !$pass;
- my $d = $self->dbUserGet(username => $user, what => 'mymessages')->[0];
+ my $d = $self->dbUserGet(username => $user, what => 'extended')->[0];
return 0 if !defined $d->{id} || !$d->{rank};
if(_authEncryptPass($self, $pass, $d->{salt}) eq $d->{passwd}) {
diff --git a/lib/VNDB/Util/CommonHTML.pm b/lib/VNDB/Util/CommonHTML.pm
index b1eb6432..ad99d32d 100644
--- a/lib/VNDB/Util/CommonHTML.pm
+++ b/lib/VNDB/Util/CommonHTML.pm
@@ -12,7 +12,7 @@ use POSIX 'ceil';
our @EXPORT = qw|
htmlMainTabs htmlDenied htmlHiddenMessage htmlBrowse htmlBrowseNavigate
- htmlRevision htmlEditMessage htmlItemMessage htmlVoteStats htmlHistory htmlSearchBox
+ htmlRevision htmlEditMessage htmlItemMessage htmlVoteStats htmlHistory htmlSearchBox htmlRGHeader
|;
@@ -101,7 +101,7 @@ sub htmlMainTabs {
end;
}
- if($type eq 'v' && $obj->{rgraph}) {
+ if($type =~ /[vp]/ && $obj->{rgraph}) {
li $sel eq 'rg' ? (class => 'tabselected') : ();
a href => "/$id/rg", mt '_mtabs_relations';
end;
@@ -501,16 +501,15 @@ sub htmlHistory {
sub { td colspan => 2, class => 'tc1', mt '_hist_col_rev' },
[ mt '_hist_col_date' ],
[ mt '_hist_col_user' ],
- sub { td; a href => '#', id => 'history_comments', 'expand'; txt mt '_hist_col_page'; end; }
+ sub { td; a href => '#', id => 'expandlist', mt '_js_expand'; txt mt '_hist_col_page'; end; }
],
row => sub {
my($s, $n, $i) = @_;
- my $tc = [qw|v r p|]->[$i->{type}];
- my $revurl = "/$tc$i->{iid}.$i->{rev}";
+ my $revurl = "/$i->{type}$i->{iid}.$i->{rev}";
Tr $n % 2 ? ( class => 'odd' ) : ();
td class => 'tc1_1';
- a href => $revurl, "$tc$i->{iid}";
+ a href => $revurl, "$i->{type}$i->{iid}";
end;
td class => 'tc1_2';
a href => $revurl, ".$i->{rev}";
@@ -524,7 +523,7 @@ sub htmlHistory {
end;
end;
if($i->{comments}) {
- Tr class => $n % 2 ? 'editsum odd hidden' : 'editsum hidden';
+ Tr class => $n % 2 ? 'collapse msgsum odd hidden' : 'collapse msgsum hidden';
td colspan => 5;
lit bb2html $i->{comments}, 150;
end;
@@ -538,13 +537,20 @@ sub htmlHistory {
sub htmlSearchBox {
my($self, $sel, $v) = @_;
+ # escape search query for use as a query string value
+ (my $q = $v||'') =~ s/&/%26/g;
+ $q =~ s/\?/%3F/g;
+ $q =~ s/;/%3B/g;
+ $q =~ s/ /%20/g;
+ $q = "?q=$q" if $q;
+
fieldset class => 'search';
p class => 'searchtabs';
- a href => '/v/all', $sel eq 'v' ? (class => 'sel') : (), mt '_searchbox_vn';
- a href => '/r', $sel eq 'r' ? (class => 'sel') : (), mt '_searchbox_releases';
- a href => '/p/all', $sel eq 'p' ? (class => 'sel') : (), mt '_searchbox_producers';
- a href => '/g', $sel eq 'g' ? (class => 'sel') : (), mt '_searchbox_tags';
- a href => '/u/all', $sel eq 'u' ? (class => 'sel') : (), mt '_searchbox_users';
+ a href => "/v/all$q", $sel eq 'v' ? (class => 'sel') : (), mt '_searchbox_vn';
+ a href => "/r$q", $sel eq 'r' ? (class => 'sel') : (), mt '_searchbox_releases';
+ a href => "/p/all$q", $sel eq 'p' ? (class => 'sel') : (), mt '_searchbox_producers';
+ a href => '/g'.($q?"/list$q":''), $sel eq 'g' ? (class => 'sel') : (), mt '_searchbox_tags';
+ a href => "/u/all$q", $sel eq 'u' ? (class => 'sel') : (), mt '_searchbox_users';
end;
input type => 'text', name => 'q', id => 'q', class => 'text', value => $v;
input type => 'submit', class => 'submit', value => mt '_searchbox_submit';
@@ -552,5 +558,41 @@ sub htmlSearchBox {
}
+sub htmlRGHeader {
+ my($self, $title, $type, $obj) = @_;
+
+ if(($self->reqHeader('Accept')||'') !~ /application\/xhtml\+xml/) {
+ $self->htmlHeader(title => $title);
+ $self->htmlMainTabs($type, $obj, 'rg');
+ div class => 'mainbox';
+ h1 $title;
+ div class => 'warning';
+ h2 mt '_rg_notsupp';
+ p mt '_rg_notsupp_msg';
+ end;
+ end;
+ $self->htmlFooter;
+ return 1;
+ }
+ $self->resHeader('Content-Type' => 'application/xhtml+xml; charset=UTF-8');
+
+ # This is a REALLY ugly hack, need find a proper solution in YAWF
+ no warnings 'redefine';
+ my $sub = \&YAWF::XML::html;
+ *YAWF::XML::html = sub () {
+ lit q|<!DOCTYPE html PUBLIC
+ "-//W3C//DTD XHTML 1.1 plus MathML 2.0 plus SVG 1.1//EN"
+ "http://www.w3.org/2002/04/xhtml-math-svg/xhtml-math-svg.dtd">|;
+ tag 'html',
+ xmlns => "http://www.w3.org/1999/xhtml",
+ 'xmlns:svg' => 'http://www.w3.org/2000/svg',
+ 'xmlns:xlink' => 'http://www.w3.org/1999/xlink';
+ };
+ $self->htmlHeader(title => $title);
+ *YAWF::XML::html = $sub;
+ $self->htmlMainTabs($type, $obj, 'rg');
+ return 0;
+}
+
1;
diff --git a/lib/VNDB/Util/LayoutHTML.pm b/lib/VNDB/Util/LayoutHTML.pm
index 85971ba9..084b9a4e 100644
--- a/lib/VNDB/Util/LayoutHTML.pm
+++ b/lib/VNDB/Util/LayoutHTML.pm
@@ -10,7 +10,7 @@ use VNDB::Func;
our @EXPORT = qw|htmlHeader htmlFooter|;
-sub htmlHeader { # %options->{ title, js, noindex, search }
+sub htmlHeader { # %options->{ title, noindex, search }
my($self, %o) = @_;
my $skin = $self->reqParam('skin') || $self->authInfo->{skin} || $self->{skin_default};
$skin = $self->{skin_default} if !$self->{skins}{$skin} || !-d "$VNDB::ROOT/static/s/$skin";
@@ -22,12 +22,6 @@ sub htmlHeader { # %options->{ title, js, noindex, search }
Link rel => 'shortcut icon', href => '/favicon.ico', type => 'image/x-icon';
Link rel => 'stylesheet', href => $self->{url_static}.'/s/'.$skin.'/style.css?'.$self->{version}, type => 'text/css', media => 'all';
Link rel => 'search', type => 'application/opensearchdescription+xml', title => 'VNDB VN Search', href => $self->{url}.'/opensearch.xml';
- if($o{js}) {
- script type => 'text/javascript', src => $self->{url_static}.'/f/forms.js?'.$self->{version}; end;
- }
- script type => 'text/javascript', src => $self->{url_static}.'/f/script.js?'.$self->{version};
- # most browsers don't like a self-closing <script> tag...
- end;
if($self->authInfo->{customcss}) {
(my $css = $self->authInfo->{customcss}) =~ s/\n/ /g;
style type => 'text/css', $css;
@@ -55,12 +49,8 @@ sub _menu {
div class => 'menubox';
h2;
- span;
- for (grep $self->{l10n}->language_tag() ne $_, $self->{l10n}->languages()) {
- a href => "?l10n=$_";
- cssicon "lang $_", mt "_lang_$_"; # NOTE: should actually be in the destination language...
- end;
- }
+ a href => "#", id => 'lang_select';
+ cssicon "lang ".$self->{l10n}->language_tag(), mt "_lang_".$self->{l10n}->language_tag();
end;
txt mt '_menu';
end;
@@ -89,6 +79,7 @@ sub _menu {
div class => 'menubox';
if($self->authInfo->{id}) {
+ my $msg = $self->dbUserMessageCount($self->authInfo->{id});
my $uid = sprintf '/u%d', $self->authInfo->{id};
h2;
a href => $uid, ucfirst $self->authInfo->{username};
@@ -99,7 +90,7 @@ sub _menu {
a href => "$uid/edit", mt '_menu_myprofile'; br;
a href => "$uid/list", mt '_menu_myvnlist'; br;
a href => "$uid/wish", mt '_menu_mywishlist'; br;
- a href => "/t$uid", mt '_menu_mymessages', $self->authInfo->{mymessages}; br;
+ a href => "/t$uid", $msg ? (class => 'standout') : (), mt '_menu_mymessages', $msg; br;
a href => "$uid/hist", mt '_menu_mychanges'; br;
a href => "$uid/tags", mt '_menu_mytags'; br;
br;
@@ -163,6 +154,7 @@ sub htmlFooter {
a href => $self->{source_url}, mt '_footer_source';
end;
end; # /div maincontent
+ script type => 'text/javascript', src => $self->{url_static}.'/f/script.js?'.$self->{version}, '';
end; # /body
end; # /html
diff --git a/static/f/forms.js b/static/f/forms.js
deleted file mode 100644
index ce9daab5..00000000
--- a/static/f/forms.js
+++ /dev/null
@@ -1,955 +0,0 @@
-// various form functions
-// called by script.js
-
-function qq(v) {
- return v.replace(/&/g,"&amp;").replace(/</,"&lt;").replace(/>/,"&gt;").replace(/"/g,'&quot;');
-}
-function shorten(v, l) {
- return qq(v.length > l ? v.substr(0, l-3)+'...' : v);
-}
-
-
-
-
-
-
- /***********************************\
- * D R O P D O W N S E A R C H *
- \***********************************/
-
-
-function dsInit(obj, url, trfunc, serfunc, retfunc, parfunc) {
- obj.setAttribute('autocomplete', 'off');
- obj.onkeydown = dsKeyDown;
- obj.onblur = function() {
- // timeout to make sure the tr.onclick event is called before we've hidden the object
- setTimeout(function () {
- if(x('ds_box'))
- x('ds_box').style.top = '-500px';
- }, 500)
- };
- // all local data is stored in the DOM input object
- obj.returnFunc = retfunc;
- obj.trFunc = trfunc;
- obj.serFunc = serfunc;
- obj.parFunc = parfunc;
- obj.searchURL = url;
- obj.selectedId = 0;
-}
-
-function dsKeyDown(ev) {
- var c = document.layers ? ev.which : document.all ? event.keyCode : ev.keyCode;
- var obj = this;
-
- if(c == 9) // tab
- return true;
-
- // do some processing when the enter key has been pressed
- if(c == 13) {
- var o = obj;
- while(o && o.nodeName.toLowerCase() != 'form')
- o = o.parentNode;
- if(o) {
- var oldsubmit = o.onsubmit;
- o.onsubmit = function() { return false };
- setTimeout(function() { o.onsubmit = oldsubmit }, 100);
- }
-
- if(obj.selectedId != 0)
- obj.value = obj.serFunc(x('ds_box_'+obj.selectedId).itemData, obj);
- if(obj.returnFunc)
- obj.returnFunc();
- if(x('ds_box'))
- x('ds_box').style.top = '-500px';
- obj.selectedId = 0;
-
- return false;
- }
-
- // process up/down keys
- if(x('ds_box') && (c == 38 || c == 40)) {
- var l = x('ds_box').getElementsByTagName('tr');
- if(l.length < 1)
- return true;
-
- if(obj.selectedId == 0) {
- if(c == 38) // up
- obj.selectedId = l[l.length-1].id.substr(7);
- else
- obj.selectedId = l[0].id.substr(7);
- } else {
- var sel = null;
- for(var i=0;i<l.length;i++)
- if(l[i].id == 'ds_box_'+obj.selectedId) {
- if(c == 38) // up
- sel = i>0 ? l[i-1] : l[l.length-1];
- else
- sel = l[i+1] ? l[i+1] : l[0];
- }
- obj.selectedId = sel.id.substr(7);
- }
-
- for(var i=0;i<l.length;i++)
- l[i].className = l[i].id == 'ds_box_'+obj.selectedId ? 'selected' : '';
- return true;
- }
-
- // this.value isn't available in a keydown event
- setTimeout(function() {
- dsSearch(obj);
- }, 10);
-
- return true;
-}
-
-function dsSearch(obj) {
- var b = x('ds_box');
- var v = obj.parFunc ? obj.parFunc(obj.value) : obj.value;
-
- // show/hide the ds_box div
- if(v.length < 2) {
- if(b) {
- b.style.top = '-500px';
- b.innerHTML = '<b>Loading...</b>';
- }
- obj.selectedId = 0;
- return;
- }
- if(!b) {
- b = document.createElement('div');
- b.setAttribute('id', 'ds_box');
- b.innerHTML = '<b>Loading...</b>';
- document.body.appendChild(b);
- }
-
- // position the div
- var ddx=0;
- var ddy=obj.offsetHeight;
- var o = obj;
- do {
- ddx += o.offsetLeft;
- ddy += o.offsetTop;
- } while(o = o.offsetParent);
-
- b.style.position = 'absolute';
- b.style.left = ddx+'px';
- b.style.top = ddy+'px';
- b.style.width = obj.offsetWidth+'px';
-
- // perform search
- ajax(obj.searchURL + encodeURIComponent(v), function(hr) {
- dsResults(hr, obj);
- });
-}
-
-function dsResults(hr, obj) {
- var l = hr.responseXML.getElementsByTagName('item');
- var b = x('ds_box');
- if(l.length < 1) {
- b.innerHTML = '<b>No results...</b>';
- obj.selectedId = 0;
- return;
- }
-
- b.innerHTML = '<table><tbody></tbody></table>';
- tb = b.getElementsByTagName('tbody')[0];
- for(var i=0;i<l.length;i++) {
- var id = l[i].getAttribute('id');
- var tr = document.createElement('tr');
- tr.setAttribute('id', 'ds_box_'+id);
- tr.itemData = l[i];
- if(obj.selectedId == id)
- tr.setAttribute('class', 'selected');
- tr.onmouseover = function() {
- obj.selectedId = this.id.substr(7);
- var l = x('ds_box').getElementsByTagName('tr');
- for(var i=0;i<l.length;i++)
- l[i].className = l[i].id == 'ds_box_'+obj.selectedId ? 'selected' : '';
- };
- tr.onclick = function() {
- obj.value = obj.serFunc(this.itemData, obj);
- if(obj.returnFunc)
- obj.returnFunc();
- if(x('ds_box'))
- x('ds_box').style.top = '-500px';
- obj.selectedId = 0;
- };
- obj.trFunc(l[i], tr);
- tb.appendChild(tr);
- }
-
- if(obj.selectedId != 0 && !x('ds_box_'+obj.selectedId))
- obj.selectedId = 0;
-}
-
-
-
-
-
-
- /*****************************\
- * V N R E L A T I O N S *
- \*****************************/
-
-
-var relTypes = [];
-function relLoad() {
- var i;var l;var o;
-
- // fetch the relation types from the add new relation selectbox
- l = x('relation_new').getElementsByTagName('select')[0].options;
- for(i=0;i<l.length;i++)
- relTypes[Math.floor(l[i].value)] = l[i].text;
-
- // read the current relations
- l = x('relations').value.split('|||');
- if(l[0]) {
- for(i=0;i<l.length;i++) {
- var rel = l[i].split(',', 3);
- relAdd(rel[0], rel[1], rel[2]);
- }
- }
- relEmpty();
-
- // make sure the title is up-to-date
- x('title').onchange = function() {
- l = x('jt_box_vn_rel').getElementsByTagName('td');
- for(i=0;i<l.length;i++)
- if(l[i].className == 'tc3')
- l[i].innerHTML = shorten(this.value, 40);
- };
-
- // bind the add-link
- x('relation_new').getElementsByTagName('a')[0].onclick = relFormAdd;
-
- // dropdown
- dsInit(x('relation_new').getElementsByTagName('input')[0], '/xml/vn.xml?q=', function(item, tr) {
- var td = document.createElement('td');
- td.innerHTML = 'v'+item.getAttribute('id');
- td.style.textAlign = 'right';
- td.style.paddingRight = '5px';
- tr.appendChild(td);
- td = document.createElement('td');
- td.innerHTML = shorten(item.firstChild.nodeValue, 40);
- tr.appendChild(td);
- }, function(item) {
- return 'v'+item.getAttribute('id')+':'+item.firstChild.nodeValue;
- }, relFormAdd);
-}
-
-function relAdd(rel, vid, title) {
- var o = document.createElement('tr');
- o.setAttribute('id', 'relation_tr_'+vid);
-
- var t = document.createElement('td');
- t.className = 'tc1';
- t.innerHTML = 'v'+vid+':<a href="/v'+vid+'">'+shorten(title, 40)+'</a>';
- o.appendChild(t);
-
- var options = '';
- for(var i=0;i<relTypes.length;i++)
- options += '<option value="'+i+'"'+(i == rel ? ' selected="selected"' : '')+'>'+qq(relTypes[i])+'</option>';
- t = document.createElement('td');
- t.className = 'tc2';
- t.innerHTML = 'is a <select onchange="relSerialize()">'+options+'</select> of';
- o.appendChild(t);
-
- t = document.createElement('td');
- t.className = 'tc3';
- t.innerHTML = shorten(x('title').value, 40);
- o.appendChild(t);
-
- t = document.createElement('td');
- t.className = 'tc4';
- t.innerHTML = '<a href="#" onclick="return relDel('+vid+')">del</a>';
- o.appendChild(t);
-
- x('relation_tbl').appendChild(o);
- relEmpty();
-}
-
-function relEmpty() {
- if(x('relation_tbl').getElementsByTagName('tr').length > 0) {
- if(x('relation_tr_none'))
- x('relation_tbl').removeChild(x('relation_tr_none'));
- return;
- }
- var o = document.createElement('tr');
- o.setAttribute('id', 'relation_tr_none');
- var t = document.createElement('td');
- t.colspan = 4;
- t.innerHTML = 'No relations selected.';
- o.appendChild(t);
- x('relation_tbl').appendChild(o);
-}
-
-function relSerialize() {
- var r='';
- var i;
- var l = x('relation_tbl').getElementsByTagName('tr');
- for(i=0;i<l.length;i++) {
- var title = l[i].getElementsByTagName('td')[0];
- title = title.innerText || title.textContent;
- title = title.substr(title.indexOf(':')+1);
- r += (r ? '|||' : '')
- +l[i].getElementsByTagName('select')[0].selectedIndex
- +','+l[i].id.substr(12)+','+title;
- }
- x('relations').value = r;
-}
-
-function relDel(vid) {
- x('relation_tbl').removeChild(x('relation_tr_'+vid));
- relSerialize();
- relEmpty();
- return false;
-}
-
-function relFormAdd() {
- var txt = x('relation_new').getElementsByTagName('input')[0];
- var sel = x('relation_new').getElementsByTagName('select')[0];
- var lnk = x('relation_new').getElementsByTagName('a')[0];
- var input = txt.value;
-
- if(!input.match(/^v[0-9]+/)) {
- alert('Visual novel textbox must start with an ID (e.g. v17)');
- return false;
- }
-
- txt.disabled = true;
- txt.value = 'loading...';
- sel.disabled = true;
- lnk.innerHTML = 'loading...';
-
- ajax('/xml/vn.xml?q='+encodeURIComponent(input), function(hr) {
- txt.disabled = false;
- txt.value = '';
- sel.disabled = false;
- lnk.innerHTML = 'add';
-
- var items = hr.responseXML.getElementsByTagName('item');
- if(items.length < 1)
- return alert('Visual novel not found!');
-
- var id = items[0].getAttribute('id');
- if(x('relation_tr_'+id))
- return alert('This visual novel has already been selected!');
-
- relAdd(sel.selectedIndex, id, items[0].firstChild.nodeValue);
- sel.selectedIndex = 0;
- relSerialize();
- });
- return false;
-}
-
-
-
-
-
-
- /*********************************\
- * V N S C R E E N S H O T S *
- \*********************************/
-
-
-var scrRel = [ [ 0, '-- select release --' ] ];
-var scrStaticURL;
-function scrLoad() {
- // load the releases
- scrStaticURL = x('scr_rel').className;
- var l = x('scr_rel').options;
- for(var i=0;i<l.length;i++)
- scrRel[i+1] = [ l[i].value, l[i].text ];
- x('scr_rel').parentNode.removeChild(x('scr_rel'));
-
- // load the current screenshots
- l = x('screenshots').value.split(' ');
- for(i=0;i<l.length;i++)
- if(l[i].length > 2) {
- var r = l[i].split(',');
- scrAdd(r[0], r[1], r[2]);
- }
- scrLast();
- scrCheckStatus();
-
- scrSetSubmit();
-}
-
-// give an error when submitting the form while still uploading an image
-function scrSetSubmit() {
- var o=x('screenshots');
- while(o.nodeName.toLowerCase() != 'form')
- o = o.parentNode;
- oldfunc = o.onsubmit;
- o.onsubmit = function() {
- var c=0;var r=0;
- var l = x('scr_table').getElementsByTagName('tr');
- for(var i=0;i<l.length-1;i++) {
- if(l[i].scrStatus > 0)
- c=1;
- else if(l[i].getElementsByTagName('select')[0].selectedIndex == 0)
- r=1;
- }
- if(c) {
- alert('Please wait for the screenshots to be uploaded before submitting the form.');
- return false;
- } else if(r) {
- alert('Please select the appropriate release for every screenshot');
- return false;
- } else if(oldfunc)
- return oldfunc();
- };
-}
-
-
-function scrURL(id, t) {
- return scrStaticURL+'/s'+t+'/'+(id%100<10?'0':'')+(id%100)+'/'+id+'.jpg';
-}
-
-function scrAdd(id, nsfw, rel) {
- var tr = document.createElement('tr');
- tr.scrId = id;
- tr.scrStatus = id ? 2 : 1; // 0: done, 1: uploading, 2: waiting for thumbnail
- tr.scrRel = rel;
- tr.scrNSFW = nsfw;
-
- var td = document.createElement('td');
- td.className = 'thumb';
- td.innerHTML = 'loading...';
- tr.appendChild(td);
-
- td = document.createElement('td');
- if(id)
- td.innerHTML = '<b>Generating thumbnail...</b><br />'
- +'Note: if this takes longer than 30 seconds, there\'s probably something wrong on our side.'
- +'Please try again later or report a bug if that is the case.';
- else
- td.innerHTML = '<b>Uploading screenshot...</b><br />'
- +'This can take a while, depending on the file size and your upload speed.<br />'
- +'<a href="#" onclick="return scrDel(this)">cancel</a>';
- tr.appendChild(td);
-
- x('scr_table').appendChild(tr);
- scrStripe();
- return tr;
-}
-
-function scrLast() {
- if(x('scr_last'))
- x('scr_table').removeChild(x('scr_last'));
- var full = x('scr_table').getElementsByTagName('tr').length >= 10;
-
- var tr = document.createElement('tr');
- tr.setAttribute('id', 'scr_last');
-
- var td = document.createElement('td');
- td.className = 'thumb';
- tr.appendChild(td);
-
- var td = document.createElement('td');
- if(full)
- td.innerHTML = '<b>Enough screenshots</b><br />'
- +'The limit of 10 screenshots per visual novel has been reached. '
- +'If you want to add a new screenshot, please remove an existing one first.';
- else
- td.innerHTML = '<b>Add screenshot</b><br />'
- +'Image must be smaller than 5MB and in PNG or JPEG format.<br />'
- +'<input name="scr_upload" id="scr_upload" type="file" class="text" /><br />'
- +'<input type="button" value="Upload!" class="submit" onclick="scrUpload()" />';
-
- tr.appendChild(td);
- x('scr_table').appendChild(tr);
- scrStripe();
-}
-
-function scrStripe() {
- var l = x('scr_table').getElementsByTagName('tr');
- for(var i=0;i<l.length;i++)
- l[i].className = i%2==0 ? 'odd' : '';
-}
-
-function scrCheckStatus() {
- var ids = '';
- var l = x('scr_table').getElementsByTagName('tr');
- for(var i=0;i<l.length-1;i++)
- if(l[i].scrStatus == 2)
- ids += (ids ? ';' : '?')+'id='+l[i].scrId;
- if(!ids)
- return setTimeout(scrCheckStatus, 1000);
-
- var ti = setTimeout(scrCheckStatus, 10000);
- ajax('/xml/screenshots.xml'+ids, function(hr) {
- var ls = hr.responseXML.getElementsByTagName('item');
- var l = x('scr_table').getElementsByTagName('tr');
- var tr;
- for(var s=0;s<ls.length;s++) {
- for(i=0;i<l.length-1;i++)
- if(l[i].scrId == ls[s].getAttribute('id') && ls[s].getAttribute('processed') == "1")
- tr = l[i];
- if(!tr)
- continue;
-
- tr.scrStatus = 0;
- tr.getElementsByTagName('td')[0].innerHTML =
- '<a href="'+scrURL(tr.scrId, 'f')+'" rel="iv:'+ls[s].getAttribute('width')+'x'+ls[s].getAttribute('height')+':edit">'
- +'<img src="'+scrURL(tr.scrId, 't')+'" style="margin: 0; padding: 0; border: 0" /></a>';
-
- var opt='';
- for(var o=0;o<scrRel.length;o++)
- opt += '<option value="'+scrRel[o][0]+'"'+(tr.scrRel && tr.scrRel == scrRel[o][0] ? ' selected="selected"' : '')+'>'+scrRel[o][1]+'</option>';
-
- tr.getElementsByTagName('td')[1].innerHTML = '<b>Screenshot #'+tr.scrId+'</b>'
- +' (<a href="#" onclick="return scrDel(this)">remove</a>)<br />'
- +'Full size: '+ls[s].getAttribute('width')+'x'+ls[s].getAttribute('height')+'<br /><br />'
- +'<input type="checkbox" onclick="scrSerialize()" id="scr_ser_'+tr.scrId+'" name="scr_ser_'+tr.scrId+'"'
- +' '+(tr.scrNSFW > 0 ? 'checked = "checked"' : '')+' />'
- +'<label for="scr_ser_'+tr.scrId+'">This screenshot is NSFW</label><br />'
- +'<select onchange="scrSerialize()">'+opt+'</select>';
- }
- scrSerialize();
- ivInit();
- clearTimeout(ti);
- setTimeout(scrCheckStatus, 1000);
- });
-}
-
-function scrDel(what) {
- while(what.nodeName.toLowerCase() != 'tr')
- what = what.parentNode;
- what.scrStatus = 3;
- x('scr_table').removeChild(what);
- scrSerialize();
- scrLast();
- return false;
-}
-
-var scrUplNr=0;
-function scrUpload() {
- scrUplNr++;
-
- // create temporary form
- var d = document.createElement('div');
- d.style.cssText = 'visibility: hidden; overflow: hidden; width: 1px; height: 1px; position: absolute; left: -500px; top: -500px';
- d.innerHTML = '<iframe id="scr_upl_'+scrUplNr+'" name="scr_upl_'+scrUplNr+'" style="height: 0px; width: 0px; visibility: hidden"'
- +' src="about:blank" onload="scrUploadComplete(this)"></iframe>'
- +'<form method="post" action="/xml/screenshots.xml" target="scr_upl_'+scrUplNr+'" enctype="multipart/form-data" id="scr_frm_'+scrUplNr+'"></form>';
- document.body.appendChild(d);
-
- // submit form and delete it
- d = x('scr_frm_'+scrUplNr);
- d.appendChild(x('scr_upload'));
- d.submit();
- d.parentNode.removeChild(d);
-
- d = scrAdd(0, 0, 0);
- x('scr_upl_'+scrUplNr).theTR = d;
- scrLast();
-
- return false;
-}
-
-function scrUploadComplete(what) {
- var f = window.frames[what.id];
- if(f.location.href.indexOf('screenshots') < 0)
- return;
-
- var tr = what.theTR;
- if(!tr || tr.scrStatus == 3)
- return;
-
- try {
- tr.scrId = f.window.document.getElementsByTagName('image')[0].getAttribute('id');
- } catch(e) {
- tr.scrId = -10;
- }
- if(tr.scrId < 0) {
- alert(
- tr.scrId == -10 ?
- 'Oops! Seems like something went wrong...\n'
- +'Make sure the file you\'re uploading doesn\'t exceed 5MB in size.\n'
- +'If that isn\'t the problem, then please report a bug.' :
- tr.scrId == -1 ?
- 'Upload failed!\nOnly JPEG or PNG images are accepted.' :
- 'Upload failed!\nNo file selected, or an empty file?'
- );
- return scrDel(tr);
- }
-
- tr.scrStatus = 2;
- tr.getElementsByTagName('td')[1].innerHTML =
- '<b>Generating thumbnail...</b><br />'
- +'Note: if this takes longer than 30 seconds, there\'s probably something wrong on our side.'
- +'Please try again later or report a bug if that is the case.';
-
- // remove the <div> in a timeout, otherwise some browsers think the page is still loading
- setTimeout(function() { document.body.removeChild(what.parentNode) }, 100);
-}
-
-function scrSerialize() {
- var r = '';
- var l = x('scr_table').getElementsByTagName('tr');
- for(var i=0;i<l.length-1;i++)
- if(l[i].scrStatus == 0)
- r += (r ? ' ' : '') + l[i].scrId + ','
- + (l[i].getElementsByTagName('input')[0].checked ? 1 : 0) + ','
- + scrRel[l[i].getElementsByTagName('select')[0].selectedIndex][0];
- x('screenshots').value = r;
-}
-
-
-
-
-
-
- /***************\
- * M E D I A *
- \***************/
-
-
-var medTypes = [ [ '', '- medium -', false ] ];
-function medLoad() {
- // load the medTypes and clear the div
- var l = x('media_div').getElementsByTagName('select')[0].options;
- for(var i=0;i<l.length;i++)
- medTypes[medTypes.length] = [ l[i].value, l[i].text, l[i].className.indexOf('noqty') ? false : true ];
- x('media_div').innerHTML = '';
-
- // load the selected media
- l = x('media').value.split(',');
- for(var i=0;i<l.length;i++)
- if(l[i].length > 2)
- medAddNew(l[i].split(' ')[0], Math.floor(l[i].split(' ')[1]));
-
- medAddNew('', 0);
- medSetSubmit();
-}
-
-function medSetSubmit() {
- var o=x('media');
- while(o.nodeName.toLowerCase() != 'form')
- o = o.parentNode;
- oldfunc = o.onsubmit;
- o.onsubmit = function() {
- var l = x('media_div').getElementsByTagName('span');
- for(var i=0;i<l.length-1;i++) {
- var s = l[i].getElementsByTagName('select');
- if(!medTypes[s[1].selectedIndex][2] && s[0].selectedIndex == 0) {
- alert('Media '+medTypes[s[1].selectedIndex][1]+' requires a quantity to be specified!');
- return false;
- }
- }
- return oldfunc ? oldfunc() : true;
- };
-}
-
-function medAddNew(med, qty) {
- var o = document.createElement('span');
- var r = '<select class="qty" onchange="medSerialize()"><option value="0">- quantity -</option>';
- for(var i=1;i<=20;i++)
- r += '<option value="'+i+'"'+(qty == i ? ' selected="selected"' : '')+'>'+i+'</option>';
- r += '</select><select class="medium" onchange="return medCheckNew(this)">';
- for(i=0;i<medTypes.length;i++)
- r += '<option value="'+medTypes[i][0]+'"'+(med == medTypes[i][0] ? ' selected="selected"' : '')+'>'+medTypes[i][1]+'</option>';
- r += '</select>';
- if(med != '')
- r += '<input type="button" class="submit" onclick="return medDel(this)" value="remove" />';
- o.innerHTML = r;
- x('media_div').appendChild(o);
-}
-
-function medDel(what) {
- what = what.nodeName ? what : this;
- while(what.nodeName.toLowerCase() != 'span')
- what = what.parentNode;
- x('media_div').removeChild(what);
- medSerialize();
- return false;
-}
-
-function medCheckNew() {
- // check for non-new items and add remove buttons
- var l = x('media_div').getElementsByTagName('span');
- var createnew=1;
- for(var i=0;i<l.length;i++) {
- var sel = l[i].getElementsByTagName('select')[1].selectedIndex;
- if(sel == 0)
- createnew = 0;
- else if(l[i].getElementsByTagName('input').length < 1) {
- var a = document.createElement('input');
- a.type = 'button';
- a.className = 'submit';
- a.onclick = medDel;
- a.value = 'remove';
- l[i].appendChild(a);
- }
- }
- if(createnew)
- medAddNew('', 0);
- medSerialize();
-
- return true;
-}
-
-function medSerialize() {
- var r = '';
- var l = x('media_div').getElementsByTagName('span');
- for(var i=0;i<l.length;i++) {
- var sel = l[i].getElementsByTagName('select');
- if(sel[1].selectedIndex != 0)
- r += (r ? ',' : '') + medTypes[sel[1].selectedIndex][0] + ' ' + (medTypes[sel[1].selectedIndex][2] ? 0 : sel[0].selectedIndex);
- }
- x('media').value = r;
-}
-
-
-
-
-
-
- /****************************************************\
- * V I S U A L N O V E L S / P R O D U C E R S *
- \****************************************************/
-
-
-function vnpLoad(type) {
- // load currently selected VNs
- var l = x(type).value.split('|||');
- for(var i=0;i<l.length;i++)
- if(l[i].length > 2)
- vnpAdd(type, l[i].split(',',2)[0], l[i].split(',',2)[1]);
- vnpCheckEmpty(type);
-
- // dropdown
- var n = x('jt_box_'+(type == 'vn' ? 'rel_vn' : 'rel_prod')).getElementsByTagName('div')[1];
- dsInit(n.getElementsByTagName('input')[0], '/xml/'+type+'.xml?q=', function(item, tr) {
- var td = document.createElement('td');
- td.innerHTML = type.substr(0,1)+item.getAttribute('id');
- td.style.textAlign = 'right';
- td.style.paddingRight = '5px';
- tr.appendChild(td);
- td = document.createElement('td');
- td.innerHTML = shorten(item.firstChild.nodeValue, 40);
- tr.appendChild(td);
- }, function(item) {
- return type.substr(0,1)+item.getAttribute('id')+':'+item.firstChild.nodeValue;
- }, function() { vnpFormAdd(type) });
- n.getElementsByTagName('a')[0].onclick = function() { vnpFormAdd(type); return false };
-}
-
-function vnpAdd(type, id, title) {
- var o = document.createElement('span');
- o.innerHTML = '<i>'+type.substr(0,1)+id+':<a href="/'+type.substr(0,1)+id+'">'+shorten(title, 40)+'</a></i>'
- +'<a href="#" onclick="return vnpDel(this, \''+type+'\')">remove</a>';
- x(type+'sel').appendChild(o);
- vnpStripe(type);
- vnpCheckEmpty(type);
-}
-
-function vnpDel(what, type) {
- what = what.nodeName ? what : this;
- while(what.nodeName.toLowerCase() != 'span')
- what = what.parentNode;
- x(type+'sel').removeChild(what);
- vnpCheckEmpty(type);
- vnpSerialize(type);
- return false;
-}
-
-function vnpCheckEmpty(type) {
- var o = x(type+'sel');
- if(o.getElementsByTagName('span').length < 1) {
- if(o.getElementsByTagName('b').length < 1)
- o.innerHTML = '<b>Nothing selected...</b>';
- } else if(o.getElementsByTagName('b').length == 1)
- o.removeChild(o.getElementsByTagName('b')[0]);
-}
-
-function vnpStripe(type) {
- var l = x(type+'sel').getElementsByTagName('span');
- for(var i=0;i<l.length;i++)
- l[i].className = i%2 ? 'odd' : '';
-}
-
-function vnpFormAdd(type) {
- var n = x('jt_box_'+(type == 'vn' ? 'rel_vn' : 'rel_prod')).getElementsByTagName('div')[1];
- var txt = n.getElementsByTagName('input')[0];
- var lnk = n.getElementsByTagName('a')[0];
- var input = txt.value;
-
- if(type == 'vn' && !input.match(/^v[0-9]+/)) {
- alert('Visual novel textbox must start with an ID (e.g. v17)');
- return false;
- }
- if(type == 'producers' && !input.match(/^p[0-9]+/)) {
- alert('Producer textbox must start with an ID (e.g. p5)');
- return false;
- }
-
- txt.disabled = true;
- txt.value = 'loading...';
- lnk.innerHTML = 'loading...';
-
- ajax('/xml/'+type+'.xml?q='+encodeURIComponent(input), function(hr) {
- txt.disabled = false;
- txt.value = '';
- lnk.innerHTML = 'add';
-
- var items = hr.responseXML.getElementsByTagName('item');
- if(items.length < 1)
- return alert('Item not found!');
-
- vnpAdd(type, items[0].getAttribute('id'), items[0].firstChild.nodeValue);
- vnpSerialize(type);
- });
- return false;
-}
-
-function vnpSerialize(type) {
- var r = '';
- var l = x(type+'sel').getElementsByTagName('span');
- for(var i=0;i<l.length;i++)
- r += (r ? '|||' : '') + l[i].getElementsByTagName('i')[0].innerHTML.substr(1, l[i].getElementsByTagName('i')[0].innerHTML.indexOf(':')-1)
- + ',' + l[i].getElementsByTagName('a')[0].innerHTML;
- x(type).value = r;
-}
-
-
-
-
-
-
- /****************************************************\
- * V I S U A L N O V E L T A G L I N K I N G *
- \****************************************************/
-
-
-function tglLoad() {
- var n = x('tagtable').getElementsByTagName('tfoot')[0].getElementsByTagName('input');
- dsInit(n[1], '/xml/tags.xml?q=', function(item, tr) {
- var td = document.createElement('td');
- td.innerHTML = shorten(item.firstChild.nodeValue, 40);
- if(item.getAttribute('meta') == 'yes')
- td.innerHTML += ' <b class="grayedout">meta</b>';
- else if(item.getAttribute('state') == 0)
- td.innerHTML += ' <b class="grayedout">awaiting moderation</b>';
- tr.appendChild(td);
- }, function(item) {
- return item.firstChild.nodeValue;
- }, tglAdd);
- n[2].onclick = tglAdd;
-
- tglStripe();
- var l = x('tagtable').getElementsByTagName('tbody')[0].getElementsByTagName('tr');
- for(var i=0; i<l.length;i++) {
- var o = l[i].getElementsByTagName('td');
- tglSpoiler(o[2], parseInt(o[2].innerHTML));
- tglVoteBar(o[1], parseInt(o[1].innerHTML));
- }
-}
-
-function tglSpoiler(obj, spoil) {
- var r = '<select onchange="tglSerialize()">';
- for(var i=-1; i<=2; i++)
- r += '<option value="'+i+'"'+(spoil==i?' selected="selected"':'')+'>'
- +(i == -1 ? 'neutral' : i == 0 ? 'no spoiler' : i == 1 ? 'minor spoiler' : 'major spoiler')
- +'&nbsp;</option>';
- obj.innerHTML = r+'</select>';
-}
-
-function tglVoteBar(obj, vote) {
- var r = '';
- for(var i=-3;i<=3;i++)
- r += '<a href="#" class="taglvl taglvl'+i+'" onmouseover="tglVoteBarSel(this, '+i+')"'
- + ' onmouseout="tglVoteBarSel(this, '+vote+')" onclick="return tglVoteBar(this.parentNode, '+i+')">&nbsp;</a>';
- obj.innerHTML = r;
- tglVoteBarSel(obj, vote);
- tglSerialize();
- return false;
-}
-
-function tglVoteBarSel(obj, vote) {
- if(obj.className.indexOf('taglvl') >= 0)
- obj = obj.parentNode;
- var l = obj.getElementsByTagName('a');
- var num;
- for(var i=0; i<l.length; i++) {
- if((num = l[i].className.replace(/^.*taglvl(-?[0-3]).*$/, "$1")) == l[i].className)
- continue;
- if(num == 0)
- l[i].innerHTML = vote == 0 ? '-' : vote;
- else if(num<0&&vote<=num || num>0&&vote>=num) {
- if(l[i].className.indexOf('taglvlsel') < 0)
- l[i].className += ' taglvlsel';
- } else
- if(l[i].className.indexOf('taglvlsel') >= 0)
- l[i].className = l[i].className.replace(/taglvlsel/, '');
- }
-}
-
-function tglAdd() {
- var n = x('tagtable').getElementsByTagName('tfoot')[0].getElementsByTagName('input');
- n[1].disabled = n[2].disabled = true;
- n[2].value = 'loading...';
- ajax('/xml/tags.xml?q=name:'+encodeURIComponent(n[1].value), function(hr) {
- n[1].disabled = n[1].disabled = false;
- n[2].value = 'Add tag';
- n[1].value = '';
-
- var items = hr.responseXML.getElementsByTagName('item');
- if(items.length < 1)
- return alert('Item not found!');
- if(items[0].getAttribute('meta') == 'yes')
- return alert('Can\'t use meta tags here!');
- var name = items[0].firstChild.nodeValue;
- var l = x('tagtable').getElementsByTagName('a');
- for(var i=0; i<l.length; i++)
- if(l[i].innerHTML == qq(name))
- return alert('Tag is already present!');
-
- var tr = document.createElement('tr');
- var td = document.createElement('td');
- td.innerHTML = '<a href="/g'+items[0].getAttribute('id')+'">'+qq(name)+'</a>';
- td.className = 'tc1';
- tr.appendChild(td);
- td = document.createElement('td');
- tglVoteBar(td, 2);
- td.className = 'tc2';
- tr.appendChild(td);
- td = document.createElement('td');
- tglSpoiler(td, -1);
- td.className = 'tc3';
- tr.appendChild(td);
- td = document.createElement('td');
- td.className = 'tc4';
- td.innerHTML = '-';
- tr.appendChild(td);
- td = document.createElement('td');
- td.innerHTML = '-';
- td.className = 'tc5';
- tr.appendChild(td);
- x('tagtable').getElementsByTagName('tbody')[0].appendChild(tr);
- tglStripe();
- tglSerialize();
- });
-}
-
-function tglStripe() {
- var l = x('tagtable').getElementsByTagName('tbody')[0].getElementsByTagName('tr');
- for(var i=0;i<l.length;i++)
- l[i].className = i%2 ? 'odd' : '';
-}
-
-function tglSerialize() {
- var r = '';
- var l = x('tagtable').getElementsByTagName('tbody')[0].getElementsByTagName('tr');
- for(var i=0; i<l.length;i++) {
- var lnk = l[i].getElementsByTagName('a')[0].href;
- var vt = l[i].getElementsByTagName('td')[1].getElementsByTagName('a');
- var id;
- if((id = lnk.replace(/^.*g([1-9][0-9]*)$/, "$1")) != lnk && vt.length > 3 && vt[3].innerHTML != '-')
- r += (r?' ':'')+id+','+vt[3].innerHTML+','+(l[i].getElementsByTagName('select')[0].selectedIndex-1);
- }
- x('taglinks').value = r;
-}
-
-
diff --git a/static/f/icons.png b/static/f/icons.png
index 78ec79fc..42c9004d 100644
--- a/static/f/icons.png
+++ b/static/f/icons.png
Binary files differ
diff --git a/static/f/script.js b/static/f/script.js
deleted file mode 100644
index 987963dc..00000000
--- a/static/f/script.js
+++ /dev/null
@@ -1,635 +0,0 @@
-
-/* G L O B A L S T U F F */
-
-function x(y){return document.getElementById(y)}
-function cl(o,f){if(x(o))x(o).onclick=f}
-function DOMLoad(y){var d=0;var f=function(){if(d++)return;y()};
-if(document.addEventListener)document.addEventListener("DOMCont"
-+"entLoaded",f,false);document.write("<script id=_ie defer src="
-+"javascript:void(0)><\/script>");document.getElementById('_ie')
-.onreadystatechange=function(){if(this.readyState=="complete")f()
-};if(/WebKit/i.test(navigator.userAgent))var t=setInterval(
-function(){if(/loaded|complete/.test(document.readyState)){
-clearInterval(t);f()}},10);window.onload=f;}
-
-var http_request = false;
-function ajax(url, func) {
- if(http_request)
- http_request.abort();
- http_request = (window.ActiveXObject) ? new ActiveXObject('Microsoft.XMLHTTP') : new XMLHttpRequest();
- if(http_request == null) {
- alert("Your browse does not support the functionality this website requires.");
- return;
- }
- http_request.onreadystatechange = function() {
- if(!http_request || http_request.readyState != 4 || !http_request.responseText)
- return;
- if(http_request.status != 200)
- return alert('Whoops, error! :(');
- func(http_request);
- };
- url += (url.indexOf('?')>=0 ? ';' : '?')+(Math.floor(Math.random()*999)+1);
- http_request.open('GET', url, true);
- http_request.send(null);
-}
-
-function setCookie(n,v) {
- var date = new Date();
- date.setTime(date.getTime()+(365*24*60*60*1000));
- document.cookie = n+'='+v+'; expires='+date.toGMTString()+'; path=/';
-}
-function readCookie(n) {
- var l = document.cookie.split(';');
- for(var i=0; i<l.length; i++) {
- var c = l[i];
- while(c.charAt(0) == ' ')
- c = c.substring(1,c.length);
- if(c.indexOf(n+'=') == 0)
- return c.substring(n.length+1,c.length);
- }
- return null;
-}
-
-
-
-
-/* I M A G E V I E W E R */
-
-function ivInit() {
- var init = 0;
- var l = document.getElementsByTagName('a');
- for(var i=0;i<l.length;i++)
- if(l[i].rel.substr(0,3) == 'iv:') {
- init++;
- l[i].onclick = ivView;
- }
- if(init && !x('iv_view')) {
- var d = document.createElement('div');
- d.id = 'iv_view';
- d.innerHTML = '<b id="ivimg"></b><br />'
- +'<a href="#" id="ivfull">&nbsp;</a>'
- +'<a href="#" onclick="return ivClose()" id="ivclose">close</a>'
- +'<a href="#" onclick="return ivView(this)" id="ivprev">&laquo; previous</a>'
- +'<a href="#" onclick="return ivView(this)" id="ivnext">next &raquo;</a>';
- document.body.appendChild(d);
- d = document.createElement('b');
- d.id = 'ivimgload';
- d.innerHTML = 'Loading...';
- document.body.appendChild(d);
- }
-}
-
-function ivView(what) {
- what = what && what.rel ? what : this;
- var u = what.href;
- var opt = what.rel.split(':');
- d = x('iv_view');
-
- // fix prev/next links (if any)
- if(opt[2]) {
- var ol = document.getElementsByTagName('a');
- var l=[];
- for(i=0;i<ol.length;i++)
- if(ol[i].rel.substr(0,3) == 'iv:' && ol[i].rel.indexOf(':'+opt[2]) > 4 && ol[i].className.indexOf('hidden') < 0 && ol[i].id != 'ivprev' && ol[i].id != 'ivnext')
- l[l.length] = ol[i];
- for(i=0;i<l.length;i++)
- if(l[i].href == u) {
- x('ivnext').style.visibility = l[i+1] ? 'visible' : 'hidden';
- x('ivnext').href = l[i+1] ? l[i+1].href : '#';
- x('ivnext').rel = l[i+1] ? l[i+1].rel : '';
- x('ivprev').style.visibility = l[i-1] ? 'visible' : 'hidden';
- x('ivprev').href = l[i-1] ? l[i-1].href : '#';
- x('ivprev').rel = l[i-1] ? l[i-1].rel : '';
- }
- } else
- x('ivnext').style.visibility = x('ivprev').style.visibility = 'hidden';
-
- // calculate dimensions
- var w = Math.floor(opt[1].split('x')[0]);
- var h = Math.floor(opt[1].split('x')[1]);
- var ww = typeof(window.innerWidth) == 'number' ? window.innerWidth : document.documentElement.clientWidth;
- var wh = typeof(window.innerHeight) == 'number' ? window.innerHeight : document.documentElement.clientHeight;
- var st = typeof(window.pageYOffset) == 'number' ? window.pageYOffset : document.body && document.body.scrollTop ? document.body.scrollTop : document.documentElement.scrollTop;
- if(w+100 > ww || h+70 > wh) {
- x('ivfull').href = u;
- x('ivfull').innerHTML = w+'x'+h;
- x('ivfull').style.visibility = 'visible';
- if(w/h > ww/wh) { // width++
- h *= (ww-100)/w;
- w = ww-100;
- } else { // height++
- w *= (wh-70)/h;
- h = wh-70;
- }
- } else
- x('ivfull').style.visibility = 'hidden';
- var dw = w;
- var dh = h+20;
- dw = dw < 200 ? 200 : dw;
-
- // update document
- d.style.display = 'block';
- x('ivimg').innerHTML = '<img src="'+u+'" onclick="ivClose()" onload="document.getElementById(\'ivimgload\').style.top=\'-400px\'" style="width: '+w+'px; height: '+h+'px" />';
- d.style.width = dw+'px';
- d.style.height = dh+'px';
- d.style.left = ((ww - dw) / 2 - 10)+'px';
- d.style.top = ((wh - dh) / 2 + st - 20)+'px';
- x('ivimgload').style.left = ((ww - 100) / 2 - 10)+'px';
- x('ivimgload').style.top = ((wh - 20) / 2 + st)+'px';
- return false;
-}
-
-function ivClose() {
- x('iv_view').style.display = 'none';
- x('iv_view').style.top = '-5000px';
- x('ivimgload').style.top = '-400px';
- x('ivimg').innerHTML = '';
- return false;
-}
-
-
-
-
-
-
-/* V N L I S T D R O P D O W N */
-
-var rstat = [ 'Unknown', 'Pending', 'Obtained', 'On loan', 'Deleted' ];
-var vstat = [ 'Unknown', 'Playing', 'Finished', 'Stalled', 'Dropped' ];
-function vlDropDown(e) {
- e = e || window.event;
- var tg = e.target || e.srcElement;
- while(tg && (tg.nodeType == 3 || tg.nodeName.toLowerCase() != 'a'))
- tg = tg.parentNode;
-
- var o = x('vldd');
- if(!o && (!tg || tg.id.substr(0,6) != 'rlsel_'))
- return;
-
- if(o) {
- var mouseX = e.pageX || (e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft);
- var mouseY = e.pageY || (e.clientY + document.body.scrollTop + document.documentElement.scrollTop);
- if((mouseX < ddx-5 || mouseX > ddx+o.offsetWidth+100 || mouseY < ddy-5 || mouseY > ddy+o.offsetHeight+5)
- || (tg && tg.id.substr(0,6) == 'rlsel_' && tg.id != 'rlsel_'+o.relId)) {
- document.body.removeChild(o);
- o = null;
- }
- }
- if(!o && tg) {
- o = tg;
- ddx = ddy = 0;
- do {
- ddx += o.offsetLeft;
- ddy += o.offsetTop;
- } while(o = o.offsetParent);
- ddx -= 185;
-
- var cu = tg.id.substr(6);
- var st = tg.innerHTML.split(' / ');
- if(st[0].indexOf('loading') >= 0)
- return;
- var r = '<ul><li><b>Release status</b></li>';
- for(var i=0;i<rstat.length;i++)
- r += st[0] && st[0].indexOf(rstat[i]) >= 0 ? '<li><i>'+rstat[i]+'</i></li>' : '<li><a href="#" onclick="return vlMod('+cu+',\'r'+i+'\')">'+rstat[i]+'</a></li>';
- r += '</ul><ul><li><b>Play status</b></li>';
- for(var i=0;i<vstat.length;i++)
- r += st[1] && st[1].indexOf(vstat[i]) >= 0 ? '<li><i>'+vstat[i]+'</i></li>' : '<li><a href="#" onclick="return vlMod('+cu+',\'v'+i+'\')">'+vstat[i]+'</a></li>';
- r += '</ul>';
- if(tg.innerHTML != '--')
- r += '<ul class="full"><li><a href="#" onclick="return vlMod('+cu+',\'del\')">Remove from VN list</a></li></ul>';
-
- o = document.createElement('div');
- o.id = 'vldd';
- o.relId = tg.id.substr(6);
- o.style.left = ddx+'px';
- o.style.top = ddy+'px';
- o.innerHTML = r;
- document.body.appendChild(o);
- }
-}
-
-function vlMod(rid, act) {
- document.body.removeChild(x('vldd'));
- x('rlsel_'+rid).innerHTML = '<b class="patch">loading...</b>';
- ajax('/xml/rlist.xml?id='+rid+';e='+act, function(hr) {
- x('rlsel_'+rid).innerHTML = hr.responseXML.getElementsByTagName('rlist')[0].firstChild.nodeValue;
- });
- return false;
-}
-
-
-
-
-
-
-/* J A V A S C R I P T T A B S */
-
-function jtInit() {
- var sel = '';
- var first = '';
- var l = x('jt_select').getElementsByTagName('a');
- for(var i=0;i<l.length;i++)
- if(l[i].id.substr(0,7) == 'jt_sel_') {
- l[i].onclick = jtSel;
- if(!first)
- first = l[i].id;
- if(location.hash && l[i].id == 'jt_sel_'+location.hash.substr(1))
- sel = l[i].id;
- }
- if(!first)
- return;
- if(!sel)
- sel = first;
- jtSel(sel, 1);
-}
-
-function jtSel(which, nolink) {
- which = typeof(which) == 'string' ? which : which && which.id ? which.id : this.id;
- which = which.substr(7);
-
- var l = x('jt_select').getElementsByTagName('a');
- for(var i=0;i<l.length;i++)
- if(l[i].id.substr(0,7) == 'jt_sel_') {
- var name = l[i].id.substr(7);
- if(name != 'all')
- x('jt_box_'+name).style.display = name == which || which == 'all' ? 'block' : 'none';
- var o = x('jt_sel_'+name).parentNode;
- if(o.className.indexOf('tabselected') >= 0) {
- if(name != which)
- o.className = o.className.replace(/tabselected/, '');
- } else
- if(name == which)
- o.className += ' tabselected';
- }
-
- if(!nolink)
- location.href = '#'+which;
- return false;
-}
-
-
-
-
-/* Tag VN spoilers */
-/* lvl = null to not change lvl, lim = null to not change limit */
-function tvsSet(lvl, lim) {
- var l = x('tagops').getElementsByTagName('a');
- for(var i=0;i<l.length;i++) {
- if(i < 3) {
- if(lvl == null) { /* determine level */
- if(l[i].className.indexOf('tsel') >= 0)
- lvl = i;
- } else { /* set level */
- if(i == lvl && l[i].className.indexOf('tsel') < 0)
- l[i].className += ' tsel';
- else if(i != lvl && l[i].className.indexOf('tsel') >= 0)
- l[i].className = l[i].className.replace(/tsel/, '');
- }
- } else {
- if(lim == null) { /* determine limit */
- if(l[i].className.indexOf('tsel') >= 0)
- lim = i == 3;
- } else { /* set limit */
- if((i == 3) == lim && l[i].className.indexOf('tsel') < 0)
- l[i].className += ' tsel';
- else if((i == 3) != lim && l[i].className.indexOf('tsel') >= 0)
- l[i].className = l[i].className.replace(/tsel/, '');
- }
- }
- }
-
- l = x('vntags').getElementsByTagName('span');
- lim = lim ? 15 : 999;
- var s=0;
- for(i=0;i<l.length;i++) {
- if((lvl < l[i].className.substr(6, 1) || s>=lim) && l[i].className.indexOf('hidden') < 0)
- l[i].className += ' hidden';
- if(lvl >= l[i].className.substr(6, 1) && ++s<=lim && l[i].className.indexOf('hidden') >= 0)
- l[i].className = l[i].className.replace(/hidden/, '');
- }
- return false;
-}
-
-
-
-
-/* date input */
-var months = ['January','February','March','April','May','June','July','August','September','October','November','December'];
-function dtLoad(obj) {
- var r = Math.floor(obj.value) || 0;
- var v = [ Math.floor(r/10000), Math.floor(r/100)%100, r%100 ];
- var i;
- r = '<select onchange="dtSerialize(this)" style="width: 70px"><option value="0">-year-</option>';
- for(i=1980; i<=(new Date()).getFullYear()+5; i++)
- r += '<option value="'+i+'"'+(i == v[0] ? ' selected="selected"':'')+'>'+i+'</option>';
- r += '<option value="9999"'+(v[0] == 9999 ? ' selected="selected"':'')+'>TBA</option>';
- r += '</select><select onchange="dtSerialize(this)" style="width: 100px"><option value="99">-month-</option>';
- for(i=1; i<=12; i++)
- r += '<option value="'+i+'"'+(i == v[1] ? ' selected="selected"':'')+'>'+months[i-1]+'</option>';
- r += '</select><select onchange="dtSerialize(this)" style="width: 70px"><option value="99">-day-</option>';
- for(i=1; i<=31; i++)
- r += '<option value="'+i+'"'+(i == v[2] ? ' selected="selected"':'')+'>'+i+'</option>';
- r += '</select>';
- v = document.createElement('div');
- v.obj = obj;
- v.innerHTML = r;
- obj.parentNode.insertBefore(v, obj);
-}
-function dtSerialize(obj) {
- obj = obj.parentNode;
- var l = obj.getElementsByTagName('select');
- var v = [ l[0].options[l[0].selectedIndex].value*1, l[1].options[l[1].selectedIndex].value*1, l[2].options[l[2].selectedIndex].value*1 ];
- obj = obj.obj;
- if(v[0] == 0) obj.value = 0;
- else if(v[0] == 9999) obj.value = 99999999;
- else obj.value = v[0]*10000 + v[1]*100 + (v[1]==99?99:v[2]);
-}
-
-
-
-/* O N L O A D E V E N T */
-
-DOMLoad(function() {
-
- // search box
- var i = x('sq');
- i.onfocus = function () {
- if(this.value == 'search') {
- this.value = '';
- this.style.fontStyle = 'normal'
- }
- };
- i.onblur = function () {
- if(this.value.length < 1) {
- this.value = 'search';
- this.style.fontStyle = 'italic'
- }
- };
-
-
- // VN Voting
- i = x('votesel');
- if(i)
- i.onchange = function() {
- var s = this.options[this.selectedIndex].value;
- if(s == 1 && !confirm(
- "You are about to give this visual novel a 1 out of 10. This is a rather extreme rating, "
- +"meaning this game has absolutely nothing to offer, and that it's the worst game you have ever played.\n"
- +"Are you really sure this visual novel matches that description?"))
- return;
- if(s == 10 && !confirm(
- "You are about to give this visual novel a 10 out of 10. This is a rather extreme rating, "
- +"meaning this is one of the best visual novels you've ever played and it's unlikely "
- +"that any other game could ever be better than this one.\n"
- +"It is generally a bad idea to have more than three games in your vote list with this rating, choose carefully!"))
- return;
- if(s)
- location.href = location.href.replace(/\.[0-9]+/, '')+'/vote?v='+s;
- };
-
- // VN Wishlist editing
- i = x('wishsel');
- if(i)
- i.onchange = function() {
- if(this.selectedIndex != 0)
- location.href = location.href.replace(/\.[0-9]+/, '')+'/wish?s='+this.options[this.selectedIndex].value;
- };
- // Batch Wishlist editing
- i = x('batchedit');
- if(i)
- i.onchange = function() {
- var frm = this;
- while(frm.nodeName.toLowerCase() != 'form')
- frm = frm.parentNode;
- if(this.selectedIndex != 0)
- frm.submit();
- };
-
-
- // Release list editing
- i = x('listsel');
- if(i)
- i.onchange = function() {
- if(this.selectedIndex != 0)
- location.href = location.href.replace(/\.[0-9]+/, '')+'/list?e='+this.options[this.selectedIndex].value;
- };
-
- // User VN list
- // (might want to make this a bit more generic, as it's now also used for the user tag list)
- i = x('relhidall');
- if(i) {
- var l = document.getElementsByTagName('tr');
- for(var i=0;i<l.length;i++)
- if(l[i].className.indexOf('relhid') >= 0)
- l[i].style.display = 'none';
- var l = document.getElementsByTagName('td');
- for(var i=0;i<l.length;i++)
- if(l[i].className.indexOf('relhid_but') >= 0)
- l[i].onclick = function() {
- var l = document.getElementsByTagName('tr');
- for(var i=0;i<l.length;i++)
- if(l[i].className.substr(7) == this.id) {
- l[i].style.display = l[i].style.display == 'none' ? '' : 'none';
- this.getElementsByTagName('i')[0].innerHTML = l[i].style.display == 'none' ? '&#9656;' : '&#9662;';
- }
- };
- var allhid = 1;
- x('relhidall').onclick = function() {
- allhid = !allhid;
- var l = document.getElementsByTagName('tr');
- for(var i=0;i<l.length;i++)
- if(l[i].className.indexOf('relhid') >= 0) {
- l[i].style.display = allhid ? 'none' : '';
- x(l[i].className.substr(7)).getElementsByTagName('i')[0].innerHTML = allhid ? '&#9656;' : '&#9662;';
- }
- this.getElementsByTagName('i')[0].innerHTML = allhid ? '&#9656;' : '&#9662;';
- };
- }
-
- // Advanced search
- cl('advselect', function() {
- var e = x('advoptions');
- e.className = e.className.indexOf('hidden')>=0 ? '' : 'hidden';
- this.getElementsByTagName('i')[0].innerHTML = e.className.indexOf('hidden')>=0 ? '&#9656;' : '&#9662;';
- return false;
- });
-
- // auto-complete tag search
- if(x('advselect') && x('ti')) {
- var fields=['ti','te'];
- for(var field=0;field<fields.length;field++)
- dsInit(x(fields[field]), '/xml/tags.xml?q=',
- function(item, tr) {
- var td = document.createElement('td');
- td.innerHTML = shorten(item.firstChild.nodeValue, 40);
- if(item.getAttribute('meta') == 'yes')
- td.innerHTML += ' <b class="grayedout">meta</b>';
- else if(item.getAttribute('state') == 0)
- td.innerHTML += ' <b class="grayedout">awaiting moderation</b>';
- tr.appendChild(td);
- },
- function(item, obj) {
- var tags = obj.value.split(/ *, */);
- tags[tags.length-1] = item.firstChild.nodeValue;
- return tags.join(', ');
- },
- function() { false; },
- function(val) { return (val.split(/, */))[val.split(/, */).length-1]; }
- );
- }
-
- // update spoiler cookie on VN search radio button
- if(x('sp_0')) {
- cl('sp_0', function(){setCookie('tagspoil',0)});
- cl('sp_1', function(){setCookie('tagspoil',1)});
- cl('sp_2', function(){setCookie('tagspoil',2)});
- if((i = readCookie('tagspoil')) == null)
- i = 1;
- x('sp_'+i).checked = true;
- }
-
- // show/hide NSFW VN image
- if(x('nsfw_show'))
- x('nsfw_show').getElementsByTagName('a')[0].onclick = function() {
- x('nsfw_show').style.display = 'none';
- x('nsfw_hid').style.display = 'block';
- x('nsfw_hid').onclick = function() {
- x('nsfw_show').style.display = 'block';
- x('nsfw_hid').style.display = 'none';
- };
- return false
- };
-
- // NSFW toggle for screenshots
- cl('nsfwhide', function() {
- var s=0;
- var l = x('screenshots').getElementsByTagName('div');
- for(var i=0;i<l.length;i++) {
- if(l[i].className.indexOf('nsfw') >= 0) {
- if(l[i].className.indexOf('hidden') >= 0) {
- s++;
- l[i].className = 'nsfw';
- l[i].getElementsByTagName('a')[0].className = '';
- } else {
- l[i].className += ' hidden';
- l[i].getElementsByTagName('a')[0].className = 'hidden';
- }
- } else
- s++;
- }
- x('nsfwshown').innerHTML = s;
- return false;
- });
-
- // initialize image viewer
- ivInit();
-
- // vnlist dropdown
- var l = document.getElementsByTagName('a');
- for(var i=0;i<l.length;i++)
- if(l[i].id.substr(0,6) == 'rlsel_') {
- document.onmousemove = vlDropDown;
- break;
- }
-
- // VN tag spoiler options
- if(x('tagops')) {
- l = x('tagops').getElementsByTagName('a');
- for(i=0;i<l.length;i++)
- l[i].onclick = function() {
- var l = x('tagops').getElementsByTagName('a');
- var sel = 0;
- for(var i=0;i<l.length;i++)
- if(l[i] == this) {
- if(i < 3) {
- tvsSet(i, null);
- setCookie('tagspoil', i);
- } else
- tvsSet(null, i==3?true:false);
- }
- return false;
- };
- tvsSet(readCookie('tagspoil'), true);
- }
-
- // Javascript tabs
- if(x('jt_select'))
- jtInit();
-
- // spoiler tags
- l = document.getElementsByTagName('b');
- for(i=0;i<l.length;i++)
- if(l[i].className == 'spoiler') {
- l[i].onmouseover = function() { this.className = 'spoiler_shown' };
- l[i].onmouseout = function() { this.className = 'spoiler' };
- }
-
- // expand/collapse edit summaries on */hist
- if(x('history_comments')) {
- setcomment = function() {
- var e = readCookie('histexpand') == 1;
- var l = x('history_comments');
- l.innerHTML = e ? 'collapse' : 'expand';
- while(l.nodeName.toLowerCase() != 'table')
- l = l.parentNode;
- l = l.getElementsByTagName('tr');
- for(var i=0;i<l.length;i++)
- //alert(l[i].className);
- if(l[i].className.indexOf('editsum') >= 0) {
- if(!e && l[i].className.indexOf('hidden') < 0)
- l[i].className += ' hidden';
- if(e && l[i].className.indexOf('hidden') >= 0)
- l[i].className = l[i].className.replace(/hidden/, '');
- }
- };
- setcomment();
- x('history_comments').onclick = function () {
- setCookie('histexpand', readCookie('histexpand') == 1 ? 0 : 1);
- setcomment();
- return false;
- };
- }
-
- // Are we really vndb?
- if(location.hostname != 'vndb.org') {
- var d = document.createElement('div');
- d.setAttribute('id', 'debug');
- d.innerHTML = '<h2>This is not VNDB!</h2>The real VNDB is <a href="http://vndb.org/">here</a>.';
- document.body.appendChild(d);
- }
-
- // date selector
- l = document.getElementsByTagName('input');
- for(i=0;i<l.length;i++)
- if(l[i].className == 'dateinput')
- dtLoad(l[i]);
-
- // forms.js
- if(x('relations'))
- relLoad();
- if(x('jt_box_vn_scr'))
- scrLoad();
- if(x('media'))
- medLoad();
- if(x('jt_box_rel_vn'))
- vnpLoad('vn');
- if(x('jt_box_rel_prod'))
- vnpLoad('producers');
- if(x('taglinks'))
- tglLoad();
-
- // make some fields readonly when patch flag is set
- if(x('jt_box_rel_geninfo')) {
- var func = function() {
- x('doujin').disabled = x('resolution').disabled = x('voiced').disabled = x('ani_story').disabled = x('ani_ero').disabled = x('patch').checked;
- };
- func();
- x('patch').onclick = func;
- }
-
- // spam protection on all forms
- if(document.forms.length >= 1)
- for(i=0; i<document.forms.length; i++)
- document.forms[i].action = document.forms[i].action.replace(/\/nospam\?/,'');
-
-});
diff --git a/util/dbgraph.pl b/util/dbgraph.pl
index 167680cd..0b72ceba 100755
--- a/util/dbgraph.pl
+++ b/util/dbgraph.pl
@@ -16,13 +16,13 @@ use warnings;
my %subgraphs = (
- 'Producers' => [qw| FFFFCC producers producers_rev |],
+ 'Producers' => [qw| FFFFCC producers producers_rev producers_relations |],
'Releases' => [qw| C8FFC8 releases releases_rev releases_media releases_platforms releases_producers releases_lang releases_vn |],
- 'Visual Novels' => [qw| FFE6BE vn vn_rev vn_relations vn_categories vn_anime vn_screenshots |],
- 'Users' => [qw| CCFFFF users votes rlists wlists |],
+ 'Visual Novels' => [qw| FFE6BE vn vn_rev vn_relations vn_anime vn_screenshots |],
+ 'Users' => [qw| CCFFFF users votes rlists wlists sessions |],
'Discussion board' => [qw| FFDCDC threads threads_boards threads_posts |],
'Tags' => [qw| FFC8C8 tags tags_aliases tags_parents tags_vn |],
- 'Misc' => [qw| F5F5F5 changes anime screenshots relgraph stats_cache quotes sessions |],
+ 'Misc' => [qw| F5F5F5 changes anime screenshots stats_cache quotes relgraphs |],
);
my %tables; # table_name => [ [ col1, pri ], ... ]
diff --git a/util/dump.sql b/util/dump.sql
index aaa1a5e9..0fdf5696 100644
--- a/util/dump.sql
+++ b/util/dump.sql
@@ -1,15 +1,16 @@
-
--- we don't use PgSQL's OIDS
-SET default_with_oids = false;
-
--- for the functions to work, the following query must
--- be executed on the database by a superuser:
--- CREATE PROCEDURAL LANGUAGE plpgsql
-
+-- plpgsql is required for our (trigger) functions
+CREATE LANGUAGE plpgsql;
+-- data types
+CREATE TYPE anime_type AS ENUM ('tv', 'ova', 'mov', 'oth', 'web', 'spe', 'mv');
+CREATE TYPE dbentry_type AS ENUM ('v', 'r', 'p');
+CREATE TYPE medium AS ENUM ('cd', 'dvd', 'gdr', 'blr', 'flp', 'mrt', 'mem', 'umd', 'nod', 'in', 'otc');
+CREATE TYPE producer_relation AS ENUM ('old', 'new', 'sub', 'par', 'imp', 'ipa', 'spa', 'ori');
+CREATE TYPE release_type AS ENUM ('complete', 'partial', 'trial');
+CREATE TYPE vn_relation AS ENUM ('seq', 'preq', 'set', 'alt', 'char', 'side', 'par', 'ser', 'fan', 'orig');
-----------------------------------------
@@ -23,7 +24,7 @@ CREATE TABLE anime (
year smallint,
ann_id integer,
nfo_id varchar(200),
- type smallint,
+ type anime_type,
title_romaji,
title_kanji,
lastfetch timestamptz
@@ -32,7 +33,7 @@ CREATE TABLE anime (
-- changes
CREATE TABLE changes (
id SERIAL NOT NULL PRIMARY KEY,
- type smallint NOT NULL DEFAULT 0,
+ type dbentry_type NOT NULL,
rev integer NOT NULL DEFAULT 1,
added timestamptz NOT NULL DEFAULT NOW(),
requester integer NOT NULL DEFAULT 0,
@@ -46,7 +47,16 @@ CREATE TABLE producers (
id SERIAL NOT NULL PRIMARY KEY,
latest integer NOT NULL DEFAULT 0,
locked boolean NOT NULL DEFAULT FALSE,
- hidden boolean NOT NULL DEFAULT FALSE
+ hidden boolean NOT NULL DEFAULT FALSE,
+ rgraph integer
+);
+
+-- producers_relations
+CREATE TABLE producers_relations (
+ pid1 integer NOT NULL,
+ pid2 integer NOT NULL,
+ relation producer_relation NOT NULL,
+ PRIMARY KEY(pid1, pid2)
);
-- producers_rev
@@ -62,7 +72,6 @@ CREATE TABLE producers_rev (
alias varchar(500) NOT NULL DEFAULT ''
);
-
-- quotes
CREATE TABLE quotes (
vid integer NOT NULL,
@@ -70,7 +79,6 @@ CREATE TABLE quotes (
PRIMARY KEY(vid, quote)
);
-
-- releases
CREATE TABLE releases (
id SERIAL NOT NULL PRIMARY KEY,
@@ -89,7 +97,7 @@ CREATE TABLE releases_lang (
-- releases_media
CREATE TABLE releases_media (
rid integer NOT NULL DEFAULT 0,
- medium character(3) NOT NULL DEFAULT '',
+ medium medium NOT NULL,
qty smallint NOT NULL DEFAULT 1,
PRIMARY KEY(rid, medium, qty)
);
@@ -105,6 +113,9 @@ CREATE TABLE releases_platforms (
CREATE TABLE releases_producers (
rid integer NOT NULL,
pid integer NOT NULL,
+ developer boolean NOT NULL DEFAULT FALSE,
+ publisher boolean NOT NULL DEFAULT TRUE,
+ CHECK(developer OR publisher),
PRIMARY KEY(pid, rid)
);
@@ -114,7 +125,7 @@ CREATE TABLE releases_rev (
rid integer NOT NULL DEFAULT 0,
title varchar(250) NOT NULL DEFAULT '',
original varchar(250) NOT NULL DEFAULT '',
- type smallint NOT NULL DEFAULT 0,
+ type release_type NOT NULL DEFAULT 'complete',
website varchar(250) NOT NULL DEFAULT '',
released integer NOT NULL,
notes text NOT NULL DEFAULT '',
@@ -137,10 +148,10 @@ CREATE TABLE releases_vn (
PRIMARY KEY(rid, vid)
);
--- relgraph
-CREATE TABLE relgraph (
- id SERIAL NOT NULL PRIMARY KEY,
- cmap text NOT NULL DEFAULT ''
+-- relgraphs
+CREATE TABLE relgraphs (
+ id SERIAL PRIMARY KEY,
+ svg xml NOT NULL
);
-- rlists
@@ -245,6 +256,7 @@ CREATE TABLE threads_boards (
tid integer NOT NULL DEFAULT 0,
type character(2) NOT NULL DEFAULT 0,
iid integer NOT NULL DEFAULT 0,
+ lastread smallint NOT NULL,
PRIMARY KEY(tid, type, iid)
);
@@ -292,7 +304,7 @@ CREATE TABLE vn_anime (
CREATE TABLE vn_relations (
vid1 integer NOT NULL DEFAULT 0,
vid2 integer NOT NULL DEFAULT 0,
- relation integer NOT NULL DEFAULT 0,
+ relation vn_relation NOT NULL,
PRIMARY KEY(vid1, vid2)
);
@@ -352,6 +364,9 @@ CREATE TABLE wlists (
ALTER TABLE changes ADD FOREIGN KEY (requester) REFERENCES users (id);
ALTER TABLE changes ADD FOREIGN KEY (causedby) REFERENCES changes (id);
ALTER TABLE producers ADD FOREIGN KEY (latest) REFERENCES producers_rev (id) DEFERRABLE INITIALLY DEFERRED;
+ALTER TABLE producers ADD FOREIGN KEY (rgraph) REFERENCES relgraphs (id);
+ALTER TABLE producers_relations ADD FOREIGN KEY (pid1) REFERENCES producers_rev (id);
+ALTER TABLE producers_relations ADD FOREIGN KEY (pid2) REFERENCES producers (id);
ALTER TABLE producers_rev ADD FOREIGN KEY (id) REFERENCES changes (id);
ALTER TABLE producers_rev ADD FOREIGN KEY (pid) REFERENCES producers (id);
ALTER TABLE quotes ADD FOREIGN KEY (vid) REFERENCES vn (id);
@@ -380,7 +395,7 @@ ALTER TABLE threads_posts ADD FOREIGN KEY (tid) REFERENCES threads
ALTER TABLE threads_posts ADD FOREIGN KEY (uid) REFERENCES users (id);
ALTER TABLE threads_boards ADD FOREIGN KEY (tid) REFERENCES threads (id);
ALTER TABLE vn ADD FOREIGN KEY (latest) REFERENCES vn_rev (id) DEFERRABLE INITIALLY DEFERRED;
-ALTER TABLE vn ADD FOREIGN KEY (rgraph) REFERENCES relgraph (id);
+ALTER TABLE vn ADD FOREIGN KEY (rgraph) REFERENCES relgraphs (id);
ALTER TABLE vn_anime ADD FOREIGN KEY (aid) REFERENCES anime (id);
ALTER TABLE vn_anime ADD FOREIGN KEY (vid) REFERENCES vn_rev (id);
ALTER TABLE vn_relations ADD FOREIGN KEY (vid1) REFERENCES vn_rev (id);
@@ -405,31 +420,6 @@ ALTER TABLE wlists ADD FOREIGN KEY (vid) REFERENCES vn
-------------------------
--- update_rev(table, ids) - updates the rev column in the changes table
-CREATE FUNCTION update_rev(tbl text, ids text) RETURNS void AS $$
-DECLARE
- r RECORD;
- r2 RECORD;
- i integer;
- t text;
- e text;
-BEGIN
- SELECT INTO t SUBSTRING(tbl, 1, 1);
- e := '';
- IF ids <> '' THEN
- e := ' WHERE id IN('||ids||')';
- END IF;
- FOR r IN EXECUTE 'SELECT id FROM '||tbl||e LOOP
- i := 1;
- FOR r2 IN EXECUTE 'SELECT id FROM '||tbl||'_rev WHERE '||t||'id = '||r.id||' ORDER BY id ASC' LOOP
- UPDATE changes SET rev = i WHERE id = r2.id;
- i := i+1;
- END LOOP;
- END LOOP;
-END;
-$$ LANGUAGE plpgsql;
-
-
-- update_vncache(id) - updates the c_* columns in the vn table
CREATE FUNCTION update_vncache(id integer) RETURNS void AS $$
DECLARE
@@ -445,7 +435,7 @@ BEGIN
JOIN releases r1 ON rr1.id = r1.latest
JOIN releases_vn rv1 ON rr1.id = rv1.rid
WHERE rv1.vid = vn.id
- AND rr1.type <> 2
+ AND rr1.type <> ''trial''
AND r1.hidden = FALSE
AND rr1.released <> 0
GROUP BY rv1.vid
@@ -457,7 +447,7 @@ BEGIN
JOIN releases r2 ON rr2.id = r2.latest
JOIN releases_vn rv2 ON rr2.id = rv2.rid
WHERE rv2.vid = vn.id
- AND rr2.type <> 2
+ AND rr2.type <> ''trial''
AND rr2.released <= TO_CHAR(''today''::timestamp, ''YYYYMMDD'')::integer
AND r2.hidden = FALSE
GROUP BY rl2.lang
@@ -470,7 +460,7 @@ BEGIN
JOIN releases r3 ON rp3.rid = r3.latest
JOIN releases_vn rv3 ON rp3.rid = rv3.rid
WHERE rv3.vid = vn.id
- AND rr3.type <> 2
+ AND rr3.type <> ''trial''
AND rr3.released <= TO_CHAR(''today''::timestamp, ''YYYYMMDD'')::integer
AND r3.hidden = FALSE
GROUP BY rp3.platform
@@ -585,7 +575,7 @@ BEGIN
SELECT * FROM tags_vn UNION SELECT * FROM tag_vn_childs();
-- grouped by (tag, vid, uid), so only one user votes on one parent tag per VN entry
CREATE OR REPLACE TEMPORARY VIEW tags_vn_grouped AS
- SELECT tag, vid, uid, MAX(vote)::real AS vote, COALESCE(AVG(spoiler), 0)::real AS spoiler
+ SELECT tag, vid, uid, MAX(vote)::real AS vote, AVG(spoiler)::real AS spoiler
FROM tags_vn_all GROUP BY tag, vid, uid;
-- grouped by (tag, vid) and serialized into a table
DROP INDEX IF EXISTS tags_vn_bayesian_tag;
@@ -781,6 +771,36 @@ $$ LANGUAGE plpgsql;
CREATE CONSTRAINT TRIGGER vn_relgraph_notify AFTER INSERT OR UPDATE ON vn DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE PROCEDURE vn_relgraph_notify();
+-- Same as above for producers, with slight differences in the steps:
+-- There is no 2, and
+-- 3 = New producer revision of which the name, language or type differs from the previous revision (deferred)
+CREATE OR REPLACE FUNCTION producer_relgraph_notify() RETURNS trigger AS $$
+BEGIN
+ -- 1.
+ IF TG_TABLE_NAME = 'producers' THEN
+ IF NEW.rgraph IS NULL AND EXISTS(SELECT 1 FROM producers_relations WHERE pid1 = NEW.latest) THEN
+ NOTIFY relgraph;
+ END IF;
+ END IF;
+ IF TG_TABLE_NAME = 'producers' AND TG_OP = 'UPDATE' THEN
+ IF NEW.rgraph IS NOT NULL AND OLD.latest > 0 THEN
+ -- 3 & 4
+ IF OLD.latest <> NEW.latest AND (
+ EXISTS(SELECT 1 FROM producers_rev p1, producers_rev p2 WHERE (p2.name <> p1.name OR p2.type <> p1.type OR p2.lang <> p1.lang) AND p1.id = OLD.latest AND p2.id = NEW.latest)
+ OR EXISTS(SELECT p1.pid2, p1.relation FROM producers_relations p1 WHERE p1.pid1 = OLD.latest EXCEPT SELECT p2.pid2, p2.relation FROM producers_relations p2 WHERE p2.pid1 = NEW.latest)
+ OR EXISTS(SELECT p1.pid2, p1.relation FROM producers_relations p1 WHERE p1.pid1 = NEW.latest EXCEPT SELECT p2.pid2, p2.relation FROM producers_relations p2 WHERE p2.pid1 = OLD.latest)
+ ) THEN
+ UPDATE producers SET rgraph = NULL WHERE id = NEW.id;
+ END IF;
+ END IF;
+ END IF;
+ RETURN NULL;
+END;
+$$ LANGUAGE plpgsql;
+
+CREATE CONSTRAINT TRIGGER vn_relgraph_notify AFTER INSERT OR UPDATE ON producers DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE PROCEDURE producer_relgraph_notify();
+
+
-- NOTIFY on insert into changes/posts/tags
CREATE OR REPLACE FUNCTION insert_notify() RETURNS trigger AS $$
BEGIN
diff --git a/util/init.pl b/util/init.pl
index 5c171705..ac533174 100755
--- a/util/init.pl
+++ b/util/init.pl
@@ -22,7 +22,7 @@ print "\n";
print "Creating directory structures...\n";
-for my $d (qw| cv rg st sf |) {
+for my $d (qw| cv st sf |) {
print " /static/$d\n";
mkdir "$ROOT/static/$d" or die "mkdir '$ROOT/static/$d': $!\n";
for my $i (0..99) {
diff --git a/util/jsgen.pl b/util/jsgen.pl
new file mode 100755
index 00000000..1ad8fb53
--- /dev/null
+++ b/util/jsgen.pl
@@ -0,0 +1,97 @@
+#!/usr/bin/perl
+
+package VNDB;
+
+use strict;
+use warnings;
+use Encode 'encode_utf8';
+use Cwd 'abs_path';
+eval { require JavaScript::Minifier::XS; };
+
+our($ROOT, %S);
+BEGIN { ($ROOT = abs_path $0) =~ s{/util/jsgen\.pl$}{}; }
+require $ROOT.'/data/global.pl';
+
+use lib "$ROOT/lib";
+use lib "$ROOT/yawf/lib";
+use LangFile;
+
+# The VNDB::L10N module is not really suited to be used outside the VNDB::*
+# framework, but it's the central location that defines which languages we have
+# and in what order to display them.
+use VNDB::L10N;
+
+
+my $jskeys_lang = join '|', VNDB::L10N::languages();
+my $jskeys = qr{^(?:
+ _lang_(?:$jskeys_lang)|
+ _js_.+|
+ _menu_emptysearch|
+ _vnpage_uopt_(?:10?vote|rel.+)|
+ _rlst_[vr]stat_.+|
+ _vnedit_rel_(?:isa|of|addbut|del|none|findformat|novn|double)|
+ _redit_form_med_.+|
+ _vnedit_scr_.+|
+ _tagv_(?:add|spoil\d|notfound|nometa|double)|
+ _redit_form_vn_(?:addbut|remove|none|vnformat|notfound|double)|
+ _redit_form_prod_(?:addbut|remove|none|pformat|notfound|double)|
+ _pedit_rel_(?:addbut|del|none|findformat|notfound|double)
+ )$}x;
+
+sub l10n {
+ # Using JSON::XS or something may be shorter and less error prone,
+ # although I would have less power over the output (mostly the quoting of the keys)
+
+ my $lang = LangFile->new(read => "$ROOT/data/lang.txt");
+ my @r;
+ push @r, 'L10N_STR = {';
+ my $cur; # undef = none/excluded, 1 = awaiting first TL line, 2 = after first TL line
+ my %lang;
+ while((my $l = $lang->read())) {
+ my $type = shift @$l;
+ if($type eq 'key') {
+ my $key = shift @$l;
+ push @r, ' }' if $cur;
+ $cur = $key =~ $jskeys ? 1 : undef;
+ if($cur) {
+ $r[$#r] .= ',' if $r[$#r] =~ /}$/;
+ # let's assume key names don't trigger a reserved word in JS
+ $key = qq{"$key"} if $key !~ /^[a-z_][a-z0-9_]*$/i;
+ push @r, qq| $key: {|;
+ }
+ }
+ $lang{$l->[0]} = 1 if $type eq 'tl';
+ if($type eq 'tl' && $cur) {
+ my($lang, $sync, $val) = @$l;
+ next if !$val;
+ $val =~ s/"/\\"/g;
+ $val =~ s/\n/\\n/g;
+ $r[$#r] .= ',' if $cur == 2;
+ $lang = qq{"$l->[0]"} if $lang =~ /^(?:as|do|if|in|is)$/; # reserved two-char words
+ push @r, qq| $lang: "$val"|;
+ $cur = 2;
+ }
+ }
+ push @r, ' }' if $cur;
+ push @r, '};';
+ push @r, 'L10N_LANG = [ '.join(', ', map qq{"$_"}, VNDB::L10N::languages()).' ];';
+ return join "\n", @r;
+}
+
+
+sub jsgen {
+ # JavaScript::Minifier::XS doesn't correctly handle perl's unicode,
+ # so just do everything in raw bytes instead.
+ my $js = encode_utf8(l10n()) . "\n";
+ $js .= sprintf "rlst_rstat = [ %s ];\n", join ', ', map qq{"$_"}, @{$S{rlst_rstat}};
+ $js .= sprintf "rlst_vstat = [ %s ];\n", join ', ', map qq{"$_"}, @{$S{rlst_vstat}};
+ open my $JS, '<', "$ROOT/data/script.js" or die $!;
+ $js .= join '', <$JS>;
+ close $JS;
+ open my $NEWJS, '>', "$ROOT/static/f/script.js" or die $!;
+ print $NEWJS $JavaScript::Minifier::XS::VERSION ? JavaScript::Minifier::XS::minify($js) : $js;
+ close $NEWJS;
+}
+
+jsgen;
+
diff --git a/util/lang.pl b/util/lang.pl
new file mode 100755
index 00000000..087dea2e
--- /dev/null
+++ b/util/lang.pl
@@ -0,0 +1,167 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Cwd 'abs_path';
+our $ROOT;
+BEGIN { ($ROOT = abs_path $0) =~ s{/util/lang\.pl$}{}; }
+
+use lib $ROOT.'/lib';
+use LangFile;
+
+my $langtxt = "$ROOT/data/lang.txt";
+
+
+sub usage {
+ print <<__;
+$0 stats
+ Prints some stats.
+
+$0 add <lang> [<file>]
+ Adds new (empty) translation lines for language <lang> to <file> (defaults to
+ the global lang.txt) for keys that don't have a TL line yet.
+
+$0 only <lang>[,..] <outfile>
+ Makes a copy of lang.txt to <outfile> and removes all translations except the
+ ones of langauge <lang> (comma-seperated list of tags)
+
+$0 merge <lang> <file>
+ Merges <file> into lang.txt, copying over all the translations of <lang> in
+ <file> while ignoring any other changes. Keys in <file> not present in
+ lang.txt are silently ignored. Keys in lang.txt but not in <file> remain
+ unaffected. Make sure each key in lang.txt already has a line for <lang>,
+ otherwise do an 'add' first.
+
+$0 reorder <lang1>,<lang2>,..
+ Re-orders the translation lines in lang.txt using the specified order.
+
+$0 stage <lang>
+ Puts all changes of <lang> into the git index, and leaves everything else untouched.
+__
+ exit;
+}
+
+
+sub stats {
+ my $r = LangFile->new(read => $langtxt);
+ my $keys = 0;
+ my %lang;
+ while(my $l = $r->read()) {
+ $keys++ if $l->[0] eq 'key';
+ if($l->[0] eq 'tl') {
+ $lang{$l->[1]} ||= [0,0];
+ $lang{$l->[1]}[0]++;
+ $lang{$l->[1]}[1]++ if $l->[2];
+ }
+ }
+ print "lang lines sync unsync\n";
+ printf "%3s %4d (%3d%%) %4d (%3d%%) %4d\n", $_,
+ $lang{$_}[0], $lang{$_}[0]/$keys*100, $lang{$_}[1], $lang{$_}[1]/$keys*100, $keys-$lang{$_}[1]
+ for keys %lang;
+ printf "Total keys: %d\n", $keys;
+}
+
+
+sub add {
+ my($lang, $file) = @_;
+ $file ||= $langtxt;
+ my $r = LangFile->new(read => $file);
+ my $w = LangFile->new(write => "$file~");
+ my $k = 0;
+ while((my $l = $r->read())) {
+ if($k && $l->[0] ne 'tl') {
+ $k = 0;
+ $w->write('tl', $lang, 0, '');
+ }
+ $k = 1 if $l->[0] eq 'key';
+ $k = 0 if $l->[0] eq 'tl' && $l->[1] eq $lang;
+ $w->write(@$l);
+ }
+ $r->close;
+ $w->close;
+ rename "$file~", $file or die $!;
+}
+
+
+sub only {
+ my($lang, $out) = @_;
+ my @lang = split /,/, $lang;
+ my $r = LangFile->new(read => $langtxt);
+ my $w = LangFile->new(write => $out);
+ while((my $l = $r->read())) {
+ $w->write(@$l) unless $l->[0] eq 'tl' && !grep $_ eq $l->[1], @lang;
+ }
+ $r->close;
+ $w->close;
+}
+
+
+sub merge {
+ my($lang, $file) = @_;
+
+ # read all translations in $lang in $file
+ my $trans = LangFile->new(read => $file);
+ my($key, %trans);
+ while((my $l = $trans->read)) {
+ $key = $l->[1] if $l->[0] eq 'key';
+ $trans{$key} = [ $l->[2], $l->[3] ] if $l->[0] eq 'tl' && $l->[1] eq $lang;
+ }
+ $trans->close;
+
+ # now update lang.txt
+ my $r = LangFile->new(read => $langtxt);
+ my $w = LangFile->new(write => "$langtxt~");
+ while((my $l = $r->read)) {
+ $key = $l->[1] if $l->[0] eq 'key';
+ ($l->[2], $l->[3]) = @{$trans{$key}} if $l->[0] eq 'tl' && $l->[1] eq $lang && $trans{$key};
+ $w->write(@$l);
+ }
+ $r->close;
+ $w->close;
+ rename "$langtxt~", $langtxt or die $!;
+}
+
+
+sub reorder {
+ my @lang = split /,/, shift;
+ my $r = LangFile->new(read => $langtxt);
+ my $w = LangFile->new(write => "$langtxt~");
+ my($key, %tl);
+ while((my $l = $r->read)) {
+ if($key && $l->[0] ne 'tl') {
+ $tl{$_} && $w->write(@{delete $tl{$_}}) for(@lang);
+ $w->write(@{$tl{$_}}) for sort keys %tl;
+ $key = undef;
+ %tl = ();
+ }
+ $key = $l->[1] if $l->[0] eq 'key';
+ $tl{$l->[1]} = $l if $l->[0] eq 'tl';
+ $w->write(@$l) unless $l->[0] eq 'tl';
+ }
+ $r->close;
+ $w->close;
+ rename "$langtxt~", $langtxt or die $!;
+}
+
+
+sub stage {
+ my $lang = shift;
+ chdir "$ROOT/data";
+ rename 'lang.txt', '.lang.txt.tmp' or die $!;
+ `git checkout lang.txt`;
+ merge $lang, '.lang.txt.tmp';
+ `git add lang.txt`;
+ rename '.lang.txt.tmp', 'lang.txt';
+}
+
+
+usage if !@ARGV;
+my $act = shift;
+stats if $act eq 'stats';
+add @ARGV if $act eq 'add';
+only @ARGV if $act eq 'only';
+merge @ARGV if $act eq 'merge';
+reorder @ARGV if $act eq 'reorder';
+stage @ARGV if $act eq 'stage';
+
diff --git a/util/skingen.pl b/util/skingen.pl
index e27ea4a1..73e4fdc7 100755
--- a/util/skingen.pl
+++ b/util/skingen.pl
@@ -5,13 +5,12 @@ package VNDB;
use strict;
use warnings;
use Cwd 'abs_path';
-use Data::Dumper 'Dumper';
use Image::Magick;
+eval { require CSS::Minifier::XS };
our($ROOT, %O);
BEGIN { ($ROOT = abs_path $0) =~ s{/util/skingen\.pl$}{}; }
-require $ROOT.'/data/global.pl';
if(@ARGV) {
@@ -78,19 +77,12 @@ sub writeskin { # $obj
# write the CSS
open my $CSS, '<', "$ROOT/data/style.css" or die $!;
+ my $css = join '', <$CSS>;
+ close $CSS;
+ $css =~ s/\$$_\$/$o->{$_}/g for (keys %$o);
open my $SKIN, '>', "$ROOT/static/s/$o->{_name}/style.css" or die $!;
- while((my $d = <$CSS>)) {
- if($O{debug}) {
- chomp $d;
- $d =~ s/^\s*/ /;
- $d =~ s{/\*.+\*/}{}; # NOTE: multiline comments or multiple comments per line won't work
- next if $d !~ /[^\s\t]/;
- }
- $d =~ s/\$$_\$/$o->{$_}/g for (keys %$o);
- print $SKIN $d;
- }
+ print $SKIN $CSS::Minifier::XS::VERSION ? CSS::Minifier::XS::minify($css) : $css;
close $SKIN;
- close $CSS;
}
diff --git a/util/updates/update_2.8.sql b/util/updates/update_2.8.sql
new file mode 100644
index 00000000..d8373210
--- /dev/null
+++ b/util/updates/update_2.8.sql
@@ -0,0 +1,213 @@
+
+-- !BEFORE! running this SQL file, make sure to kill Multi,
+-- After running this SQL file, also make sure to do a:
+-- $ rm -r static/rg/
+-- And start multi again
+
+-- VN Relation graphs are stored in the database as SVG - no cmaps and .png anymore
+UPDATE vn SET rgraph = NULL;
+ALTER TABLE vn DROP CONSTRAINT vn_rgraph_fkey;
+DROP TABLE relgraph;
+CREATE TABLE relgraphs (
+ id SERIAL PRIMARY KEY,
+ svg xml NOT NULL
+);
+ALTER TABLE vn ADD FOREIGN KEY (rgraph) REFERENCES relgraphs (id);
+
+
+-- VN relations stored as enum
+CREATE TYPE vn_relation AS ENUM ('seq', 'preq', 'set', 'alt', 'char', 'side', 'par', 'ser', 'fan', 'orig');
+ALTER TABLE vn_relations ALTER COLUMN relation DROP DEFAULT;
+ALTER TABLE vn_relations ALTER COLUMN relation TYPE vn_relation USING
+ CASE
+ WHEN relation = 0 THEN 'seq'::vn_relation
+ WHEN relation = 1 THEN 'preq'
+ WHEN relation = 2 THEN 'set'
+ WHEN relation = 3 THEN 'alt'
+ WHEN relation = 4 THEN 'char'
+ WHEN relation = 5 THEN 'side'
+ WHEN relation = 6 THEN 'par'
+ WHEN relation = 7 THEN 'ser'
+ WHEN relation = 8 THEN 'fan'
+ ELSE 'orig'
+ END;
+
+
+-- producer relations
+CREATE TYPE producer_relation AS ENUM ('old', 'new', 'sub', 'par', 'imp', 'ipa', 'spa', 'ori');
+CREATE TABLE producers_relations (
+ pid1 integer NOT NULL REFERENCES producers_rev (id),
+ pid2 integer NOT NULL REFERENCES producers (id),
+ relation producer_relation NOT NULL,
+ PRIMARY KEY(pid1, pid2)
+);
+ALTER TABLE producers ADD COLUMN rgraph integer REFERENCES relgraphs (id);
+
+CREATE OR REPLACE FUNCTION producer_relgraph_notify() RETURNS trigger AS $$
+BEGIN
+ -- 1.
+ IF TG_TABLE_NAME = 'producers' THEN
+ IF NEW.rgraph IS NULL AND EXISTS(SELECT 1 FROM producers_relations WHERE pid1 = NEW.latest) THEN
+ NOTIFY relgraph;
+ END IF;
+ END IF;
+ IF TG_TABLE_NAME = 'producers' AND TG_OP = 'UPDATE' THEN
+ IF NEW.rgraph IS NOT NULL AND OLD.latest > 0 THEN
+ -- 3 & 4
+ IF OLD.latest <> NEW.latest AND (
+ EXISTS(SELECT 1 FROM producers_rev p1, producers_rev p2 WHERE (p2.name <> p1.name OR p2.type <> p1.type OR p2.lang <> p1.lang) AND p1.id = OLD.latest AND p2.id = NEW.latest)
+ OR EXISTS(SELECT p1.pid2, p1.relation FROM producers_relations p1 WHERE p1.pid1 = OLD.latest EXCEPT SELECT p2.pid2, p2.relation FROM producers_relations p2 WHERE p2.pid1 = NEW.latest)
+ OR EXISTS(SELECT p1.pid2, p1.relation FROM producers_relations p1 WHERE p1.pid1 = NEW.latest EXCEPT SELECT p2.pid2, p2.relation FROM producers_relations p2 WHERE p2.pid1 = OLD.latest)
+ ) THEN
+ UPDATE producers SET rgraph = NULL WHERE id = NEW.id;
+ END IF;
+ END IF;
+ END IF;
+ RETURN NULL;
+END;
+$$ LANGUAGE plpgsql;
+CREATE CONSTRAINT TRIGGER vn_relgraph_notify AFTER INSERT OR UPDATE ON producers DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE PROCEDURE producer_relgraph_notify();
+
+
+-- Anime types stored as enum
+CREATE TYPE anime_type AS ENUM ('tv', 'ova', 'mov', 'oth', 'web', 'spe', 'mv');
+ALTER TABLE anime ALTER COLUMN type TYPE anime_type USING
+ CASE
+ WHEN type = 0 THEN 'tv'::anime_type
+ WHEN type = 1 THEN 'ova'
+ WHEN type = 2 THEN 'mov'
+ WHEN type = 3 THEN 'oth'
+ WHEN type = 4 THEN 'web'
+ WHEN type = 5 THEN 'spe'
+ WHEN type = 6 THEN 'mv'
+ ELSE NULL
+ END;
+
+
+-- Release media stored as enum
+CREATE TYPE medium AS ENUM ('cd', 'dvd', 'gdr', 'blr', 'flp', 'mrt', 'mem', 'umd', 'nod', 'in', 'otc');
+ALTER TABLE releases_media ALTER COLUMN medium DROP DEFAULT;
+ALTER TABLE releases_media ALTER COLUMN medium TYPE medium USING TRIM(both ' ' from medium)::medium;
+
+
+-- Differentiate between publishers and developers
+ALTER TABLE releases_producers ADD COLUMN developer boolean NOT NULL DEFAULT FALSE;
+ALTER TABLE releases_producers ADD COLUMN publisher boolean NOT NULL DEFAULT TRUE;
+ALTER TABLE releases_producers ADD CHECK(developer OR publisher);
+
+
+-- Keep track of last read post for PMs
+ALTER TABLE threads_boards ADD COLUMN lastread smallint;
+
+
+-- changes.type stored as enum
+CREATE TYPE dbentry_type AS ENUM ('v', 'r', 'p');
+ALTER TABLE changes ALTER COLUMN type DROP DEFAULT;
+ALTER TABLE changes ALTER COLUMN type TYPE dbentry_type USING
+ CASE
+ WHEN type = 0 THEN 'v'::dbentry_type
+ WHEN type = 1 THEN 'r'
+ WHEN type = 2 THEN 'p'
+ ELSE NULL -- not allowed to happen, otherwise FIX YOUR DATABASE!
+ END;
+
+
+-- releases_rev.type stored as enum
+CREATE TYPE release_type AS ENUM ('complete', 'partial', 'trial');
+ALTER TABLE releases_rev ALTER COLUMN type DROP DEFAULT;
+ALTER TABLE releases_rev ALTER COLUMN type TYPE release_type USING
+ CASE
+ WHEN type = 0 THEN 'complete'::release_type
+ WHEN type = 1 THEN 'partial'
+ WHEN type = 2 THEN 'trial'
+ ELSE NULL
+ END;
+ALTER TABLE releases_rev ALTER COLUMN type SET DEFAULT 'complete';
+
+CREATE OR REPLACE FUNCTION update_vncache(id integer) RETURNS void AS $$
+DECLARE
+ w text := '';
+BEGIN
+ IF id > 0 THEN
+ w := ' WHERE id = '||id;
+ END IF;
+ EXECUTE 'UPDATE vn SET
+ c_released = COALESCE((SELECT
+ MIN(rr1.released)
+ FROM releases_rev rr1
+ JOIN releases r1 ON rr1.id = r1.latest
+ JOIN releases_vn rv1 ON rr1.id = rv1.rid
+ WHERE rv1.vid = vn.id
+ AND rr1.type <> ''trial''
+ AND r1.hidden = FALSE
+ AND rr1.released <> 0
+ GROUP BY rv1.vid
+ ), 0),
+ c_languages = COALESCE(ARRAY_TO_STRING(ARRAY(
+ SELECT rl2.lang
+ FROM releases_rev rr2
+ JOIN releases_lang rl2 ON rl2.rid = rr2.id
+ JOIN releases r2 ON rr2.id = r2.latest
+ JOIN releases_vn rv2 ON rr2.id = rv2.rid
+ WHERE rv2.vid = vn.id
+ AND rr2.type <> ''trial''
+ AND rr2.released <= TO_CHAR(''today''::timestamp, ''YYYYMMDD'')::integer
+ AND r2.hidden = FALSE
+ GROUP BY rl2.lang
+ ORDER BY rl2.lang
+ ), ''/''), ''''),
+ c_platforms = COALESCE(ARRAY_TO_STRING(ARRAY(
+ SELECT rp3.platform
+ FROM releases_platforms rp3
+ JOIN releases_rev rr3 ON rp3.rid = rr3.id
+ JOIN releases r3 ON rp3.rid = r3.latest
+ JOIN releases_vn rv3 ON rp3.rid = rv3.rid
+ WHERE rv3.vid = vn.id
+ AND rr3.type <> ''trial''
+ AND rr3.released <= TO_CHAR(''today''::timestamp, ''YYYYMMDD'')::integer
+ AND r3.hidden = FALSE
+ GROUP BY rp3.platform
+ ORDER BY rp3.platform
+ ), ''/''), '''')
+ '||w;
+END;
+$$ LANGUAGE plpgsql;
+
+
+
+-- fix calculation of the tags_vn_bayesian.spoiler column
+
+CREATE OR REPLACE FUNCTION tag_vn_calc() RETURNS void AS $$
+BEGIN
+ -- all votes for all tags
+ CREATE OR REPLACE TEMPORARY VIEW tags_vn_all AS
+ SELECT * FROM tags_vn UNION SELECT * FROM tag_vn_childs();
+ -- grouped by (tag, vid, uid), so only one user votes on one parent tag per VN entry
+ CREATE OR REPLACE TEMPORARY VIEW tags_vn_grouped AS
+ SELECT tag, vid, uid, MAX(vote)::real AS vote, AVG(spoiler)::real AS spoiler
+ FROM tags_vn_all GROUP BY tag, vid, uid;
+ -- grouped by (tag, vid) and serialized into a table
+ DROP INDEX IF EXISTS tags_vn_bayesian_tag;
+ TRUNCATE tags_vn_bayesian;
+ INSERT INTO tags_vn_bayesian
+ SELECT tag, vid, COUNT(uid) AS users, AVG(vote)::real AS rating,
+ (CASE WHEN AVG(spoiler) < 0.7 THEN 0 WHEN AVG(spoiler) > 1.3 THEN 2 ELSE 1 END)::smallint AS spoiler
+ FROM tags_vn_grouped
+ GROUP BY tag, vid
+ HAVING AVG(vote) > 0;
+ CREATE INDEX tags_vn_bayesian_tag ON tags_vn_bayesian (tag);
+ -- now perform the bayesian ranking calculation
+ UPDATE tags_vn_bayesian tvs SET rating =
+ ((SELECT AVG(users)::real * AVG(rating)::real FROM tags_vn_bayesian WHERE tag = tvs.tag) + users*rating)
+ / ((SELECT AVG(users)::real FROM tags_vn_bayesian WHERE tag = tvs.tag) + users)::real;
+ -- and update the VN count in the tags table as well
+ UPDATE tags SET c_vns = (SELECT COUNT(*) FROM tags_vn_bayesian WHERE tag = id);
+ RETURN;
+END;
+$$ LANGUAGE plpgsql;
+SELECT tag_vn_calc();
+
+
+-- remove update_rev()
+DROP FUNCTION update_rev(text, text);
+
diff --git a/util/vndb.pl b/util/vndb.pl
index ed74a92f..4752c274 100755
--- a/util/vndb.pl
+++ b/util/vndb.pl
@@ -28,6 +28,10 @@ our(%O, %S);
$S{skins} = readskins();
+# automatically regenerate script.js when required and possible
+checkjs();
+
+
# load lang.dat
VNDB::L10N::loadfile();
@@ -113,3 +117,18 @@ sub readskins {
return \%skins;
}
+
+sub checkjs {
+ my $script = "$ROOT/static/f/script.js";
+ my $lastmod = [stat $script]->[9];
+ system "$ROOT/util/jsgen.pl" if
+ (!-e $script && -x "$ROOT/static/f")
+ || (-e $script && -w $script && (
+ $lastmod < [stat "$ROOT/data/script.js"]->[9]
+ || $lastmod < [stat "$ROOT/data/lang.txt"]->[9]
+ || (-e "$ROOT/data/config.pl" && $lastmod < [stat "$ROOT/data/config.pl"]->[9])
+ || $lastmod < [stat "$ROOT/data/global.pl"]->[9]
+ || $lastmod < [stat "$ROOT/util/jsgen.pl"]->[9]
+ ));
+}
+