summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--data/js/filter.js288
-rw-r--r--data/style.css58
-rw-r--r--lib/VNDB/DB/Releases.pm39
-rw-r--r--lib/VNDB/DB/VN.pm10
-rw-r--r--lib/VNDB/Handler/Chars.pm6
-rw-r--r--lib/VNDB/Handler/Releases.pm6
-rw-r--r--lib/VNDB/Handler/Staff.pm6
-rw-r--r--lib/VNDB/Handler/Tags.pm6
-rw-r--r--lib/VNDB/Handler/Traits.pm6
-rw-r--r--lib/VNDB/Handler/VNBrowse.pm23
-rw-r--r--lib/VNDB/Util/Misc.pm2
11 files changed, 280 insertions, 170 deletions
diff --git a/data/js/filter.js b/data/js/filter.js
index e9148ab7..1f0ac418 100644
--- a/data/js/filter.js
+++ b/data/js/filter.js
@@ -1,7 +1,7 @@
/* Filter box definition:
* [ <title>,
* [ <category_name>,
- * [ <fieldcode>, <fieldname>, <fieldcontents>, <fieldreadfunc>, <fieldwritefunc> ], ..
+ * [ <fieldcode>, <fieldname>, <fieldcontents>, <fieldreadfunc>, <fieldwritefunc>, <fieldshowfunc> ], ..
* ], ..
* ]
* Where:
@@ -12,6 +12,7 @@
* <fieldcontents> tag() object, or an array of tag() objects
* <fieldreadfunc> function reference. argument: <fieldcontents>; must return data to be used in the filter format
* <fieldwritefunc> function reference, argument: <fieldcontents>, data from filter format; must update the contents with the passed data
+ * <fieldshowfunc> function reference, argument: <fieldcontents>, called when the field is displayed
*
* Filter string format:
* <field>-<value1>~<value2>.<field2>-<value>.<field3>-<value1>~<value2>
@@ -25,211 +26,277 @@
* For boolean fields, the <value> is either 0 or 1.
*/
-var fil_cats; // [ <object with field->tr mapping>, <category-link1>, .. ]
var fil_escape = "_ !\"#$%&'()*+,-./:;<=>?@[\\]^`{|}~".split('');
-function filLoad() {
- var l = byId('filselect').href.match(/#r$/) ? filReleases()
- : byId('filselect').href.match(/#c$/) ? filChars()
- : byId('filselect').href.match(/#s$/) ? filStaff()
- : filVN();
- fil_cats = [ new Object ];
+var fil_objs = [];
+function getObj(obj) {
+ while(!obj.fil_fields)
+ obj = obj.parentNode;
+ return obj;
+}
+
+
+function filLoad(lnk, serobj) {
+ var type = lnk.href.match(/#r$/) ? 'r' : lnk.href.match(/#c$/) ? 'c' : lnk.href.match(/#s$/) ? 's' : 'v';
+ var l = {r: filReleases, c: filChars, s: filStaff, v: filVN}[type]();
+
+ var fields = {};
var p = tag('p', {'class':'browseopts'});
var c = tag('div', null);
var idx = 0;
for(var i=1; i<l.length; i++) {
if(!l[i])
continue;
- idx++;
// category link
- var a = tag('a', { href: '#', onclick: filSelectCat, fil_num: idx, fil_onshow:[] }, l[i][0]);
+ var a = tag('a', { href: '#', onclick: selectCat, fil_onshow:[] }, l[i][0]);
p.appendChild(a);
p.appendChild(tag(' '));
// category contents
- var t = tag('table', {'class':'formtable', fil_num: idx}, null);
- setClass(t, 'hidden', true);
+ var t = tag('table', {'class':'formtable hidden', fil_a: a}, null);
a.fil_t = t;
for(var j=1; j<l[i].length; j++) {
var fd = l[i][j];
var lab = typeof fd[1] == 'object' ? fd[1][0] : fd[1];
- var f = tag('tr', {'class':'newfield', fil_code: fd[0], fil_contents: fd[2], fil_readfunc: fd[3], fil_writefunc: fd[4]},
- fd[0] ? tag('td', {'class':'check'}, tag('input', {type:'checkbox', id:'fil_check_'+fd[0], 'class':fd[1]==' '?'hidden':'', name:'fil_check_'+fd[0], onclick: filSelectField })) : tag('td', null),
+ var name = 'fil_check_'+type+'_'+fd[0];
+ var f = tag('tr', {'class':'newfield', fil_code: fd[0], fil_readfunc: fd[3], fil_writefunc: fd[4]},
+ // Checkbox
+ fd[0] ? tag('td', {'class':'check'},
+ tag('input', {type:'checkbox', id:name, name:name, 'class': 'enabled_check'+(fd[1]==' '?' hidden':''), onclick: selectField }))
+ : tag('td', null),
+ // Label
fd[1] ? tag('td', {'class':'label'},
- tag('label', {'for':'fil_check_'+fd[0]}, lab),
+ tag('label', {'for':name}, lab),
typeof fd[1] == 'object' ? tag('b', fd[1][1]) : null
) : null,
+ // Contents
tag('td', {'class':'cont' }, fd[2]));
if(fd[0])
- fil_cats[0][fd[0]] = f;
+ fields[fd[0]] = f;
if(fd[5])
- a.fil_onshow.push([ fd[5], f.fil_contents ]);
+ a.fil_onshow.push([ fd[5], fd[2] ]);
t.appendChild(f);
}
c.appendChild(t);
-
- fil_cats[idx] = a;
+ idx++;
}
- addBody(tag('div', { id: 'fil_div', 'class':'hidden' },
- tag('a', {href:'#', onclick:filShow, 'class':'close'}, mt('_js_close')),
+ var savenote = tag('p', {'class':'hidden'}, '')
+ var obj = tag('div', {
+ 'class': 'fil_div hidden',
+ fil_fields: fields,
+ fil_cats: byName(p, 'a'),
+ fil_savenote: savenote,
+ fil_serobj: serobj,
+ fil_lnk: lnk,
+ fil_type: type
+ },
+ tag('a', {href:'#', onclick:show, 'class':'close'}, mt('_js_close')),
tag('h3', l[0]),
p,
tag('b', {'class':'ruler'}, null),
c,
tag('b', {'class':'ruler'}, null),
tag('input', {type:'button', 'class':'submit', value: mt('_js_fil_apply'), onclick:function () {
- var f = byId('fil');
+ var f = serobj;
while(f.nodeName.toLowerCase() != 'form')
f = f.parentNode;
f.submit();
}}),
- tag('input', {type:'button', 'class':'submit', value: mt('_js_fil_reset'), onclick:function () { byId('fil').value = ''; filDeSerialize()} }),
- byId('pref_code') ? tag('input', {type:'button', 'class':'submit', value: mt('_js_fil_save'), onclick:filSaveDefault }) : null,
- tag('p', {id:'fil_savenote', 'class':'hidden'}, '')
- ));
- filSelectCat(1);
- byId('filselect').onclick = filShow;
- filDeSerialize();
+ tag('input', {type:'button', 'class':'submit', value: mt('_js_fil_reset'), onclick:function () { serobj.value = ''; deSerialize(obj) } }),
+ byId('pref_code') && lnk.id != 'rfilselect' ? tag('input', {type:'button', 'class':'submit', value: mt('_js_fil_save'), onclick:saveDefault }) : null,
+ savenote
+ );
+ lnk.fil_obj = obj;
+ lnk.onclick = show;
+
+ addBody(obj);
+ fil_objs.push(obj);
+ deSerialize(obj);
+ selectCat(obj.fil_cats[0]);
}
-function filSaveDefault() {
+
+function saveDefault() {
var but = this;
- var note = byId('fil_savenote');
+ var obj = getObj(this);
+ var note = obj.fil_savenote;
setText(note, mt('_js_loading'));
but.enabled = false;
- setClass(byId('fil_savenote'), 'hidden', false);
- var type = byId('filselect').href.match(/#r$/) ? 'release' : 'vn';
- ajax('/xml/prefs.xml?formcode='+byId('pref_code').title+';key=filter_'+type+';value='+byId('fil').value, function (hr) {
+ setClass(note, 'hidden', false);
+ var type = obj.fil_type == 'r' ? 'release' : 'vn';
+ ajax('/xml/prefs.xml?formcode='+byId('pref_code').title+';key=filter_'+type+';value='+obj.fil_serobj.value, function (hr) {
setText(note, mt('_js_fil_savenote'));
but.enable = true;
});
}
-function filSelectCat(n) {
- setClass(byId('fil_savenote'), 'hidden', true);
- n = this.fil_num ? this.fil_num : n;
- for(var i=1; i<fil_cats.length; i++) {
- setClass(fil_cats[i], 'optselected', i == n);
- setClass(fil_cats[i].fil_t, 'hidden', i != n);
+
+function selectCat(n) {
+ var lnk = n.fil_onshow ? n : this;
+ var obj = getObj(lnk);
+ setClass(obj.fil_savenote, 'hidden', true);
+ for(var i=0; i<obj.fil_cats.length; i++) {
+ var n = obj.fil_cats[i];
+ setClass(n, 'optselected', n == lnk);
+ setClass(n.fil_t, 'hidden', n != lnk);
}
- 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]);
+ for(var i=0; i<lnk.fil_onshow.length; i++)
+ lnk.fil_onshow[i][0](lnk.fil_onshow[i][1]);
return false
}
-function filSelectField(obj) {
- var t = obj && obj.parentNode ? obj : this;
- setClass(byId('fil_savenote'), 'hidden', true);
+
+function selectField(f) {
+ if(!f.parentNode)
+ f = this;
+ setClass(getObj(f).fil_savenote, 'hidden', true);
+
// update checkbox and label
- var o = t;
+ var o = f;
while(o.nodeName.toLowerCase() != 'tr')
o = o.parentNode;
- var c = byId('fil_check_'+o.fil_code);
- if(c != t)
+ var c = byClass(o, 'enabled_check')[0];
+ if(c != f)
c.checked = true;
- if(hasClass(c, 'hidden'))
+ if(hasClass(c, 'hidden')) // When there's no label (e.g. tagspoil selector)
c.checked = true;
- setClass(byName(o, 'label')[0], 'active', c.checked);
+ var l = byName(o, 'label')[0];
+ if(l)
+ setClass(l, 'active', c.checked);
// update category link
while(o.nodeName.toLowerCase() != 'table')
o = o.parentNode;
- var l = byName(o, 'input');
+ var l = byName(o, 'tr');
var n=0;
- for(var i=0; i<l.length; i++)
- if(l[i].type == 'checkbox' && l[i].id.substr(0, 10) == 'fil_check_' && !hasClass(l[i], 'hidden') && l[i].checked)
+ for(var i=0; i<l.length; i++) {
+ var ch = byClass(l[i], 'enabled_check')[0];
+ if(ch && !hasClass(ch, 'hidden') && ch.checked)
n++;
- setClass(fil_cats[o.fil_num], 'active', n>0);
+ }
+ setClass(o.fil_a, 'active', n>0);
// serialize
- filSerialize();
+ serialize(getObj(o));
return true;
}
-function filSerialize() {
+
+function escapeVal(val) {
+ var r = [];
+ for(var h=0; h<val.length; h++) {
+ var vs = (''+val[h]).split('');
+ r[h] = '';
+
+ // this isn't a very fast escaping method, blame JavaScript for inflexible search/replace support
+ for(var i=0; i<vs.length; i++) {
+ for(var j=0; j<fil_escape.length; j++)
+ if(vs[i] == fil_escape[j])
+ break;
+ r[h] += j == fil_escape.length ? vs[i] : '_'+(j<10?'0'+j:j);
+ }
+ }
+
+ return r[0] == '' ? '' : r.join('~');
+}
+
+
+function serialize(obj) {
+ if(!obj.fil_fields)
+ obj = getObj(this);
var num = 0;
var values = {};
- for(var f in fil_cats[0]) {
- if(!byId('fil_check_'+f).checked)
+
+ for(var f in obj.fil_fields) {
+ var fo = obj.fil_fields[f];
+ var ch = byClass(fo, 'enabled_check')[0];
+ if(!ch || !ch.checked)
continue;
- if(!hasClass(byId('fil_check_'+f), 'hidden'))
+ if(!hasClass(ch, 'hidden'))
num++;
- var v = fil_cats[0][f].fil_readfunc(fil_cats[0][f].fil_contents);
- var r = [];
- for(var h=0; h<v.length; h++) {
- 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<vs.length; i++) {
- for(var j=0; j<fil_escape.length; j++)
- if(vs[i] == fil_escape[j])
- break;
- r[h] += j == fil_escape.length ? vs[i] : '_'+(j<10?'0'+j:j);
- }
- }
- if(r.length > 0 && r[0] != '')
- values[fil_cats[0][f].fil_code] = r.join('~');
+
+ var v = escapeVal(fo.fil_readfunc(byClass(fo, 'cont')[0].childNodes[0]));
+ if(v != '')
+ values[fo.fil_code] = v;
}
+
if(!values['tag_inc'] && !values['trait_inc'])
delete values['tagspoil'];
+
var l = [];
for(var f in values)
l.push(f+'-'+values[f]);
- byId('fil').value = l.join('.');
- setText(byName(byId('filselect'), 'i')[1], num > 0 ? ' ('+num+')' : '');
+
+ obj.fil_serobj.value = l.join('.');
+ setText(byName(obj.fil_lnk, 'i')[1], num > 0 ? ' ('+num+')' : '');
}
-function filDeSerialize() {
- var d = byId('fil').value;
+
+function deSerialize(obj) {
+ var d = obj.fil_serobj.value;
var fs = d.split('.');
- var f = new Object;
+
+ var f = {};
for(var i=0; i<fs.length; i++) {
var v = fs[i].split('-');
- if(fil_cats[0][v[0]])
+ if(obj.fil_fields[v[0]])
f[v[0]] = v[1];
}
- for(var fn in fil_cats[0])
+
+ for(var fn in obj.fil_fields)
if(!f[fn])
f[fn] = '';
for(var fn in f) {
- var c = byId('fil_check_'+fn);
+ var c = byClass(obj.fil_fields[fn], 'enabled_check')[0];
if(!c)
continue;
- c.checked = f[fn] == '' ? false : true;
+ c.checked = f[fn] != '';
+
var v = f[fn].split('~');
for(var i=0; i<v.length; i++)
v[i] = v[i].replace(/_([0-9]{2})/g, function (a, e) { return fil_escape[Math.floor(e)] });
- fil_cats[0][fn].fil_writefunc(fil_cats[0][fn].fil_contents, v);
- // not very efficient: filSelectField() does a lot of things that can be
+
+ obj.fil_fields[fn].fil_writefunc(byClass(obj.fil_fields[fn], 'cont')[0].childNodes[0], v);
+ // not very efficient: selectField() does a lot of things that can be
// batched after all fields have been updated, and in some cases the
- // writefunc() triggers the same filSelectField() as well
- filSelectField(c);
+ // writefunc() triggers the same selectField() as well
+ selectField(c);
}
}
-function filShow() {
- var div = byId('fil_div');
- var hid = !hasClass(div, 'hidden');
- setClass(div, 'hidden', hid);
- setText(byName(byId('filselect'), 'i')[0], hid ? collapsed_icon : expanded_icon);
- setClass(byId('fil_savenote'), 'hidden', true);
- var o = this;
+function show() {
+ var obj = this.fil_obj || getObj(this);
+
+ // Hide other filter objects
+ for(var i=0; i<fil_objs.length; i++)
+ if(fil_objs[i] != obj) {
+ setClass(fil_objs[i], 'hidden', true);
+ setText(byName(fil_objs[i].fil_lnk, 'i')[0], collapsed_icon);
+ }
+
+ var hid = !hasClass(obj, 'hidden');
+ setClass(obj, 'hidden', hid);
+ setText(byName(obj.fil_lnk, 'i')[0], hid ? collapsed_icon : expanded_icon);
+ setClass(obj.fil_savenote, 'hidden', true);
+
+ var o = obj.fil_lnk;
ddx = ddy = 0;
do {
ddx += o.offsetLeft;
ddy += o.offsetTop;
} while(o = o.offsetParent);
- ddy += this.offsetHeight+2;
- ddx += (this.offsetWidth-div.offsetWidth)/2;
- div.style.left = ddx+'px';
- div.style.top = ddy+'px';
+ ddy += obj.fil_lnk.offsetHeight+2;
+ ddx += (obj.fil_lnk.offsetWidth-obj.offsetWidth)/2;
+ obj.style.left = ddx+'px';
+ obj.style.top = ddy+'px';
return false;
}
+
var curSlider = null;
function filFSlider(c, n, min, max, def, unit) {
var bw = 200; var pw = 1; // slidebar width and pointer width
@@ -277,7 +344,7 @@ function filFSlider(c, n, min, max, def, unit) {
curSlider.oldmousemove = null;
document.onmouseup = curSlider.oldmouseup;
curSlider.oldmouseup = null;
- filSelectField(curSlider);
+ selectField(curSlider);
return false;
}
document.onmousemove = set;
@@ -288,7 +355,7 @@ function filFSlider(c, n, min, max, def, unit) {
}
function filFSelect(c, n, lines, opts) {
- var s = tag('select', {onfocus: filSelectField, onchange: filSerialize, multiple: lines > 1, size: lines});
+ var s = tag('select', {onfocus: selectField, onchange: serialize, multiple: lines > 1, size: lines});
for(var i=0; i<opts.length; i++) {
if(typeof opts[i][1] != 'object')
s.appendChild(tag('option', {name: opts[i][0]}, opts[i][1]));
@@ -327,7 +394,7 @@ function filFOptions(c, n, opts) {
setClass(l[i], 'tsel', l[i].fil_n+'' == o+'');
p.fil_val = o;
if(typeof e != 'string')
- filSelectField(p);
+ selectField(p);
return false
};
for(var i=0; i<opts.length; i++) {
@@ -355,7 +422,7 @@ function filFTagInput(name, label, type) {
// a -> li -> ul -> div
var ul = this.parentNode.parentNode;
ul.removeChild(this.parentNode);
- filSelectField(ul.parentNode);
+ selectField(ul.parentNode);
return false
}
}, mt('_js_remove')), ')'
@@ -394,7 +461,7 @@ function filFTagInput(name, label, type) {
c.fil_val = null;
}, 1);
};
- var input = tag('input', {type:'text', 'class':'text', style:'width:300px', onfocus:filSelectField});
+ var input = tag('input', {type:'text', 'class':'text', style:'width:300px', onfocus:selectField});
var list = tag('ul', null);
dsInit(input, src+'?q=',
function(item, tr) {
@@ -411,11 +478,11 @@ function filFTagInput(name, label, type) {
alert(mt('_js_ds_'+type+'_nometa'));
else {
addtag(byName(obj.parentNode, 'ul')[0], item.getAttribute('id'), item.firstChild.nodeValue, item.getAttribute('groupname'));
- filSelectField(obj);
+ selectField(obj);
}
return '';
},
- function(o) { filSelectField(o) }
+ function(o) { selectField(o) }
);
return [
@@ -476,13 +543,14 @@ function filReleases() {
filFOptions('patch', mt('_rbrowse_patch'), [ [1, mt('_rbrowse_patch_yes')], [0, mt('_rbrowse_patch_no')] ]),
filFOptions('freeware', mt('_rbrowse_freeware'),[ [1, mt('_rbrowse_freeware_yes')], [0, mt('_rbrowse_freeware_no')] ]),
filFOptions('doujin', mt('_rbrowse_doujin'), [ [1, mt('_rbrowse_doujin_yes')], [0, mt('_rbrowse_doujin_no')] ]),
- [ 'date_after', mt('_rbrowse_dateafter'), dateLoad(null, filSelectField), function (c) { return [c.date_val] }, function(o,v) { o.dateSet(v) } ],
- [ 'date_before', mt('_rbrowse_datebefore'), dateLoad(null, filSelectField), function (c) { return [c.date_val] }, function(o,v) { o.dateSet(v) } ],
+ [ 'date_after', mt('_rbrowse_dateafter'), dateLoad(null, selectField), function (c) { return [c.date_val] }, function(o,v) { o.dateSet(v) } ],
+ [ 'date_before', mt('_rbrowse_datebefore'), dateLoad(null, selectField), function (c) { return [c.date_val] }, function(o,v) { o.dateSet(v) } ],
filFOptions('released', mt('_rbrowse_released'),[ [1, mt('_rbrowse_released_yes')], [0, mt('_rbrowse_released_no')] ])
],
[ mt('_rbrowse_minage'), filFSelect('minage', mt('_rbrowse_minage'), 15, VARS.age_ratings) ],
[ mt('_rbrowse_language'), filFSelect('lang', mt('_rbrowse_language'), 20, VARS.languages) ],
- [ mt('_rbrowse_olang'), filFSelect('olang', mt('_rbrowse_olang'), 20, VARS.languages) ],
+ byId('rfilselect') ? null :
+ [ mt('_rbrowse_olang'), filFSelect('olang', mt('_rbrowse_olang'), 20, VARS.languages) ],
[ mt('_rbrowse_resolution'), filFSelect('resolution', mt('_rbrowse_resolution'), 15, VARS.resolutions) ],
[ mt('_rbrowse_platform'), filFSelect('plat', mt('_rbrowse_platform'), 20, plat) ],
[ mt('_rbrowse_medium'), filFSelect('med', mt('_rbrowse_medium'), 10, med) ],
@@ -554,4 +622,6 @@ function filStaff() {
}
if(byId('filselect'))
- filLoad();
+ filLoad(byId('filselect'), byId('fil'));
+if(byId('rfilselect'))
+ filLoad(byId('rfilselect'), byId('rfil'));
diff --git a/data/style.css b/data/style.css
index 697efe27..de0454a3 100644
--- a/data/style.css
+++ b/data/style.css
@@ -459,13 +459,6 @@ div.scr_uploader { visibility: hidden; overflow: hidden; width: 1px; height:
.vnbrowse .tc6 { width: 80px }
.vnbrowse .tc7 { text-align: right; width: 8px }
.vnbrowse .tc8 { width: 8px }
-#filselect {
- text-align: center;
- display: block;
- margin: 10px auto 3px auto;
- border: none;
- outline: none;
-}
@@ -844,7 +837,7 @@ div#iv_view {
/****** filter selector *****/
-div#fil_div {
+.fil_div {
position: absolute;
top: 0px;
left: 0px;
@@ -854,26 +847,35 @@ div#fil_div {
width: 600px;
text-align: center;
}
-#fil_div a.close { float: right; border: 0; font-weight: bold }
-#fil_div p.browseopts { padding: 2px 50px; line-height: 23px }
-#fil_div .browseopts a { outline: none; color: $maintext$ }
-#fil_div .browseopts a.active { font-weight: bold }
-#fil_div b.ruler { display: block; margin: auto; width: 93%; height: 1px; border-bottom: 1px solid $border$; margin-bottom: 5px }
-#fil_div h3 { width: 100%; text-align: center; font-size: 11px }
-#fil_div table { width: 93%; text-align: left; margin: 0 auto 5px auto }
-#fil_div table td.label label { width: 120px }
-#fil_div table td.label b { display: block; font-weight: normal; padding: 10px 5px 0 0 }
-#fil_div table td.check { width: 15px }
-#fil_div label.active { font-weight: bold }
-#fil_div .opts a { border: 0; outline: none }
-#fil_div .opts b { margin: 0 7px; font-weight: normal }
-#fil_div .opts a.tsel { color: $maintext$; }
-#filselect i { font-style: normal }
-#fil_div table ul { margin: 0 0 0 15px }
-#fil_div .slider p { margin: 1px; }
-#fil_div .slider div { margin: 1px; border: 1px solid $secborder$; float: left; height: 12px; }
-#fil_div .slider div div { border-top: none; border-bottom: none; cursor: default; position: relative; height: 10px; margin: 1px; }
-#fil_div .slider span { margin-left: 5px }
+.fil_div a.close { float: right; border: 0; font-weight: bold }
+.fil_div p.browseopts { padding: 2px 50px; line-height: 23px }
+.fil_div .browseopts a { outline: none; color: $maintext$ }
+.fil_div .browseopts a.active { font-weight: bold }
+.fil_div b.ruler { display: block; margin: auto; width: 93%; height: 1px; border-bottom: 1px solid $border$; margin-bottom: 5px }
+.fil_div h3 { width: 100%; text-align: center; font-size: 11px }
+.fil_div table { width: 93%; text-align: left; margin: 0 auto 5px auto }
+.fil_div table td.label label { width: 120px }
+.fil_div table td.label b { display: block; font-weight: normal; padding: 10px 5px 0 0 }
+.fil_div table td.check { width: 15px }
+.fil_div label.active { font-weight: bold }
+.fil_div .opts a { border: 0; outline: none }
+.fil_div .opts b { margin: 0 7px; font-weight: normal }
+.fil_div .opts a.tsel { color: $maintext$; }
+.fil_div table ul { margin: 0 0 0 15px }
+.fil_div .slider p { margin: 1px; }
+.fil_div .slider div { margin: 1px; border: 1px solid $secborder$; float: left; height: 12px; }
+.fil_div .slider div div { border-top: none; border-bottom: none; cursor: default; position: relative; height: 10px; margin: 1px; }
+.fil_div .slider span { margin-left: 5px }
+
+p.filselect {
+ text-align: center;
+ display: block;
+ margin: 10px auto 3px auto;
+ border: none;
+ outline: none;
+}
+p.filselect a { margin: 0 5px }
+p.filselect i { font-style: normal }
diff --git a/lib/VNDB/DB/Releases.pm b/lib/VNDB/DB/Releases.pm
index eafb84c0..ecec63aa 100644
--- a/lib/VNDB/DB/Releases.pm
+++ b/lib/VNDB/DB/Releases.pm
@@ -7,26 +7,15 @@ use POSIX 'strftime';
use Exporter 'import';
use VNDB::Func 'gtintype';
-our @EXPORT = qw|dbReleaseGet dbReleaseGetRev dbReleaseRevisionInsert|;
+our @EXPORT = qw|dbReleaseFilters dbReleaseGet dbReleaseGetRev dbReleaseRevisionInsert|;
-# Options: id vid pid released page results what med sort reverse date_before date_after
-# plat lang olang type minage search resolution freeware doujin voiced ani_story ani_ero
-# What: extended vn producers platforms media affiliates
-# Sort: title released minage
-sub dbReleaseGet {
+# Release filters shared by dbReleaseGet and dbVNGet
+sub dbReleaseFilters {
my($self, %o) = @_;
- $o{results} ||= 50;
- $o{page} ||= 1;
- $o{what} ||= '';
$o{plat} = [ $o{plat} ] if $o{plat} && !ref $o{plat};
$o{med} = [ $o{med} ] if $o{med} && !ref $o{med};
-
- my @where = (
- !$o{id} ? ( 'r.hidden = FALSE' => 0 ) : (),
- $o{id} ? ( 'r.id = ?' => $o{id} ) : (),
- $o{vid} ? ( 'rv.vid IN(!l)' => [ ref $o{vid} ? $o{vid} : [$o{vid}] ] ) : (),
- $o{pid} ? ( 'rp.pid = ?' => $o{pid} ) : (),
+ return (
defined $o{patch} ? ( 'r.patch = ?' => $o{patch} == 1 ? 1 : 0) : (),
defined $o{freeware} ? ( 'r.freeware = ?' => $o{freeware} == 1 ? 1 : 0) : (),
defined $o{doujin} ? ( 'r.doujin = ?' => $o{doujin} == 1 ? 1 : 0) : (),
@@ -52,6 +41,26 @@ sub dbReleaseGet {
grep(!/^unk$/, @{$o{med}}) ? 'r.id IN(SELECT irm.id FROM releases_media irm WHERE irm.medium IN(!l))' : ()
).')', [ [ grep(!/^unk$/, @{$o{med}}) ] ]) : (),
);
+}
+
+
+# Options: id vid pid released page results what med sort reverse date_before date_after
+# plat lang olang type minage search resolution freeware doujin voiced ani_story ani_ero
+# What: extended vn producers platforms media affiliates
+# Sort: title released minage
+sub dbReleaseGet {
+ my($self, %o) = @_;
+ $o{results} ||= 50;
+ $o{page} ||= 1;
+ $o{what} ||= '';
+
+ my @where = (
+ !$o{id} ? ( 'r.hidden = FALSE' => 0 ) : (),
+ $o{id} ? ( 'r.id = ?' => $o{id} ) : (),
+ $o{vid} ? ( 'rv.vid IN(!l)' => [ ref $o{vid} ? $o{vid} : [$o{vid}] ] ) : (),
+ $o{pid} ? ( 'rp.pid = ?' => $o{pid} ) : (),
+ $self->dbReleaseFilters(%o),
+ );
if($o{search}) {
for (split /[ -,._]/, $o{search}) {
diff --git a/lib/VNDB/DB/VN.pm b/lib/VNDB/DB/VN.pm
index 203fdcad..91ad7ccb 100644
--- a/lib/VNDB/DB/VN.pm
+++ b/lib/VNDB/DB/VN.pm
@@ -3,6 +3,7 @@ package VNDB::DB::VN;
use strict;
use warnings;
+use TUWF 'sqlprint';
use Exporter 'import';
use VNDB::Func 'gtintype', 'normalize_query';
use Encode 'decode_utf8';
@@ -11,7 +12,7 @@ our @EXPORT = qw|dbVNGet dbVNGetRev dbVNRevisionInsert dbVNImageId dbScreenshotA
# Options: id, char, search, length, lang, olang, plat, tag_inc, tag_exc, tagspoil,
-# hasani, hasshot, ul_notblack, ul_onwish, results, page, what, sort, reverse, inc_hidden
+# hasani, hasshot, ul_notblack, ul_onwish, results, page, what, sort, reverse, inc_hidden, release
# What: extended anime staff seiyuu relations screenshots relgraph rating ranking wishlist vnlist
# Note: wishlist and vnlist are ignored (no db search) unless a user is logged in
# Sort: id rel pop rating title tagscore rand
@@ -71,6 +72,13 @@ sub dbVNGet {
'v.id IN(SELECT floor(random() * last_value)::integer FROM generate_series(1,20), (SELECT MAX(id) AS last_value FROM vn) s1 LIMIT 20)' ) : (),
);
+ if($o{release}) {
+ my($q, @p) = sqlprint
+ 'v.id IN(SELECT rv.vid FROM releases r JOIN releases_vn rv ON rv.id = r.id !W)',
+ [ 'NOT r.hidden' => 1, $self->dbReleaseFilters(%{$o{release}}), ];
+ push @where, $q, \@p;
+ }
+
my @join = (
$o{what} =~ /relgraph/ ?
'JOIN relgraphs vg ON vg.id = v.rgraph' : (),
diff --git a/lib/VNDB/Handler/Chars.pm b/lib/VNDB/Handler/Chars.pm
index 98847b4c..74029e5c 100644
--- a/lib/VNDB/Handler/Chars.pm
+++ b/lib/VNDB/Handler/Chars.pm
@@ -508,8 +508,10 @@ sub list {
}
end;
- a id => 'filselect', href => '#c';
- lit '<i>&#9656;</i> '.mt('_js_fil_filters').'<i></i>';
+ p class => 'filselect';
+ a id => 'filselect', href => '#c';
+ lit '<i>&#9656;</i> '.mt('_js_fil_filters').'<i></i>';
+ end;
end;
input type => 'hidden', class => 'hidden', name => 'fil', id => 'fil', value => $f->{fil};
end;
diff --git a/lib/VNDB/Handler/Releases.pm b/lib/VNDB/Handler/Releases.pm
index d2497954..890058af 100644
--- a/lib/VNDB/Handler/Releases.pm
+++ b/lib/VNDB/Handler/Releases.pm
@@ -498,8 +498,10 @@ sub browse {
div class => 'mainbox';
h1 mt '_rbrowse_title';
$self->htmlSearchBox('r', $f->{q});
- a id => 'filselect', href => '#r';
- lit '<i>&#9656;</i> '.mt('_js_fil_filters').'<i></i>';
+ p class => 'filselect';
+ a id => 'filselect', href => '#r';
+ lit '<i>&#9656;</i> '.mt('_js_fil_filters').'<i></i>';
+ end;
end;
input type => 'hidden', class => 'hidden', name => 'fil', id => 'fil', value => $f->{fil};
end;
diff --git a/lib/VNDB/Handler/Staff.pm b/lib/VNDB/Handler/Staff.pm
index 0f878ca6..839d118a 100644
--- a/lib/VNDB/Handler/Staff.pm
+++ b/lib/VNDB/Handler/Staff.pm
@@ -326,8 +326,10 @@ sub list {
}
end;
- a id => 'filselect', href => '#s';
- lit '<i>&#9656;</i> '.mt('_js_fil_filters').'<i></i>';
+ p class => 'filselect';
+ a id => 'filselect', href => '#s';
+ lit '<i>&#9656;</i> '.mt('_js_fil_filters').'<i></i>';
+ end;
end;
input type => 'hidden', class => 'hidden', name => 'fil', id => 'fil', value => $f->{fil};
end;
diff --git a/lib/VNDB/Handler/Tags.pm b/lib/VNDB/Handler/Tags.pm
index cc38cdf8..f97fe238 100644
--- a/lib/VNDB/Handler/Tags.pm
+++ b/lib/VNDB/Handler/Tags.pm
@@ -111,8 +111,10 @@ sub tagpage {
a href => "/g$t->{id}?fil=$f->{fil};m=2", $f->{m} == 2 ? (class => 'optselected') : (), mt '_spoilset_2';
end;
- a id => 'filselect', href => '#v';
- lit '<i>&#9656;</i> '.mt('_js_fil_filters').'<i></i>';
+ p class => 'filselect';
+ a id => 'filselect', href => '#v';
+ lit '<i>&#9656;</i> '.mt('_js_fil_filters').'<i></i>';
+ end;
end;
input type => 'hidden', class => 'hidden', name => 'fil', id => 'fil', value => $f->{fil};
diff --git a/lib/VNDB/Handler/Traits.pm b/lib/VNDB/Handler/Traits.pm
index 98f0b757..8d689581 100644
--- a/lib/VNDB/Handler/Traits.pm
+++ b/lib/VNDB/Handler/Traits.pm
@@ -100,8 +100,10 @@ sub traitpage {
a href => "/i$trait?m=2", $f->{m} == 2 ? (class => 'optselected') : (), mt '_spoilset_2';
end;
- a id => 'filselect', href => '#c';
- lit '<i>&#9656;</i> '.mt('_js_fil_filters').'<i></i>';
+ p class => 'filselect';
+ a id => 'filselect', href => '#c';
+ lit '<i>&#9656;</i> '.mt('_js_fil_filters').'<i></i>';
+ end;
end;
input type => 'hidden', class => 'hidden', name => 'fil', id => 'fil', value => $f->{fil};
diff --git a/lib/VNDB/Handler/VNBrowse.pm b/lib/VNDB/Handler/VNBrowse.pm
index bc8b39b2..01f5bc61 100644
--- a/lib/VNDB/Handler/VNBrowse.pm
+++ b/lib/VNDB/Handler/VNBrowse.pm
@@ -22,6 +22,7 @@ sub list {
{ get => 'q', required => 0, default => '' },
{ get => 'sq', required => 0, default => '' },
{ get => 'fil',required => 0 },
+ { get => 'rfil', required => 0, default => '' },
{ get => 'vnlist', required => 0, default => 2, enum => [ '0', '1' ] }, # 2: use pref
{ get => 'wish', required => 0, default => 2, enum => [ '0', '1' ] }, # 2: use pref
);
@@ -50,6 +51,9 @@ sub list {
$f->{s} = 'title' if $f->{fil} !~ /tag_inc-/ && $f->{s} eq 'tagscore';
$f->{o} = $f->{s} eq 'tagscore' ? 'd' : 'a' if !$f->{o};
+ my $rfil = fil_parse $f->{rfil}, @{$VNDB::Util::Misc::filfields{release}};
+ $f->{rfil} = fil_serialize $rfil, @{$VNDB::Util::Misc::filfields{release}};
+
my($list, $np) = $self->filFetchDB(vn => $f->{fil}, {
%compat,
tagspoil => $self->authPref('spoilers')||0,
@@ -57,8 +61,9 @@ sub list {
what => ' rating' .
($f->{vnlist} ? ' vnlist' : '').
($f->{wish} ? ' wishlist' : ''),
- $char ne 'all' ? ( char => $char ) : (),
- $f->{q} ? ( search => $f->{q} ) : (),
+ $char ne 'all' ? ( char => $char ) : (),
+ $f->{q} ? ( search => $f->{q} ) : (),
+ keys %$rfil ? ( release => $rfil ) : (),
results => 50,
page => $f->{p},
sort => $f->{s}, reverse => $f->{o} eq 'd',
@@ -76,7 +81,7 @@ sub list {
my $url = sub {
my($char, $toggle) = @_;
- return "/v/$char?q=$quri;fil=$f->{fil};s=$f->{s};o=$f->{o}" .
+ return "/v/$char?q=$quri;fil=$f->{fil};rfil=$f->{rfil};s=$f->{s};o=$f->{o}" .
($toggle ? ";$toggle=".($f->{$toggle}?0:1) : '');
};
@@ -95,14 +100,20 @@ sub list {
end 'p';
}
- a id => 'filselect', href => '#v';
- lit '<i>&#9656;</i> '.mt('_js_fil_filters').'<i></i>';
+ p class => 'filselect';
+ a id => 'filselect', href => '#v';
+ lit '<i>&#9656;</i> '.mt('_vnbrowse_fil_title').'<i></i>';
+ end;
+ a id => 'rfilselect', href => '#r';
+ lit '<i>&#9656;</i> '.mt('_rbrowse_fil_title').'<i></i>';
+ end;
end;
input type => 'hidden', class => 'hidden', name => 'fil', id => 'fil', value => $f->{fil};
+ input type => 'hidden', class => 'hidden', name => 'rfil', id => 'rfil', value => $f->{rfil};
end;
end 'form';
- $self->htmlBrowseVN($list, $f, $np, "/v/$char?q=$quri;fil=$f->{fil}", $f->{fil} =~ /tag_inc-/);
+ $self->htmlBrowseVN($list, $f, $np, "/v/$char?q=$quri;fil=$f->{fil};rfil=$f->{rfil}", $f->{fil} =~ /tag_inc-/);
$self->htmlFooter(pref_code => 1);
}
diff --git a/lib/VNDB/Util/Misc.pm b/lib/VNDB/Util/Misc.pm
index f05bdf72..c84b41d5 100644
--- a/lib/VNDB/Util/Misc.pm
+++ b/lib/VNDB/Util/Misc.pm
@@ -10,7 +10,7 @@ use VNDB::Func;
our @EXPORT = qw|filFetchDB bbSubstLinks|;
-my %filfields = (
+our %filfields = (
vn => [qw|length hasani hasshot tag_inc tag_exc taginc tagexc tagspoil lang olang plat ul_notblack ul_onwish ul_voted ul_onlist|],
release => [qw|type patch freeware doujin date_before date_after released minage lang olang resolution plat med voiced ani_story ani_ero|],
char => [qw|gender bloodt bust_min bust_max waist_min waist_max hip_min hip_max height_min height_max weight_min weight_max trait_inc trait_exc tagspoil role|],