summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYorhel <git@yorhel.nl>2010-12-21 16:14:11 +0100
committerYorhel <git@yorhel.nl>2010-12-21 16:14:11 +0100
commite15869b9c0c124d67f5d8ac85ffefad383642672 (patch)
tree53a3fc52e27480792b2f0ba5ab9b9ffdbbbdac6d
parente77484d81461c322656615759ba77ea25713a8d6 (diff)
Pass VN tag filters by ID rather than name
This makes the UI slightly uglier and less intuitive. I'll see if I can find a way around that. This update is required for the permanent browsing filters to be fast and reliable.
-rw-r--r--ChangeLog1
-rw-r--r--data/lang.txt21
-rw-r--r--data/script.js114
-rw-r--r--lib/VNDB/DB/Tags.pm2
-rw-r--r--lib/VNDB/DB/VN.pm24
-rw-r--r--lib/VNDB/Handler/Tags.pm16
-rw-r--r--lib/VNDB/Handler/VNBrowse.pm40
7 files changed, 115 insertions, 103 deletions
diff --git a/ChangeLog b/ChangeLog
index d266865c..c80fa245 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -12,6 +12,7 @@
- Added advanced page-browsing tabs to threads
- Added notes field to the user VN list
- Added vnlists.status filter to /u+/list
+ - Pass VN tag filters by ID rather than name
2.15 - 2010-12-15
- Removed expand/collapse from history browser and /u+/posts and switched to
diff --git a/data/lang.txt b/data/lang.txt
index 505da71d..7310e881 100644
--- a/data/lang.txt
+++ b/data/lang.txt
@@ -5875,27 +5875,6 @@ cs : Hodnocení
hu : Értékelés
nl : Waardering
-:_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:
-nl : De volgende tags zijn genegeerd:
-
-:_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ő
-nl : kan niet filteren op metatags
-
-:_vnbrowse_tagign_notfound
-en : no such tag found
-ru : тег не найден
-cs : nenalezeny takové tagy
-hu : nem található ilyen címke
-nl : tag niet gevonden
-
:_vnbrowse_fil_title
en : Visual Novel Filters
ru : Фильтры новелл
diff --git a/data/script.js b/data/script.js
index e5565a11..b208af56 100644
--- a/data/script.js
+++ b/data/script.js
@@ -41,22 +41,24 @@ 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)
+function ajax(url, func, async) {
+ if(!async && http_request)
http_request.abort();
- http_request = (window.ActiveXObject) ? new ActiveXObject('Microsoft.XMLHTTP') : new XMLHttpRequest();
- if(http_request == null)
+ var req = (window.ActiveXObject) ? new ActiveXObject('Microsoft.XMLHTTP') : new XMLHttpRequest();
+ if(req == 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)
+ if(!async)
+ http_request = req;
+ req.onreadystatechange = function() {
+ if(!req || req.readyState != 4 || !req.responseText)
return;
- if(http_request.status != 200)
+ if(req.status != 200)
return alert('Whoops, error! :(');
- func(http_request);
+ func(req);
};
url += (url.indexOf('?')>=0 ? ';' : '?')+(Math.floor(Math.random()*999)+1);
- http_request.open('GET', url, true);
- http_request.send(null);
+ req.open('GET', url, true);
+ req.send(null);
}
function setCookie(n,v) {
@@ -1748,7 +1750,7 @@ function filLoad() {
var c = tag('div', null);
for(var i=1; i<l.length; i++) {
// category link
- var a = tag('a', { href: '#', onclick: filSelectCat, fil_num: i }, l[i][0]);
+ var a = tag('a', { href: '#', onclick: filSelectCat, fil_num: i, fil_onshow:[] }, l[i][0]);
p.appendChild(a);
p.appendChild(tag(' '));
@@ -1768,6 +1770,8 @@ function filLoad() {
tag('td', {'class':'cont' }, fd[2]));
if(fd[0])
fil_cats[0][fd[0]] = f;
+ if(fd[5])
+ a.fil_onshow.push([ fd[5], f.fil_contents ]);
t.appendChild(f);
}
c.appendChild(t);
@@ -1802,6 +1806,8 @@ function filSelectCat(n) {
setClass(fil_cats[i], 'optselected', i == n);
setClass(fil_cats[i].fil_t, 'hidden', i != n);
}
+ for(var i=0; i<fil_cats[n].fil_onshow.length; i++)
+ fil_cats[n].fil_onshow[i][0](fil_cats[n].fil_onshow[i][1]);
return false
}
@@ -1844,14 +1850,14 @@ function filSerialize() {
var v = fil_cats[0][f].fil_readfunc(fil_cats[0][f].fil_contents);
var r = [];
for(var h=0; h<v.length; h++) {
- v[h] = (''+v[h]).split('');
+ var vs = (''+v[h]).split('');
r[h] = '';
// this isn't a very fast escaping method, blame JavaScript for inflexible search/replace support
- for(var i=0; i<v[h].length; i++) {
+ for(var i=0; i<vs.length; i++) {
for(var j=0; j<fil_escape.length; j++)
- if(v[h][i] == fil_escape[j])
+ if(vs[i] == fil_escape[j])
break;
- r[h] += j == fil_escape.length ? v[h][i] : '_'+(j<10?'0'+j:j);
+ r[h] += j == fil_escape.length ? vs[i] : '_'+(j<10?'0'+j:j);
}
}
if(r.length > 0 && r[0] != '')
@@ -1963,6 +1969,58 @@ function filFOptions(c, n, opts, setfunc) {
];
}
+function filFTagInput(name, label) {
+ var visible = false;
+ var input = tag('input', {type:'text', 'class':'text', style:'width:410px', onfocus:filSelectField});
+ 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(/ *, */);
+ var id = item.getAttribute('id');
+ tags[tags.length-1] = 'g'+id+':'+item.firstChild.nodeValue;
+ filSelectField(obj);
+ return tags.join(', ');
+ };
+ var retfunc = function(o) { filSelectField(o); false };
+ var parfunc = function(val) { return (val.split(/, */))[val.split(/, */).length-1]; };
+ var readfunc = function(c) {
+ var l = [];
+ c.value.replace(/g([0-9]+):/g, function (a, e) { l.push(e); return '' });
+ return l;
+ };
+ var fetch = function(c) {
+ var v = c.fil_val;
+ if(!v[0]) {
+ c.value = '';
+ return;
+ }
+ var q = []; var t = [];
+ for(var i=0; i<v.length; i++) {
+ q.push('id='+v[i]);
+ t.push('g'+v[i]+':');
+ }
+ c.value = mt('_js_loading')+' (tags: '+t.join(',')+')';
+ c.disabled = true;
+ if(visible)
+ ajax('/xml/tags.xml?'+q.join(';'), function (hr) {
+ var l = [];
+ var items = hr.responseXML.getElementsByTagName('item');
+ for(var i=0; i<items.length; i++)
+ l.push('g'+items[i].getAttribute('id')+':'+items[i].firstChild.nodeValue);
+ c.value = l.join(', ');
+ c.disabled = false;
+ }, 1);
+ };
+ var writefunc = function(c,v) { c.fil_val = v; fetch(c) };
+ var showfunc = function(c) { visible = true; fetch(c); };
+ dsInit(input, '/xml/tags.xml?q=', trfunc, serfunc, retfunc, parfunc);
+ return [name, label, input, readfunc, writefunc, showfunc];
+}
+
function filReleases() {
var types = release_types;
for(var i=0; i<types.length; i++) // l10n /_rtype_.+/
@@ -2020,28 +2078,6 @@ function filVN() {
for(var i=0; i<len.length; i++) // l10n /_vnlength_.+/
len[i] = [ len[i], mt('_vnlength_'+len[i]) ];
- // tag include/exclude dropdown search
- var taginc = tag('input', {type:'text', 'class':'text', style:'width:350px', onfocus:filSelectField});
- var tagexc = tag('input', {type:'text', 'class':'text', style:'width:350px', onfocus:filSelectField});
- 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;
- filSelectField(obj);
- return tags.join(', ');
- };
- var retfunc = function(o) { filSelectField(o); false };
- var parfunc = function(val) { return (val.split(/, */))[val.split(/, */).length-1]; };
- var readfunc = function(c) { return c.value.split(/, */) };
- var writefunc = function(c,v) { c.value = v.join(', ') };
- dsInit(taginc, '/xml/tags.xml?q=', trfunc, serfunc, retfunc, parfunc);
- dsInit(tagexc, '/xml/tags.xml?q=', trfunc, serfunc, retfunc, parfunc);
-
return [
mt('_vnbrowse_fil_title'),
[ mt('_vnbrowse_general'),
@@ -2050,8 +2086,8 @@ function filVN() {
],
[ mt('_vnbrowse_tags'),
[ '', ' ', tag('('+mt('_vnbrowse_booland')+')') ],
- [ 'taginc', mt('_vnbrowse_taginc'), taginc, readfunc, writefunc ],
- [ 'tagexc', mt('_vnbrowse_tagexc'), tagexc, readfunc, writefunc ],
+ filFTagInput('tag_inc', mt('_vnbrowse_taginc')),
+ filFTagInput('tag_exc', mt('_vnbrowse_tagexc')),
filFOptions('tagspoil', ' ', [[0, mt('_vnbrowse_spoil0')],[1, mt('_vnbrowse_spoil1')],[2, mt('_vnbrowse_spoil2')]],
function (o) { var s = getCookie('tagspoil'); if(o+'' == '') return s == null ? 0 : s; setCookie('tagspoil', o); return o})
],
diff --git a/lib/VNDB/DB/Tags.pm b/lib/VNDB/DB/Tags.pm
index 4a87713b..b3e16960 100644
--- a/lib/VNDB/DB/Tags.pm
+++ b/lib/VNDB/DB/Tags.pm
@@ -24,7 +24,7 @@ sub dbTagGet {
my %where = (
$o{id} ? (
- 't.id = ?' => $o{id} ) : (),
+ 't.id IN(!l)' => [ ref $o{id} ? $o{id} : [$o{id}] ] ) : (),
$o{noid} ? (
't.id <> ?' => $o{noid} ) : (),
$o{name} ? (
diff --git a/lib/VNDB/DB/VN.pm b/lib/VNDB/DB/VN.pm
index d25a5796..b01d3328 100644
--- a/lib/VNDB/DB/VN.pm
+++ b/lib/VNDB/DB/VN.pm
@@ -10,7 +10,8 @@ use Encode 'decode_utf8';
our @EXPORT = qw|dbVNGet dbVNRevisionInsert dbVNImageId dbScreenshotAdd dbScreenshotGet dbScreenshotRandom|;
-# Options: id, rev, char, search, length, lang, olang, plat, tags_include, tags_exclude, hasani, results, page, what, sort, reverse
+# Options: id, rev, char, search, length, lang, olang, plat, tag_inc, tag_exc, tagspoil,
+# hasani, results, page, what, sort, reverse
# What: extended anime relations screenshots relgraph rating ranking changes
# Sort: id rel pop rating title tagscore rand
sub dbVNGet {
@@ -19,6 +20,12 @@ sub dbVNGet {
$o{page} ||= 1;
$o{what} ||= '';
$o{sort} ||= 'title';
+ $o{tagspoil} //= 2;
+
+ # user input that is literally added to the query should be checked...
+ die "Invalid input for tagspoil or tag_inc at dbVNGet()\n" if
+ grep !defined($_) || $_!~/^\d+$/, $o{tagspoil},
+ !$o{tag_inc} ? () : (ref($o{tag_inc}) ? @{$o{tag_inc}} : $o{tag_inc});
my @where = (
$o{id} ? (
@@ -39,19 +46,18 @@ sub dbVNGet {
'('.join(' OR ', map "v.c_platforms ILIKE '%%$_%%'", ref $o{plat} ? @{$o{plat}} : $o{plat}).')' => 1 ) : (),
defined $o{hasani} ? (
'!sEXISTS(SELECT 1 FROM vn_anime va WHERE va.vid = vr.id)' => [ $o{hasani} ? '' : 'NOT ' ]) : (),
- $o{tags_include} && @{$o{tags_include}} ? (
+ $o{tag_inc} ? (
'v.id IN(SELECT vid FROM tags_vn_inherit WHERE tag IN(!l) AND spoiler <= ? GROUP BY vid HAVING COUNT(tag) = ?)',
- [ $o{tags_include}[1], $o{tags_include}[0], $#{$o{tags_include}[1]}+1 ]
- ) : (),
- $o{tags_exclude} && @{$o{tags_exclude}} ? (
- 'v.id NOT IN(SELECT vid FROM tags_vn_inherit WHERE tag IN(!l))' => [ $o{tags_exclude} ] ) : (),
+ [ ref $o{tag_inc} ? $o{tag_inc} : [$o{tag_inc}], $o{tagspoil}, ref $o{tag_inc} ? $#{$o{tag_inc}}+1 : 1 ]) : (),
+ $o{tag_exc} ? (
+ 'v.id NOT IN(SELECT vid FROM tags_vn_inherit WHERE tag IN(!l))' => [ ref $o{tag_exc} ? $o{tag_exc} : [$o{tag_exc}] ] ) : (),
$o{search} ? (
map +('v.c_search like ?', "%$_%"), normalize_query($o{search})) : (),
# don't fetch hidden items unless we ask for an ID
!$o{id} && !$o{rev} ? (
'v.hidden = FALSE' => 0 ) : (),
# optimize fetching random entries (only when there are no other filters present, otherwise this won't work well)
- $o{sort} eq 'rand' && $o{results} <= 10 && !grep(!/^(?:results|page|what|sort)$/, keys %o) ? (
+ $o{sort} eq 'rand' && $o{results} <= 10 && !grep(!/^(?:results|page|what|sort|tagspoil)$/, keys %o) ? (
sprintf 'v.id IN(SELECT floor(random() * last_value)::integer
FROM generate_series(1,20), (SELECT last_value FROM vn_id_seq) s1
LIMIT 20)' ) : (),
@@ -69,7 +75,7 @@ sub dbVNGet {
'JOIN relgraphs vg ON vg.id = v.rgraph' : (),
);
- my $tag_ids = $o{tags_include} && join ',', @{$o{tags_include}[1]};
+ my $tag_ids = $o{tag_inc} && join ',', ref $o{tag_inc} ? @{$o{tag_inc}} : $o{tag_inc};
my @select = ( # see https://rt.cpan.org/Ticket/Display.html?id=54224 for the cast on c_languages
qw|v.id v.locked v.hidden v.c_released v.c_languages::text[] v.c_platforms vr.title vr.original v.rgraph|, 'vr.id AS cid',
$o{what} =~ /extended/ ? (
@@ -84,7 +90,7 @@ sub dbVNGet {
) : (),
# TODO: optimize this, as it will be very slow when the selected tags match a lot of VNs (>1000)
$tag_ids ?
- qq|(SELECT AVG(tvh.rating) FROM tags_vn_inherit tvh WHERE tvh.tag IN($tag_ids) AND tvh.vid = v.id AND spoiler <= $o{tags_include}[0] GROUP BY tvh.vid) AS tagscore| : (),
+ qq|(SELECT AVG(tvh.rating) FROM tags_vn_inherit tvh WHERE tvh.tag IN($tag_ids) AND tvh.vid = v.id AND spoiler <= $o{tagspoil} GROUP BY tvh.vid) AS tagscore| : (),
);
my $order = sprintf {
diff --git a/lib/VNDB/Handler/Tags.pm b/lib/VNDB/Handler/Tags.pm
index 0567bff6..6e373d04 100644
--- a/lib/VNDB/Handler/Tags.pm
+++ b/lib/VNDB/Handler/Tags.pm
@@ -44,7 +44,8 @@ sub tagpage {
results => 50,
page => $f->{p},
sort => $f->{s}, reverse => $f->{o} eq 'd',
- tags_include => [ $f->{m}, [$tag ]],
+ tagspoil => $f->{m},
+ tag_inc => $tag,
);
my $title = mt '_tagp_title', $t->{meta}?0:1, $t->{name};
@@ -668,19 +669,22 @@ sub fulltree {
sub tagxml {
my $self = shift;
- my $q = $self->formValidate({ name => 'q', maxlength => 500 });
- return 404 if $q->{_err};
- $q = $q->{q};
+ my $f = $self->formValidate(
+ { name => 'q', required => 0, maxlength => 500 },
+ { name => 'id', required => 0, multi => 1, template => 'int' },
+ );
+ return 404 if $f->{_err} || (!$f->{q} && !$f->{id} && !$f->{id}[0]);
my($list, $np) = $self->dbTagGet(
- $q =~ /^g([1-9]\d*)/ ? (id => $1) : $q =~ /^name:(.+)$/ ? (name => $1) : (search => $q),
+ !$f->{q} ? () : $f->{q} =~ /^g([1-9]\d*)/ ? (id => $1) : $f->{q} =~ /^name:(.+)$/ ? (name => $1) : (search => $f->{q}),
+ $f->{id} && $f->{id}[0] ? (id => $f->{id}) : (),
results => 15,
page => 1,
);
$self->resHeader('Content-type' => 'text/xml; charset=UTF-8');
xml;
- tag 'tags', more => $np ? 'yes' : 'no', query => $q;
+ tag 'tags', more => $np ? 'yes' : 'no', $f->{q} ? (query => $f->{q}) : ();
for(@$list) {
tag 'item', id => $_->{id}, meta => $_->{meta} ? 'yes' : 'no', state => $_->{state}, $_->{name};
}
diff --git a/lib/VNDB/Handler/VNBrowse.pm b/lib/VNDB/Handler/VNBrowse.pm
index 8e30da99..25166717 100644
--- a/lib/VNDB/Handler/VNBrowse.pm
+++ b/lib/VNDB/Handler/VNBrowse.pm
@@ -25,7 +25,7 @@ sub list {
);
return 404 if $f->{_err};
$f->{q} ||= $f->{sq};
- my $fil = fil_parse $f->{fil}, qw|length hasani taginc tagexc tagspoil lang olang plat|;
+ my $fil = fil_parse $f->{fil}, qw|length hasani tag_inc tag_exc taginc tagexc tagspoil lang olang plat|;
_fil_compat($self, $fil);
if($f->{q}) {
@@ -39,20 +39,7 @@ sub list {
}
$f->{fil} = fil_serialize $fil;
- # TODO: this should be moved to dbVNGet() in order for savable VN filters to be useful
- my @ignored;
- my $tagfind = sub {
- return map {
- my $i = $self->dbTagGet(name => $_)->[0];
- push @ignored, [$_, 0] if !$i;
- push @ignored, [$_, 1] if $i && $i->{meta};
- $i && !$i->{meta} ? $i->{id} : ();
- } grep $_, ref $_[0] ? @{$_[0]} : ($_[0]||'')
- };
- my @ti = $tagfind->(delete $fil->{taginc});
- my @te = $tagfind->(delete $fil->{tagexc});
-
- $f->{s} = 'title' if !@ti && $f->{s} eq 'tagscore';
+ $f->{s} = 'title' if !$fil->{tag_inc} && $f->{s} eq 'tagscore';
$f->{o} = $f->{s} eq 'tagscore' ? 'd' : 'a' if !$f->{o};
my($list, $np) = $self->dbVNGet(
@@ -62,8 +49,6 @@ sub list {
results => 50,
page => $f->{p},
sort => $f->{s}, reverse => $f->{o} eq 'd',
- @ti ? (tags_include => [ delete $fil->{tagspoil}, \@ti ]) : (),
- @te ? (tags_exclude => \@te) : (),
%$fil
);
@@ -82,15 +67,6 @@ sub list {
}
end;
- if(@ignored) {
- div class => 'warning';
- h2 mt '_vnbrowse_tagign_title';
- ul;
- li $_->[0].' ('.mt('_vnbrowse_tagign_'.($_->[1]?'meta':'notfound')).')' for @ignored;
- end;
- end;
- }
-
a id => 'filselect', href => '#v';
lit '<i>&#9656;</i> '.mt('_rbrowse_filters').'<i></i>'; # TODO: it's not *r*browse
end;
@@ -98,7 +74,7 @@ sub list {
end;
end; # /form
- $self->htmlBrowseVN($list, $f, $np, "/v/$char?q=$f->{q};fil=$f->{fil}", scalar @ti);
+ $self->htmlBrowseVN($list, $f, $np, "/v/$char?q=$f->{q};fil=$f->{fil}", $fil->{tag_inc});
$self->htmlFooter;
}
@@ -117,6 +93,16 @@ sub _fil_compat {
$fil->{taginc} //= $f->{ti} if $f->{ti};
$fil->{tagexc} //= $f->{te} if $f->{te};
$fil->{tagspoil} //= $f->{sp};
+
+ # older tag specification (by name rather than ID)
+ my $tagfind = sub {
+ return map {
+ my $i = $self->dbTagGet(name => $_)->[0];
+ $i && !$i->{meta} ? $i->{id} : ();
+ } grep $_, ref $_[0] ? @{$_[0]} : ($_[0]||'')
+ };
+ $fil->{tag_inc} //= [ $tagfind->(delete $fil->{taginc}) ] if $fil->{taginc};
+ $fil->{tag_exc} //= [ $tagfind->(delete $fil->{tagexc}) ] if $fil->{tagexc};
}