summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYorhel <git@yorhel.nl>2015-08-17 16:02:40 +0200
committerYorhel <git@yorhel.nl>2015-08-17 16:02:40 +0200
commit6f424c8a6765c00c5df7b359ab4c8d409c758738 (patch)
tree1abebbf903341f50f8da90026425c8e0198d612e
parente064b8c2a70ab02417a58d2db83bb31fcb59fbcf (diff)
parentc24962392d8959eb912be14b225d2e8a08ae3f54 (diff)
Merge branch 'jscleanup'
-rw-r--r--Makefile2
-rw-r--r--README3
-rw-r--r--data/config_example.pl7
-rw-r--r--data/global.pl14
-rw-r--r--data/js/charops.js81
-rw-r--r--data/js/chartraits.js125
-rw-r--r--data/js/charvns.js195
-rw-r--r--data/js/dateselector.js84
-rw-r--r--data/js/dropdown.js91
-rw-r--r--data/js/dropdownsearch.js194
-rw-r--r--data/js/filter.js555
-rw-r--r--data/js/iv.js124
-rw-r--r--data/js/lib.js185
-rw-r--r--data/js/main.js74
-rw-r--r--data/js/misc.js316
-rw-r--r--data/js/prodrel.js108
-rw-r--r--data/js/relmedia.js68
-rw-r--r--data/js/relprod.js105
-rw-r--r--data/js/relvns.js88
-rw-r--r--data/js/staffalias.js80
-rw-r--r--data/js/tabs.js49
-rw-r--r--data/js/tagops.js65
-rw-r--r--data/js/vncast.js146
-rw-r--r--data/js/vnrel.js122
-rw-r--r--data/js/vnreldropdown.js36
-rw-r--r--data/js/vnscr.js233
-rw-r--r--data/js/vnstaff.js102
-rw-r--r--data/js/vntagmod.js163
-rw-r--r--data/script.js3381
-rw-r--r--data/style.css2
-rw-r--r--lib/VNDB/Handler/Releases.pm7
-rw-r--r--lib/VNDB/Handler/ULists.pm10
-rw-r--r--lib/VNDB/Handler/VNEdit.pm2
-rw-r--r--lib/VNDB/Handler/VNPage.pm4
-rwxr-xr-xutil/jsgen.pl187
35 files changed, 3544 insertions, 3464 deletions
diff --git a/Makefile b/Makefile
index a858183a..60023928 100644
--- a/Makefile
+++ b/Makefile
@@ -67,7 +67,7 @@ static/ch static/cv static/sf static/st:
static/f/js data/log www www/feeds www/api:
mkdir $@
-static/f/js/en.js: data/script.js data/lang.txt util/jsgen.pl data/config.pl data/global.pl
+static/f/js/en.js: data/js/*.js data/lang.txt util/jsgen.pl data/config.pl data/global.pl
util/jsgen.pl
data/icons/icons.css: data/icons/*.png data/icons/*/*.png util/spritegen.pl
diff --git a/README b/README
index 627db769..39a060a4 100644
--- a/README
+++ b/README
@@ -30,6 +30,7 @@ Requirements
Crypt::URandom
Crypt::ScryptKDF
Image::Magick
+ JSON::XS
TUWF
FCGI (optional, for running as a FastCGI script)
PerlIO::gzip (optional, for output compression)
@@ -58,7 +59,9 @@ Requirements
CSS::Minifier::XS (optional, minimizes CSS output)
util/jsgen.pl
+ JSON::XS
JavaScript::Minifier::XS (optional, minimizes JS output)
+ uglifyjs (optional, slower but better JS compression)
util/spritegen.pl
Image::Magick
diff --git a/data/config_example.pl b/data/config_example.pl
index 56ed2ec7..9c255bb2 100644
--- a/data/config_example.pl
+++ b/data/config_example.pl
@@ -23,7 +23,7 @@ package VNDB;
);
-# Uncomment to disable certain features of Multi
+# Uncomment to enable certain features of Multi
#$M{modules}{API} = {};
#$M{modules}{APIDump} = {};
@@ -35,3 +35,8 @@ package VNDB;
# pass => '<nickserv-password>',
# masters => [ 'yorhel!~Ayo@your.hell' ],
#};
+
+
+# Uncomment the compression method to use for the generated Javascript (or just leave as-is to disable compression)
+#$JSGEN{compress} = 'JavaScript::Minifier::XS';
+#$JSGEN{compress} = "|/usr/bin/uglifyjs --compress --mangle";
diff --git a/data/global.pl b/data/global.pl
index ef368d61..5cefa5ff 100644
--- a/data/global.pl
+++ b/data/global.pl
@@ -1,9 +1,6 @@
package VNDB;
-our(%O, %S, $ROOT);
-
-
# options for TUWF
our %O = (
db_login => [ 'dbi:Pg:dbname=vndb', 'vndb', 'passwd' ],
@@ -18,7 +15,8 @@ our %O = (
# VNDB-specific options (object_data)
-our %S = (%S,
+our %S;
+%S = (%S,
version => `cd $VNDB::ROOT; git describe` =~ /^(.+)$/ && $1,
url => 'http://vndb.org', # Only used by Multi, web pages infer their own address
url_static => 'http://s.vndb.org',
@@ -106,7 +104,7 @@ our %S = (%S,
voiced => [ 0..4 ],
animated => [ 0..4 ],
wishlist_status => [ 0..3 ],
- rlist_status => [ 0..4 ], # 2 = hardcoded 'OK'
+ rlist_status => [ 0..4 ], # 0 = hardcoded "unknown", 2 = hardcoded 'OK'. List must not have gaps
vnlist_status => [ 0..4 ],
blood_types => [qw| unknown o a b ab |],
genders => [qw| unknown m f b |],
@@ -136,6 +134,12 @@ our %M = (
);
+# Options for jsgen.pl
+our %JSGEN = (
+ compress => undef,
+);
+
+
# allow the settings to be overwritten in config.pl
require $ROOT.'/data/config.pl' if -f $ROOT.'/data/config.pl';
diff --git a/data/js/charops.js b/data/js/charops.js
new file mode 100644
index 00000000..7adb6ae2
--- /dev/null
+++ b/data/js/charops.js
@@ -0,0 +1,81 @@
+var spoil, sexual, t;
+
+
+// Fixes the commas between trait names and the hidden status of the entire row
+function fixrow(c) {
+ var l = byName(byName(c, 'td')[1], 'span');
+ var first = 1;
+ for(var i=0; i<l.length; i+=2)
+ if(!hasClass(l[i], 'hidden')) {
+ setClass(l[i+1], 'hidden', first);
+ first = 0;
+ }
+ setClass(c, 'hidden', first);
+}
+
+
+function restripe() {
+ for(var i=0; i<t.length; i++) {
+ var b = byName(t[i], 'tbody');
+ if(!b.length)
+ continue;
+ setClass(t[i], 'stripe', false);
+ var r = 1;
+ var rows = byName(b[0], 'tr');
+ for(var j=0; j<rows.length; j++) {
+ if(hasClass(rows[j], 'traitrow'))
+ fixrow(rows[j]);
+ if(!hasClass(rows[j], 'nostripe') && !hasClass(rows[j], 'hidden'))
+ setClass(rows[j], 'odd', r++&1);
+ }
+ }
+}
+
+
+function setall(h) {
+ var k = byClass('charspoil');
+ for(var i=0; i<k.length; i++)
+ setClass(k[i], 'hidden',
+ !sexual && hasClass(k[i], 'sexual') ? true :
+ hasClass(k[i], 'charspoil_0') ? false :
+ hasClass(k[i], 'charspoil_-1') ? spoil > 1 :
+ hasClass(k[i], 'charspoil_1') ? spoil < 1 : spoil < 2);
+ for(var i=0; i<3; i++)
+ setClass(h[i], 'sel', spoil == i);
+ if(h[3])
+ setClass(h[3], 'sel', sexual);
+ if(k.length)
+ restripe();
+ return false;
+}
+
+
+function init() {
+ var h = byName(byId('charops'), 'a');
+ t = byClass('table', 'stripe');
+
+ // Spoiler level
+ for(var i=0; i<3; i++) {
+ h[i].num = i;
+ h[i].onclick = function() {
+ spoil = this.num;
+ return setall(h);
+ };
+ if(hasClass(h[i], 'sel'))
+ spoil = i;
+ };
+
+ // Sexual toggle
+ if(h[3]) {
+ h[3].onclick = function() {
+ sexual = !sexual;
+ return setall(h);
+ };
+ sexual = hasClass(h[3], 'sel');
+ }
+ setall(h);
+}
+
+
+if(byId('charops'))
+ init();
diff --git a/data/js/chartraits.js b/data/js/chartraits.js
new file mode 100644
index 00000000..dacb31f7
--- /dev/null
+++ b/data/js/chartraits.js
@@ -0,0 +1,125 @@
+// l10n /_spoil_-?\d+/
+
+function ctrLoad() {
+ // load current traits
+ var l = byId('traits').value.split(' ');
+ var v = {}; // tag id -> spoiler lookup table
+ var q = []; // list of id=X parameters
+ for(var i=0; i<l.length; i++) {
+ if(l[i]) {
+ var m = l[i].split(/-/);
+ v[m[0]] = m[1];
+ q[i] = 'id='+m[0];
+ }
+ }
+ if(q.length > 0)
+ ajax('/xml/traits.xml?r=200;'+q.join(';'), function (ht) {
+ var t = ht.responseXML.getElementsByTagName('item');
+ for(var i=0; i<t.length; i++)
+ ctrAdd(t[i], v[t[i].getAttribute('id')]);
+ }, 1);
+ else
+ ctrEmpty();
+
+ // dropdown
+ dsInit(byId('trait_input'), '/xml/traits.xml?q=', function(item, tr) {
+ var g = item.getAttribute('groupname');
+ g = g ? g+' / ' : '';
+ tr.appendChild(tag('td', { style: 'text-align: right; padding-right: 5px'}, 'i'+item.getAttribute('id')));
+ tr.appendChild(tag('td',
+ tag('b', {'class':'grayedout'}, g), item.firstChild.nodeValue,
+ tag('b', {'class':'grayedout'}, item.getAttribute('meta')=='yes' ? mt('_js_ds_tag_meta') : '')));
+ }, ctrFormAdd);
+}
+
+function ctrEmpty() {
+ var x = byId('traits_loading');
+ var t = byId('traits_tbl');
+ if(x)
+ t.removeChild(x);
+ var l = byName(t, 'tr');
+ var e = byId('traits_empty');
+ if(e && l.length > 1)
+ t.removeChild(e);
+ else if(!e && l.length < 1)
+ t.appendChild(tag('tr', {id:'traits_empty',colspan:3}, tag('td', mt('_chare_traits_empty'))));
+}
+
+function ctrAdd(item, spoil) {
+ var id = item.getAttribute('id');
+ var name = item.firstChild.nodeValue;
+ var group = item.getAttribute('groupname');
+ var sp = tag('td', {'class':'tc_spoil', onclick:ctrSpoilNext, ctr_spoil:spoil}, mt('_spoil_'+spoil));
+ ddInit(sp, 'left', ctrSpoilDD);
+ byId('traits_tbl').appendChild(tag('tr', {ctr_id:id, ctr_spoiler:spoil},
+ tag('td', {'class':'tc_name'},
+ tag('b', {'class':'grayedout'}, group?group+' / ':''),
+ tag('a', {'href':'/i'+id}, name)),
+ sp,
+ tag('td', {'class':'tc_del'}, tag('a', {href:'#', onclick:ctrDel}, mt('_js_remove')))
+ ));
+ ctrEmpty();
+ ctrSerialize();
+}
+
+function ctrFormAdd(item) {
+ var l = byName(byId('traits_tbl'), 'tr');
+ for(var i=0; i<l.length; i++)
+ if(l[i].ctr_id && l[i].ctr_id == item.getAttribute('id'))
+ break;
+ if(i < l.length)
+ alert(mt('_chare_traits_present'));
+ else if(item.getAttribute('meta') == 'yes')
+ alert(mt('_chare_traits_nometa'));
+ else
+ ctrAdd(item, 0);
+ return '';
+}
+
+function ctrSpoilNext() {
+ if(++this.ctr_spoil > 2)
+ this.ctr_spoil = 0;
+ setText(this, mt('_spoil_'+this.ctr_spoil));
+ ddRefresh();
+ ctrSerialize();
+}
+
+function ctrSpoilDD(lnk) {
+ var lst = tag('ul', null);
+ for(var i=0; i<=2; i++)
+ lst.appendChild(tag('li', i == lnk.ctr_spoil
+ ? tag('i', mt('_spoil_'+i))
+ : tag('a', {href: '#', onclick:ctrSpoilSet, ctr_td:lnk, ctr_sp:i}, mt('_spoil_'+i))
+ ));
+ return lst;
+}
+
+function ctrSpoilSet() {
+ this.ctr_td.ctr_spoil = this.ctr_sp;
+ setText(this.ctr_td, mt('_spoil_'+this.ctr_sp));
+ ddHide();
+ ctrSerialize();
+ return false;
+}
+
+function ctrDel() {
+ var tr = this;
+ while(tr.nodeName.toLowerCase() != 'tr')
+ tr = tr.parentNode;
+ tr.parentNode.removeChild(tr);
+ ctrEmpty();
+ ctrSerialize();
+ return false
+}
+
+function ctrSerialize() {
+ var l = byName(byId('traits_tbl'), 'tr');
+ var v = [];
+ for(var i=0; i<l.length; i++)
+ if(l[i].ctr_id)
+ v.push(l[i].ctr_id+'-'+byClass(l[i], 'tc_spoil')[0].ctr_spoil);
+ byId('traits').value = v.join(' ');
+}
+
+if(byId('traits_tbl'))
+ ctrLoad();
diff --git a/data/js/charvns.js b/data/js/charvns.js
new file mode 100644
index 00000000..10fd1f77
--- /dev/null
+++ b/data/js/charvns.js
@@ -0,0 +1,195 @@
+function cvnLoad() {
+ // load current links
+ var l = byId('vns').value.split(' ');
+ var v = {}; // vid -> { rid: [ role, spoil ], .. }
+ var q = []; // list of v=X parameters
+ for(var i=0; i<l.length; i++) {
+ if(!l[i])
+ continue;
+ var m = l[i].split(/-/); // vid, rid, spoil, role
+ if(!v[m[0]]) {
+ q.push('v='+m[0]);
+ v[m[0]] = {};
+ }
+ v[m[0]][m[1]] = [ m[3], m[2] ];
+ }
+ if(q.length > 0)
+ ajax('/xml/releases.xml?'+q.join(';'), function(hr) {
+ var vns = byName(hr.responseXML, 'vn');
+ for(var i=0; i<vns.length; i++) {
+ var vid = vns[i].getAttribute('id');
+ cvnVNAdd(vns[i]);
+ var rels = byName(vns[i], 'release');
+ for(var r=0; r<rels.length; r++) {
+ var rid = rels[r].getAttribute('id');
+ if(v[vid][rid])
+ cvnRelAdd(vid, rid, v[vid][rid][0], v[vid][rid][1]);
+ }
+ if(v[vid][0])
+ cvnRelAdd(vid, 0, v[vid][0][0], v[vid][0][1]);
+ }
+ cvnEmpty();
+ }, 1);
+ else
+ cvnEmpty();
+
+ // dropdown search
+ dsInit(byId('vns_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)));
+ }, cvnFormAdd);
+}
+
+function cvnEmpty() {
+ var x = byId('vns_loading');
+ var t = byId('vns_tbl');
+ if(x)
+ t.removeChild(x);
+ var l = byName(t, 'tr');
+ var e = byId('vns_empty');
+ if(e && l.length > 1)
+ t.removeChild(e);
+ else if(!e && l.length < 1)
+ t.appendChild(tag('tr', {id:'vns_empty',colspan:3}, tag('td', mt('_chare_vns_empty'))));
+}
+
+function cvnVNAdd(vn, rel) {
+ var vid = vn.getAttribute('id');
+ var rels = byName(vn, 'release');
+ byId('vns_tbl').appendChild(tag('tr', {id:'cvn_v'+vid, cvn_vid:vid, cvn_rels:rels},
+ tag('td', {'class':'tc_vn',colspan:4}, 'v'+vid+':',
+ tag('a', {href:'/v'+vid}, vn.getAttribute('title')),
+ tag('i', '(', tag('a', {href:'#', onclick:cvnRelNew}, mt('_chare_vns_addrel')), ')')
+ )
+ ));
+ if(rel)
+ cvnRelAdd(vid, 0, 'primary', 0);
+ cvnEmpty();
+}
+
+function cvnRelAdd(vid, rid, role, spoil) {
+ var rels = byId('cvn_v'+vid).cvn_rels;
+ var rsel = tag('select', {onchange:cvnRelChange}, tag('option', {value:0}, mt('_chare_vns_other')));
+ for(var i=0; i<rels.length; i++) {
+ var id = rels[i].getAttribute('id');
+ rsel.appendChild(tag('option', {value: id, selected:id==rid},
+ '['+rels[i].getAttribute('lang')+'] '+rels[i].firstChild.nodeValue+' (r'+id+')'));
+ }
+
+ var lsel = tag('select', {onchange:cvnSerialize});
+ for(var i=0; i<VARS.char_roles.length; i++)
+ lsel.appendChild(tag('option', {value: VARS.char_roles[i][0], selected:VARS.char_roles[i][0]==role}, VARS.char_roles[i][1]));
+
+ // l10n /_spoil_\d+/
+ var ssel = tag('select', {onchange:cvnSerialize});
+ for(var i=0; i<3; i++)
+ ssel.appendChild(tag('option', {value:i, selected:i==spoil}, mt('_spoil_'+i)));
+
+ var tbl = byId('vns_tbl');
+ var l = byName(tbl, 'tr');
+ var last = null;
+ for(var i=1; i<l.length; i++)
+ if(l[i-1].cvn_vid == vid && l[i].cvn_vid != vid)
+ last = l[i-1];
+ tbl.insertBefore(tag('tr', {id:'cvn_v'+vid+'r'+rid, cvn_vid:vid, cvn_rid:rid},
+ tag('td', {'class':'tc_rel'}, rsel),
+ tag('td', {'class':'tc_rol'}, lsel),
+ tag('td', {'class':'tc_spl'}, ssel),
+ tag('td', {'class':'tc_del'}, tag('a', {href:'#', onclick:cvnRelDel}, mt('_js_remove')))
+ ), last);
+}
+
+function cvnRelChange() {
+ // look for duplicates and disallow the change
+ var val = this.options[this.selectedIndex].value;
+ var tr = this;
+ while(tr.nodeName.toLowerCase() != 'tr')
+ tr = tr.parentNode;
+ if(byId('cvn_v'+tr.cvn_vid+'r'+val)) {
+ alert(mt('_chare_vns_relexists'));
+ for(var i=0; i<this.options.length; i++)
+ this.options[i].selected = this.options[i].value == tr.cvn_rid;
+ return;
+ }
+ // otherwise, 'rename' this entry
+ tr.id = 'cvn_v'+tr.cvn_vid+'r'+val;
+ tr.cvn_rid = val;
+ cvnSerialize();
+}
+
+function cvnRelNew() {
+ var tr = this;
+ while(tr.nodeName.toLowerCase() != 'tr')
+ tr = tr.parentNode;
+ var id = 0;
+ if(byId('cvn_v'+tr.cvn_vid+'r0')) {
+ for(var i=0; i<tr.cvn_rels.length; i++) {
+ id = tr.cvn_rels[i].getAttribute('id');
+ if(!byId('cvn_v'+tr.cvn_vid+'r'+id))
+ break;
+ }
+ if(i == tr.cvn_rels.length) {
+ alert(mt('_chare_vns_allrel'));
+ return false;
+ }
+ }
+ cvnRelAdd(tr.cvn_vid, id, 'primary', 0);
+ cvnSerialize();
+ return false;
+}
+
+function cvnRelDel() {
+ var tbl = byId('vns_tbl');
+ var tr = this;
+ while(tr.nodeName.toLowerCase() != 'tr')
+ tr = tr.parentNode;
+ tbl.removeChild(tr);
+ var l = byName(tbl, 'tr');
+ var c = 0;
+ for(var i=0; i<l.length; i++)
+ if(l[i].cvn_vid == tr.cvn_vid)
+ c++;
+ if(c <= 1)
+ tbl.removeChild(byId('cvn_v'+tr.cvn_vid));
+ cvnSerialize();
+ cvnEmpty();
+ return false;
+}
+
+function cvnFormAdd(item) {
+ var inpt = byId('vns_input');
+ inpt.disabled = true;
+
+ ajax('/xml/releases.xml?v='+item.getAttribute('id'), function(hr) {
+ inpt.disabled = false;
+ inpt.value = '';
+
+ var items = byName(hr.responseXML, 'vn');
+ if(items.length < 1) // shouldn't happen
+ return alert('Oops! Error!');
+
+ var id = items[0].getAttribute('id');
+ if(byId('cvn_v'+id))
+ return alert(mt('_chare_vns_exists'));
+ cvnVNAdd(items[0], 1);
+ cvnSerialize();
+ }, 1);
+ return mt('_js_loading');
+}
+
+function cvnSerialize() {
+ var l = byName(byId('vns_tbl'), 'tr');
+ var v = [];
+ for(var i=0; i<l.length; i++)
+ if(l[i].cvn_rid != null) {
+ var rol = byName(byClass(l[i], 'tc_rol')[0], 'select')[0];
+ var spl = byName(byClass(l[i], 'tc_spl')[0], 'select')[0];
+ v.push(l[i].cvn_vid+'-'+l[i].cvn_rid+'-'+
+ spl.options[spl.selectedIndex].value+'-'+
+ rol.options[rol.selectedIndex].value);
+ }
+ byId('vns').value = v.join(' ');
+}
+
+if(byId('jt_box_chare_vns'))
+ cvnLoad();
diff --git a/data/js/dateselector.js b/data/js/dateselector.js
new file mode 100644
index 00000000..1ba12eca
--- /dev/null
+++ b/data/js/dateselector.js
@@ -0,0 +1,84 @@
+/* Date selector widget for the 'release date'-style dates, with support for
+ * TBA and unknown month or day. Usage:
+ *
+ * <input type="hidden" class="dateinput" .. />
+ *
+ * Will add a date selector to the HTML at that place, and automatically
+ * read/write the value of the hidden field. Alternative usage:
+ *
+ * var obj = dateLoad(ref, serfunc);
+ *
+ * If 'ref' is set, it will behave as above with 'ref' being the input object.
+ * Otherwise it will return the widget object. The setfunc, if set, will be
+ * called whenever the date widget is focussed or its value is changed.
+ *
+ * The object returned by dateLoad() can be used as follows:
+ * obj.date_val: Always contains the currently selected date.
+ * obj.dateSet(val): Change the selected date
+ */
+function load(obj, serfunc) {
+ var i;
+ var selops = {style: 'width: 70px', onfocus:serfunc, onchange: serialize, tabIndex: 10};
+
+ var year = tag('select', selops,
+ tag('option', {value:0}, mt('_js_date_year')),
+ tag('option', {value:9999}, 'TBA')
+ );
+ for(i=(new Date()).getFullYear()+5; i>=1980; i--)
+ year.appendChild(tag('option', {value: i}, i));
+
+ var month = tag('select', selops,
+ tag('option', {value:99}, mt('_js_date_month'))
+ );
+ for(i=1; i<=12; i++)
+ month.appendChild(tag('option', {value: i}, i));
+
+ var day = tag('select', selops,
+ tag('option', {value:99}, mt('_js_date_day'))
+ );
+ for(i=1; i<=31; i++)
+ day.appendChild(tag('option', {value: i}, i));
+
+ var div = tag('div', {
+ date_obj: obj,
+ date_serfunc: serfunc,
+ date_val: obj ? obj.value : 0
+ }, year, month, day);
+ div.dateSet = function(v){ set(div, v) };
+
+ set(div, div.date_val);
+ return obj ? obj.parentNode.insertBefore(div, obj) : div;
+}
+
+function set(div, val) {
+ val = +val || 0;
+ val = [ Math.floor(val/10000), Math.floor(val/100)%100, val%100 ];
+ if(val[1] == 0) val[1] = 99;
+ if(val[2] == 0) val[2] = 99;
+ var l = byName(div, 'select');
+ for(var i=0; i<l.length; i++)
+ for(var j=0; j<l[i].options.length; j++)
+ l[i].options[j].selected = l[i].options[j].value == val[i];
+ serialize(div, true);
+}
+
+function serialize(div, nonotify) {
+ div = div.dateSet ? 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_val = val[0] == 0 ? 0 : val[0] == 9999 ? 99999999 : val[0]*10000+val[1]*100+(val[1]==99?99:val[2]);
+ if(div.date_obj)
+ div.date_obj.value = div.date_val;
+ if(!nonotify && div.date_serfunc)
+ div.date_serfunc(div);
+}
+
+var l = byClass('input', 'dateinput');
+for(var i=0; i<l.length; i++)
+ load(l[i]);
+
+window.dateLoad = load;
diff --git a/data/js/dropdown.js b/data/js/dropdown.js
new file mode 100644
index 00000000..76be3f35
--- /dev/null
+++ b/data/js/dropdown.js
@@ -0,0 +1,91 @@
+/* Dropdown widget, used as follows:
+ *
+ * ddInit(obj, align, func);
+ *
+ * Show a dropdown box on mouse-over on 'obj'. 'func' should generate and
+ * return the contents of the box as a DOM node, or null to not show a dropdown
+ * box at all. The 'align' argument indicates where the box should be shown,
+ * relative to the obj:
+ *
+ * left: To the left of obj
+ * bottom: To the bottom of obj
+ * tagmod: Special alignment for tagmod page
+ *
+ * Other functions:
+ *
+ * ddHide(); Hides the box
+ * ddRefresh(); Refreshes the box contents
+ */
+var box;
+
+function init(obj, align, contents) {
+ obj.dd_align = align;
+ obj.dd_contents = contents;
+ obj.onmouseover = show;
+}
+
+function show() {
+ if(!box) {
+ box = tag('div', {id:'dd_box', 'class':'hidden'});
+ addBody(box);
+ }
+ box.dd_lnk = this;
+ document.onmousemove = mouse;
+ document.onscroll = hide;
+ refresh();
+}
+
+function hide() {
+ if(box) {
+ setText(box, '');
+ setClass(box, 'hidden', true);
+ box.dd_lnk = document.onmousemove = document.onscroll = null;
+ }
+}
+
+function mouse(e) {
+ e = e || window.event;
+ // Don't hide if the cursor is on the link
+ for(var lnk = e.target || e.srcElement; lnk; lnk=lnk.parentNode)
+ if(lnk == box.dd_lnk)
+ return;
+
+ // Hide if it's 10px outside of the box
+ 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 < box.dd_x-10 || mouseX > box.dd_x+box.offsetWidth+10 || mouseY < box.dd_y-10 || mouseY > box.dd_y+box.offsetHeight+10)
+ hide();
+}
+
+function refresh() {
+ if(!box || !box.dd_lnk)
+ return hide();
+ var lnk = box.dd_lnk;
+ var content = lnk.dd_contents(lnk, box);
+ if(content == null)
+ return hide();
+ setContent(box, content);
+ setClass(box, 'hidden', false);
+
+ 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.dd_x = ddx;
+ box.dd_y = ddy;
+ box.style.left = ddx+'px';
+ box.style.top = ddy+'px';
+}
+
+window.ddInit = init;
+window.ddHide = hide;
+window.ddRefresh = refresh;
diff --git a/data/js/dropdownsearch.js b/data/js/dropdownsearch.js
new file mode 100644
index 00000000..35bd1443
--- /dev/null
+++ b/data/js/dropdownsearch.js
@@ -0,0 +1,194 @@
+/* Interactive drop-down search widget. Usage:
+ *
+ * dsInit(obj, url, trfunc, serfunc, retfunc);
+ *
+ * obj: An <input type="text"> object.
+ *
+ * url: The base URL of the XML API, e.g. "/xml/tags.xml?q=", the search query is appended to this URL.
+ * The resource at the URL should return an XML document with a
+ * <item id="something" ..>..</item>
+ * element for each result.
+ *
+ * trfunc(item, tr): Function that is given an <item> object given by the XML
+ * document and an empty <tr> object. The function should format the data of
+ * the item to be shown in the tr.
+ *
+ * serfunc(item, obj): Called whenever a user selects an item from the search
+ * results. Should return a string, which will be used as the new value of the
+ * input object.
+ *
+ * retfunc(obj): Called whenever the user selects an item from the search
+ * results (after setfunc()) or when enter is pressed (even if nothing is
+ * selected).
+ *
+ * setfunc and retfunc can be null.
+ *
+ * TODO: Some users of this widget consider serfunc() as their final "apply
+ * this selection" function, whereas others use retfunc() for this. Might be
+ * worth investigating whether the additional flexibility offered by
+ * retfunc() is actually necessary, and remove the callback if not.
+ */
+var boxobj;
+
+function box() {
+ if(!boxobj) {
+ boxobj = tag('div', {id: 'ds_box', 'class':'hidden'}, tag('b', mt('_js_loading')));
+ addBody(boxobj);
+ }
+ return boxobj;
+}
+
+function init(obj, url, trfunc, serfunc, retfunc) {
+ obj.setAttribute('autocomplete', 'off');
+ obj.onkeydown = keydown;
+ obj.onblur = blur;
+ obj.ds_returnFunc = retfunc;
+ obj.ds_trFunc = trfunc;
+ obj.ds_serFunc = serfunc;
+ obj.ds_searchURL = url;
+ obj.ds_selectedId = 0;
+ obj.ds_dosearch = null;
+}
+
+function blur() {
+ setTimeout(function () {
+ setClass(box(), 'hidden', true)
+ }, 500)
+}
+
+function setselected(obj, id) {
+ obj.ds_selectedId = id;
+ var l = byName(box(), 'tr');
+ for(var i=0; i<l.length; i++)
+ setClass(l[i], 'selected', id && l[i].id == 'ds_box_'+id);
+}
+
+function setvalue(obj) {
+ 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(obj);
+
+ setClass(box(), 'hidden', true);
+ setContent(box(), tag('b', mt('_js_loading')));
+ setselected(obj, 0);
+ if(obj.ds_dosearch) {
+ clearTimeout(obj.ds_dosearch);
+ obj.ds_dosearch = null;
+ }
+}
+
+function enter(obj) {
+ // Make sure the form doesn't submit when enter is pressed.
+ // This solution is a hack, but it's simple and reliable.
+ 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);
+ }
+
+ setvalue(obj);
+ return false;
+}
+
+function updown(obj, up) {
+ var i, sel, l = byName(box(), 'tr');
+ if(l.length < 1)
+ return true;
+
+ if(obj.ds_selectedId == 0)
+ sel = up ? l.length-1 : 0;
+ else
+ for(i=0; i<l.length; i++)
+ if(l[i].id == 'ds_box_'+obj.ds_selectedId)
+ sel = up ? (i>0 ? i-1 : l.length-1) : (l[i+1] ? i+1 : 0);
+
+ setselected(obj, l[sel].id.substr(7));
+ return false;
+}
+
+function keydown(ev) {
+ var c = document.layers ? ev.which : document.all ? event.keyCode : ev.keyCode;
+ var obj = this;
+
+ if(c == 9) // tab
+ return true;
+
+ if(c == 13) // enter
+ return enter(obj);
+
+ if(c == 38 || c == 40) // up / down
+ return updown(obj, c == 38);
+
+ // perform search after a timeout
+ if(obj.ds_dosearch)
+ clearTimeout(obj.ds_dosearch);
+ obj.ds_dosearch = setTimeout(function() {
+ search(obj);
+ }, 500);
+
+ return true;
+}
+
+function search(obj) {
+ var b = box();
+ var val = obj.value;
+
+ clearTimeout(obj.ds_dosearch);
+ obj.ds_dosearch = null;
+
+ // hide the ds_box div if the search string is too short
+ if(val.length < 2) {
+ setClass(b, 'hidden', true);
+ setContent(b, tag('b', mt('_js_loading')));
+ setselected(obj, 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);
+
+ b.style.position = 'absolute';
+ b.style.left = ddx+'px';
+ b.style.top = ddy+'px';
+ b.style.width = obj.offsetWidth+'px';
+ setClass(b, 'hidden', false);
+
+ // perform search
+ ajax(obj.ds_searchURL + encodeURIComponent(val), function(hr) { results(hr, obj) });
+}
+
+function results(hr, obj) {
+ var lst = hr.responseXML.getElementsByTagName('item');
+ var b = box();
+ if(lst.length < 1) {
+ setContent(b, tag('b', mt('_js_ds_noresults')));
+ setselected(obj, 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]} );
+
+ tr.onmouseover = function() { setselected(obj, this.id.substr(7)) };
+ tr.onmousedown = function() { setselected(obj, this.id.substr(7)); setvalue(obj) };
+
+ obj.ds_trFunc(lst[i], tr);
+ tb.appendChild(tr);
+ }
+ setContent(b, tag('table', tb));
+ setselected(obj, obj.ds_selectedId != 0 && !byId('ds_box_'+obj.ds_selectedId) ? 0 : obj.ds_selectedId);
+}
+
+window.dsInit = init;
diff --git a/data/js/filter.js b/data/js/filter.js
new file mode 100644
index 00000000..b1c7d581
--- /dev/null
+++ b/data/js/filter.js
@@ -0,0 +1,555 @@
+/* Filter box definition:
+ * [ <title>,
+ * [ <category_name>,
+ * [ <fieldcode>, <fieldname>, <fieldcontents>, <fieldreadfunc>, <fieldwritefunc> ], ..
+ * ], ..
+ * ]
+ * Where:
+ * <title> human-readable title of the filter box
+ * <category_name> human-readable name of the category. ignored if there's only one category
+ * <fieldcode> code of this field, refers to the <field> in the filter format. Empty string for just a <tr>
+ * <fieldname> human-readanle name of the field. Empty to not display a label. Space for always-enabled items (without checkbox)
+ * <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
+ *
+ * Filter string format:
+ * <field>-<value1>~<value2>.<field2>-<value>.<field3>-<value1>~<value2>
+ * Where:
+ * <field> = [a-z0-9]+
+ * <value> = [a-zA-Z0-9_]+ and any UTF-8 characters not in the ASCII range
+ * Escaping of the <value>:
+ * "_<two-number-code>"
+ * Where <two-number-code> is the decimal index to the following array:
+ * _ <space> ! " # $ % & ' ( ) * + , - . / : ; < = > ? @ [ \ ] ^ ` { | } ~
+ * 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 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]);
+ p.appendChild(a);
+ p.appendChild(tag(' '));
+
+ // category contents
+ var t = tag('table', {'class':'formtable', fil_num: idx}, null);
+ setClass(t, 'hidden', true);
+ 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),
+ fd[1] ? tag('td', {'class':'label'},
+ tag('label', {'for':'fil_check_'+fd[0]}, lab),
+ typeof fd[1] == 'object' ? tag('b', fd[1][1]) : null
+ ) : null,
+ 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);
+
+ fil_cats[idx] = a;
+ }
+
+ addBody(tag('div', { id: 'fil_div', 'class':'hidden' },
+ tag('a', {href:'#', onclick:filShow, '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');
+ 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();
+}
+
+function filSaveDefault() {
+ var but = this;
+ var note = byId('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) {
+ 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);
+ }
+ 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
+}
+
+function filSelectField(obj) {
+ var t = obj && obj.parentNode ? obj : this;
+ setClass(byId('fil_savenote'), 'hidden', true);
+ // update checkbox and label
+ var o = t;
+ while(o.nodeName.toLowerCase() != 'tr')
+ o = o.parentNode;
+ var c = byId('fil_check_'+o.fil_code);
+ if(c != t)
+ c.checked = true;
+ if(hasClass(c, 'hidden'))
+ c.checked = true;
+ setClass(byName(o, 'label')[0], 'active', c.checked);
+
+ // update category link
+ while(o.nodeName.toLowerCase() != 'table')
+ o = o.parentNode;
+ var l = byName(o, 'input');
+ 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)
+ n++;
+ setClass(fil_cats[o.fil_num], 'active', n>0);
+
+ // serialize
+ filSerialize();
+ return true;
+}
+
+function filSerialize() {
+ var num = 0;
+ var values = {};
+ for(var f in fil_cats[0]) {
+ if(!byId('fil_check_'+f).checked)
+ continue;
+ if(!hasClass(byId('fil_check_'+f), '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('~');
+ }
+ 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+')' : '');
+}
+
+function filDeSerialize() {
+ var d = byId('fil').value;
+ var fs = d.split('.');
+ var f = new Object;
+ for(var i=0; i<fs.length; i++) {
+ var v = fs[i].split('-');
+ if(fil_cats[0][v[0]])
+ f[v[0]] = v[1];
+ }
+ for(var fn in fil_cats[0])
+ if(!f[fn])
+ f[fn] = '';
+ for(var fn in f) {
+ var c = byId('fil_check_'+fn);
+ if(!c)
+ continue;
+ c.checked = f[fn] == '' ? false : true;
+ 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
+ // batched after all fields have been updated, and in some cases the
+ // writefunc() triggers the same filSelectField() as well
+ filSelectField(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;
+ 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';
+
+ return false;
+}
+
+var curSlider = null;
+function filFSlider(c, n, min, max, def, unit) {
+ var bw = 200; var pw = 1; // slidebar width and pointer width
+ var s = tag('p', {fil_val:def, 'class':'slider'});
+ var b = tag('div', {style:'width:'+(bw-2)+'px;', s:s});
+ var p = tag('div', {style:'width:'+pw+'px;', s:s});
+ var v = tag('span', def+' '+unit);
+ s.appendChild(b);
+ b.appendChild(p);
+ s.appendChild(v);
+
+ var set = function (e, v) {
+ var w = bw-pw-6;
+ var s,x;
+
+ if(v) {
+ s = e;
+ x = v[0] == '' ? def : parseInt(v[0]);
+ x = (x-min)*w/(max-min);
+ } else {
+ s = curSlider;
+ if(!e) e = window.event;
+ x = (!e) ? (def-min)*w/(max-min)
+ : (e.pageX || e.clientX + document.body.scrollLeft - document.body.clientLeft)-5;
+ var o = s.childNodes[0];
+ while(o.offsetParent) {
+ x -= o.offsetLeft;
+ o = o.offsetParent;
+ }
+ }
+
+ if(x<0) x = 0; if(x>w) x = w;
+ s.fil_val = min + Math.floor(x*(max-min)/w);
+ s.childNodes[1].innerHTML = s.fil_val+' '+unit;
+ s.childNodes[0].childNodes[0].style.left = x+'px';
+ return false;
+ }
+
+ b.onmousedown = p.onmousedown = function (e) {
+ curSlider = this.s;
+ if(!curSlider.oldmousemove) curSlider.oldmousemove = document.onmousemove;
+ if(!curSlider.oldmouseup) curSlider.oldmouseup = document.onmouseup;
+ document.onmouseup = function () {
+ document.onmousemove = curSlider.oldmousemove;
+ curSlider.oldmousemove = null;
+ document.onmouseup = curSlider.oldmouseup;
+ curSlider.oldmouseup = null;
+ filSelectField(curSlider);
+ return false;
+ }
+ document.onmousemove = set;
+ return set(e);
+ }
+
+ return [c, n, s, function (c) { return [ c.fil_val ]; }, set ];
+}
+
+function filFSelect(c, n, lines, opts) {
+ var s = tag('select', {onfocus: filSelectField, onchange: filSerialize, 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]));
+ else {
+ var g = tag('optgroup', {label: opts[i][0]});
+ for(var j=1; j<opts[i].length; j++)
+ g.appendChild(tag('option', {name: opts[i][j][0]}, opts[i][j][1]));
+ s.appendChild(g);
+ }
+ }
+ return [ c, lines > 1 ? [ n, mt('_js_fil_boolor') ] : n, s,
+ function (c) {
+ var l = [];
+ for(var i=0; i<c.options.length; i++)
+ if(c.options[i].selected)
+ l.push(c.options[i].name);
+ return l;
+ },
+ function (c, f) {
+ for(var i=0; i<c.options.length; i++) {
+ for(var j=0; j<f.length; j++)
+ if(c.options[i].name+'' == f[j]+'') // beware of JS logic: 0 == '', but '0' != ''
+ break;
+ c.options[i].selected = j != f.length;
+ }
+ }
+ ];
+}
+
+function filFOptions(c, n, opts) {
+ var p = tag('p', {'class':'opts', fil_val:opts[0][0]});
+ var sel = function (e) {
+ var o = typeof e == 'string' ? e : this.fil_n;
+ var l = byName(p, 'a');
+ for(var i=0; i<l.length; i++)
+ setClass(l[i], 'tsel', l[i].fil_n+'' == o+'');
+ p.fil_val = o;
+ if(typeof e != 'string')
+ filSelectField(p);
+ return false
+ };
+ for(var i=0; i<opts.length; i++) {
+ p.appendChild(tag('a', {href:'#', fil_n: opts[i][0], onclick:sel}, opts[i][1]));
+ if(i<opts.length-1)
+ p.appendChild(tag('b', '|'));
+ }
+ return [ c, n, p,
+ function (c) { return [ c.fil_val ] },
+ function (c, v) { sel(v[0]) }
+ ];
+}
+
+function filFTagInput(name, label, type) {
+ var src = type=='tag' ? '/xml/tags.xml' : '/xml/traits.xml';
+
+ var visible = false;
+ var addtag = function(ul, id, name, group) {
+ ul.appendChild(
+ tag('li', { fil_id: id },
+ type=='trait' && group ? tag('b', {'class':'grayedout'}, group+' / ') : null,
+ type=='tag' ? tag('a', {href:'/g'+id}, name||'g'+id) : tag('a', {href:'/i'+id}, name||'i'+id),
+ ' (', tag('a', {href:'#',
+ onclick:function () {
+ // a -> li -> ul -> div
+ var ul = this.parentNode.parentNode;
+ ul.removeChild(this.parentNode);
+ filSelectField(ul.parentNode);
+ return false
+ }
+ }, mt('_js_remove')), ')'
+ ));
+ }
+ var fetch = function(c) {
+ var v = c.fil_val;
+ var ul = byName(c, 'ul')[0];
+ var txt = byName(c, 'input')[0];
+ if(v == null)
+ return;
+ if(!v[0]) {
+ setText(ul, '');
+ txt.disabled = false;
+ txt.value = '';
+ return;
+ }
+ if(!visible)
+ setText(ul, '');
+ var q = [];
+ for(var i=0; i<v.length; i++) {
+ q.push('id='+v[i]);
+ if(!visible)
+ addtag(ul, v[i]);
+ }
+ txt.value = mt('_js_loading');
+ txt.disabled = true;
+ if(visible)
+ ajax(src+'?'+q.join(';'), function (hr) {
+ var items = hr.responseXML.getElementsByTagName('item');
+ setText(ul, '');
+ for(var i=0; i<items.length; i++)
+ addtag(ul, items[i].getAttribute('id'), items[i].firstChild.nodeValue, items[i].getAttribute('groupname'));
+ txt.value = '';
+ txt.disabled = false;
+ c.fil_val = null;
+ }, 1);
+ };
+ var input = tag('input', {type:'text', 'class':'text', style:'width:300px', onfocus:filSelectField});
+ var list = tag('ul', null);
+ dsInit(input, src+'?q=',
+ function(item, tr) {
+ var g = item.getAttribute('groupname');
+ tr.appendChild(tag('td',
+ type=='trait' && g ? tag('b', {'class':'grayedout'}, g+' / ') : null,
+ shorten(item.firstChild.nodeValue, 40), // l10n /_js_ds_(tag|trait)_(meta|mod)/
+ item.getAttribute('meta') == 'yes' ? tag('b', {'class': 'grayedout'}, ' '+mt('_js_ds_'+type+'_meta')) : null,
+ item.getAttribute('state') == 0 ? tag('b', {'class': 'grayedout'}, ' '+mt('_js_ds_'+type+'_mod')) : null
+ ));
+ },
+ function(item, obj) {
+ if(item.getAttribute('meta') == 'yes') // l10n /_js_ds_(tag|trait)_nometa/
+ alert(mt('_js_ds_'+type+'_nometa'));
+ else {
+ addtag(byName(obj.parentNode, 'ul')[0], item.getAttribute('id'), item.firstChild.nodeValue, item.getAttribute('groupname'));
+ filSelectField(obj);
+ }
+ return '';
+ },
+ function(o) { filSelectField(o) }
+ );
+
+ return [
+ name, label, tag('div', list, input),
+ function(c) {
+ var v = []; var l = byName(c, 'li');
+ for(var i=0; i<l.length; i++)
+ v.push(l[i].fil_id);
+ return v;
+ },
+ function(c,v) { c.fil_val = v; fetch(c) },
+ function(c) { visible = true; fetch(c); }
+ ];
+}
+
+function filChars() {
+ var ontraitpage = location.pathname.indexOf('/c/') < 0;
+
+ return [
+ mt('_charb_fil_title'),
+ [ mt('_charb_general'),
+ filFSelect('gender', mt('_charb_gender'), 4, VARS.genders),
+ filFSelect('bloodt', mt('_charb_bloodt'), 5, VARS.blood_types),
+ '',
+ filFSlider('bust_min', mt('_charb_bust_min'), 20, 120, 40, 'cm'),
+ filFSlider('bust_max', mt('_charb_bust_max'), 20, 120, 100, 'cm'),
+ filFSlider('waist_min', mt('_charb_waist_min'), 20, 120, 40, 'cm'),
+ filFSlider('waist_max', mt('_charb_waist_max'), 20, 120, 100, 'cm'),
+ filFSlider('hip_min', mt('_charb_hip_min'), 20, 120, 40, 'cm'),
+ filFSlider('hip_max', mt('_charb_hip_max'), 20, 120, 100, 'cm'),
+ '',
+ filFSlider('height_min', mt('_charb_height_min'), 0, 300, 60, 'cm'),
+ filFSlider('height_max', mt('_charb_height_max'), 0, 300, 240, 'cm'),
+ filFSlider('weight_min', mt('_charb_weight_min'), 0, 400, 80, 'kg'),
+ filFSlider('weight_max', mt('_charb_weight_max'), 0, 400, 320, 'kg'),
+ ],
+ ontraitpage ? [ mt('_charb_traits'),
+ [ '', ' ', tag(mt('_charb_traitnothere')) ],
+ ] : [ mt('_charb_traits'),
+ [ '', ' ', tag(mt('_js_fil_booland')) ],
+ filFTagInput('trait_inc', mt('_charb_traitinc'), 'trait'),
+ filFTagInput('trait_exc', mt('_charb_traitexc'), 'trait'),
+ filFOptions('tagspoil', ' ', [[0, mt('_spoilset_0')],[1, mt('_spoilset_1')],[2, mt('_spoilset_2')]]),
+ ],
+ [ mt('_charb_roles'), filFSelect('role', mt('_charb_roles'), 4, VARS.char_roles) ]
+ ];
+}
+
+function filReleases() {
+ var plat = VARS.platforms;
+ plat.splice(0, 0, [ 'unk', mt('_unknown') ]);
+ var med = VARS.media;
+ med.splice(0, 0, [ 'unk', mt('_unknown') ]);
+ return [
+ mt('_rbrowse_fil_title'),
+ [ mt('_rbrowse_general'),
+ filFOptions('type', mt('_rbrowse_type'), VARS.release_types),
+ 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) } ],
+ 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) ],
+ [ 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) ],
+ [ mt('_rbrowse_voiced'), filFSelect('voiced', mt('_rbrowse_voiced'), 5, VARS.voiced) ],
+ [ mt('_rbrowse_animation'),
+ filFSelect('ani_story', mt('_rbrowse_ani_story'), 5, VARS.animated),
+ filFSelect('ani_ero', mt('_rbrowse_ani_ero'), 5, VARS.animated)
+ ]
+ ];
+}
+
+function filVN() {
+ var ontagpage = location.pathname.indexOf('/v/') < 0;
+
+ return [
+ mt('_vnbrowse_fil_title'),
+ [ mt('_vnbrowse_general'),
+ filFSelect( 'length', mt('_vnbrowse_length'), 6, VARS.vn_lengths),
+ filFOptions('hasani', mt('_vnbrowse_anime'), [[1, mt('_vnbrowse_anime_yes')],[0, mt('_vnbrowse_anime_no')]])
+ ],
+ ontagpage ? [ mt('_vnbrowse_tags'),
+ [ '', ' ', tag(mt('_vnbrowse_tagnothere')) ],
+ ] : [ mt('_vnbrowse_tags'),
+ [ '', ' ', tag(mt('_js_fil_booland')) ],
+ [ '', ' ', byId('pref_code') ? tag(mt('_vnbrowse_tagactive')) : null ],
+ filFTagInput('tag_inc', mt('_vnbrowse_taginc'), 'tag'),
+ filFTagInput('tag_exc', mt('_vnbrowse_tagexc'), 'tag'),
+ filFOptions('tagspoil', ' ', [[0, mt('_spoilset_0')],[1, mt('_spoilset_1')],[2, mt('_spoilset_2')]])
+ ],
+ [ mt('_vnbrowse_language'), filFSelect('lang', mt('_vnbrowse_language'), 20, VARS.languages) ],
+ [ mt('_vnbrowse_olang'), filFSelect('olang',mt('_vnbrowse_olang'), 20, VARS.languages) ],
+ [ mt('_vnbrowse_platform'), filFSelect('plat', mt('_vnbrowse_platform'), 20, VARS.platforms) ],
+ !byId('pref_code') ? null : [
+ mt('_vnbrowse_ul'),
+ filFOptions('ul_notblack', mt('_vnbrowse_ul_notblack'), [[1, mt('_vnbrowse_ul_notblackmsg')]]),
+ filFOptions('ul_onwish', mt('_vnbrowse_ul_onwish'), [[0, mt('_vnbrowse_ul_onwishno')],[1, mt('_vnbrowse_ul_onwishyes')]]),
+ filFOptions('ul_voted', mt('_vnbrowse_ul_voted'), [[0, mt('_vnbrowse_ul_votedno')], [1, mt('_vnbrowse_ul_votedyes') ]]),
+ filFOptions('ul_onlist', mt('_vnbrowse_ul_onlist'), [[0, mt('_vnbrowse_ul_onlistno')],[1, mt('_vnbrowse_ul_onlistyes')]])
+ ],
+ ];
+}
+
+function filStaff() {
+ var gend = [
+ ['unknown', mt('_gender_unknown')],
+ ['m', mt('_gender_m')],
+ ['f', mt('_gender_f')],
+ ];
+
+ // Insert seiyuu into the list of roles, before the "staff" role.
+ var roles = VARS.staff_roles;
+ for(var i=0; i<roles.length; i++)
+ if(roles[i][0] == 'staff') {
+ roles.splice(i, 0, ['seiyuu', mt('_credit_seiyuu')]);
+ break;
+ }
+
+ return [
+ mt('_sbrowse_fil_title'),
+ [ mt('_sbrowse_general'),
+ filFOptions('truename', mt('_sbrowse_names'), [[1, mt('_sbrowse_names_primary')],[0, mt('_sbrowse_names_all')]]),
+ filFSelect('role', mt('_sbrowse_roles'), roles.length, roles),
+ '',
+ filFSelect('gender', mt('_sbrowse_gender'), gend.length, gend),
+ ]
+ ];
+}
+
+if(byId('filselect'))
+ filLoad();
diff --git a/data/js/iv.js b/data/js/iv.js
new file mode 100644
index 00000000..75754a90
--- /dev/null
+++ b/data/js/iv.js
@@ -0,0 +1,124 @@
+/* Simple image viewer widget. Usage:
+ *
+ * <a href="full_image.jpg" rel="iv:{width}x{height}:{category}">..</a>
+ *
+ * Clicking on the above link will cause the image viewer to open
+ * full_image.jpg. The {category} part can be empty or absent. If it is not
+ * empty, next/previous links will show up to point to the other images within
+ * the same category.
+ *
+ * ivInit() should be called when links with "iv:" tags are dynamically added
+ * or removed from the DOM.
+ */
+
+// Cache of image categories and the list of associated link objects. Used to
+// quickly generate the next/prev links.
+var cats;
+
+function init() {
+ cats = {};
+ var n = 0;
+ var l = byName('a');
+ for(var i=0;i<l.length;i++) {
+ var o = l[i];
+ if(o.rel.substr(0,3) == 'iv:' && o.id != 'ivprev' && o.id != 'ivnext') {
+ n++;
+ o.onclick = show;
+ var cat = o.rel.split(':')[2];
+ if(cat) {
+ if(!cats[cat])
+ cats[cat] = [];
+ o.iv_i = cats[cat].length;
+ cats[cat].push(o);
+ }
+ }
+ }
+
+ if(n && !byId('iv_view')) {
+ addBody(tag('div', {id: 'iv_view','class':'hidden'},
+ tag('b', {id:'ivimg'}, ''),
+ tag('br', null),
+ tag('a', {href:'#', id:'ivfull'}, ''),
+ tag('a', {href:'#', onclick: close, id:'ivclose'}, mt('_js_close')),
+ tag('a', {href:'#', onclick: show, id:'ivprev'}, '« '+mt('_js_iv_prev')),
+ tag('a', {href:'#', onclick: show, id:'ivnext'}, mt('_js_iv_next')+' »')
+ ));
+ addBody(tag('b', {id:'ivimgload','class':'hidden'}, mt('_js_loading')));
+ }
+}
+
+// Find the next (dir=1) or previous (dir=-1) non-hidden link object for the category.
+function findnav(cat, i, dir) {
+ for(var j=i+dir; j>=0 && j<cats[cat].length; j+=dir)
+ if(!hasClass(cats[cat][j], 'hidden'))
+ return cats[cat][j];
+ return 0
+}
+
+// fix properties of the prev/next links
+function fixnav(lnk, cat, i, dir) {
+ var a = cat ? findnav(cat, i, dir) : 0;
+ lnk.style.visibility = a ? 'visible' : 'hidden';
+ lnk.href = a ? a.href : '#';
+ lnk.rel = a ? a.rel : '';
+ lnk.iv_i = a ? a.iv_i : 0;
+}
+
+function show() {
+ var u = this.href;
+ var opt = this.rel.split(':');
+ var idx = this.iv_i;
+ var view = byId('iv_view');
+ var full = byId('ivfull');
+
+ fixnav(byId('ivprev'), opt[2], idx, -1);
+ fixnav(byId('ivnext'), opt[2], idx, 1);
+
+ // 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
+ setClass(view, 'hidden', false);
+ setContent(byId('ivimg'), tag('img', {src:u, onclick:close,
+ onload: function() { setClass(byId('ivimgload'), 'hidden', true); },
+ 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';
+ setClass(byId('ivimgload'), 'hidden', false);
+ return false;
+}
+
+function close() {
+ setClass(byId('iv_view'), 'hidden', true);
+ setClass(byId('ivimgload'), 'hidden', true);
+ setText(byId('ivimg'), '');
+ return false;
+}
+
+window.ivInit = init;
+init();
diff --git a/data/js/lib.js b/data/js/lib.js
new file mode 100644
index 00000000..72925a93
--- /dev/null
+++ b/data/js/lib.js
@@ -0,0 +1,185 @@
+window.expanded_icon = '▾',
+window.collapsed_icon = '▸';
+
+
+var ajax_req;
+window.ajax = function(url, func, async) {
+ if(!async && ajax_req)
+ ajax_req.abort();
+ var req = window.ActiveXObject ? new ActiveXObject('Microsoft.XMLHTTP') : new XMLHttpRequest();
+ if(!async)
+ ajax_req = req;
+ req.onreadystatechange = function() {
+ if(!req || req.readyState != 4 || !req.responseText)
+ return;
+ if(req.status != 200)
+ return alert('Whoops, error! :(');
+ func(req);
+ };
+ url += (url.indexOf('?')>=0 ? ';' : '?')+(Math.floor(Math.random()*999)+1);
+ req.open('GET', url, true);
+ req.send(null);
+};
+
+
+window.setCookie = function(n,v) {
+ var date = new Date();
+ date.setTime(date.getTime()+(365*24*60*60*1000));
+ document.cookie = VARS.cookie_prefix+n+'='+v+'; expires='+date.toGMTString()+'; path=/';
+};
+
+window.getCookie = function(n) {
+ var l = document.cookie.split(';');
+ n = VARS.cookie_prefix+n;
+ 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;
+};
+
+
+window.byId = function(n) {
+ return document.getElementById(n)
+};
+
+window.byName = function(){
+ var d = arguments.length > 1 ? arguments[0] : document;
+ var n = arguments.length > 1 ? arguments[1] : arguments[0];
+ return d.getElementsByTagName(n);
+};
+
+window.byClass = function() { // [class], [parent, class], [tagname, class], [parent, tagname, class]
+ var par = typeof arguments[0] == 'object' ? arguments[0] : document;
+ var t = arguments.length == 2 && typeof arguments[0] == 'string' ? arguments[0] : arguments.length == 3 ? arguments[1] : '*';
+ var c = arguments[arguments.length-1];
+ var l = byName(par, t);
+ 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 */
+window.tag = function() {
+ 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;
+};
+
+
+window.addBody = function(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);
+};
+
+window.setContent = function() {
+ setText(arguments[0], '');
+ for(var i=1; i<arguments.length; i++)
+ if(arguments[i] != null)
+ arguments[0].appendChild(tag(arguments[i]));
+};
+
+window.getText = function(obj) {
+ return obj.textContent || obj.innerText || '';
+};
+
+window.setText = function(obj, txt) {
+ if(obj.textContent != null)
+ obj.textContent = txt;
+ else
+ obj.innerText = txt;
+};
+
+
+window.listClass = function(obj) {
+ var n = obj.className;
+ if(!n)
+ return [];
+ return n.split(/ /);
+};
+
+window.hasClass = function(obj, c) {
+ var l = listClass(obj);
+ for(var i=0; i<l.length; i++)
+ if(l[i] == c)
+ return true;
+ return false;
+};
+
+window.setClass = function(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(' ');
+};
+
+window.onSubmit = function(form, handler) {
+ var prev_handler = form.onsubmit;
+ form.onsubmit = function(e) {
+ if(prev_handler)
+ if(!prev_handler(e))
+ return false;
+ return handler(e);
+ }
+};
+
+
+window.shorten = function(v, l) {
+ return v.length > l ? v.substr(0, l-3)+'...' : v;
+};
+
+
+/* maketext function. Less powerful than the Perl equivalent, as it only
+ * supports [_n], ~[ and ~]. jsgen.pl is responsible for weeding out the other
+ * codes. */
+window.mt = function() {
+ var key = arguments[0];
+ var val = VARS.l10n_str[key] || key;
+ // BUG: ~[_1] will get replaced. We don't have a string like that, so whatever.
+ for(var i=1; i<arguments.length; i++)
+ val = val.replace(new RegExp('\[_'+i+'\]', 'g'), arguments[i]);
+ return val.replace(/~\[/g, '[').replace(/~\]/g, ']');
+};
+
+
+window.jsonParse = function(s) {
+ return s ? JSON.parse(s) : '';
+};
diff --git a/data/js/main.js b/data/js/main.js
new file mode 100644
index 00000000..dc1d3b27
--- /dev/null
+++ b/data/js/main.js
@@ -0,0 +1,74 @@
+/* This is the main Javascript file. This file is processed by util/jsgen.pl to
+ * generate the final JS file(s) used by the site.
+ *
+ *
+ * Internationalization note:
+ *
+ * The translation keys to be inserted in the generates JS file are parsed
+ * from the source code. So when using mt(), make sure it is in the following
+ * format:
+ * mt('<exact translation key>',<more arguments>
+ * or
+ * mt('<exact translation key>')
+ * The single quotes and (lack of) spaces are significant!
+ *
+ * To use non-exact translation keys as argument to mt(), make sure to
+ * indicate which keys should be inserted in the header by adding a comment
+ * containing the following format:
+ * l10n /<perl regex>/
+ * any keys matching that regex will be included.
+ *
+ * In the case of an mt('<key>') without any extra arguments, the entire
+ * function call may be replaced by the TL string.
+ */
+
+// Variables from jsgen.pl
+VARS = /*VARS*/;
+
+/* The include directives below automatically wrap the file contents inside an
+ * anonymous function, so each file has its own local namespace. Included files
+ * can't access variables or functions from other files, unless these variables
+ * are explicitely shared in DOM objects or (more commonly) the global 'window'
+ * object.
+ */
+
+// Reusable library functions
+//include lib.js
+
+// Reusable widgets
+//include iv.js
+//include dropdown.js
+//include dateselector.js
+//include dropdownsearch.js
+//include tabs.js
+
+// Page/functionality-specific widgets
+//include vnreldropdown.js
+//include tagops.js
+//include charops.js
+//include filter.js
+//include misc.js
+
+// VN editing (/v+/edit)
+//include vnrel.js
+//include vnscr.js
+//include vnstaff.js
+//include vncast.js
+
+// VN tag editing (/v+/tagmod)
+//include vntagmod.js
+
+// Release editing (/r+/edit)
+//include relmedia.js
+//include relvns.js
+//include relprod.js
+
+// Producer editing (/p+/edit)
+//include prodrel.js
+
+// Character editing (/c+/edit)
+//include chartraits.js
+//include charvns.js
+
+// Staff editing (/s+/edit)
+//include staffalias.js
diff --git a/data/js/misc.js b/data/js/misc.js
new file mode 100644
index 00000000..89572e82
--- /dev/null
+++ b/data/js/misc.js
@@ -0,0 +1,316 @@
+// search box
+byId('sq').onfocus = function () {
+ if(this.value == mt('_menu_emptysearch')) {
+ this.value = '';
+ this.style.fontStyle = 'normal'
+ }
+};
+byId('sq').onblur = function () {
+ if(this.value.length < 1) {
+ this.value = mt('_menu_emptysearch');
+ this.style.fontStyle = 'italic'
+ }
+};
+
+
+function ulist_redirect(type, path, formcode, args) {
+ var r = new RegExp('/('+type+'[0-9]+).*$');
+ location.href = location.href.replace(r, '/$1')+path
+ +'?formcode='+formcode
+ +';ref='+encodeURIComponent(location.pathname+location.search)
+ +';'+args;
+}
+
+
+// VN Voting (/v+)
+if(byId('votesel'))
+ byId('votesel').onchange = function() {
+ var s = this.options[this.selectedIndex].value;
+ if(s == -2)
+ s = prompt(mt('_vnpage_uopt_othervote'), '');
+ if(!s || s == -3)
+ return;
+ if(s != -1 && (!s.match(/^([1-9]|10)([\.,][0-9])?$/) || s > 10 || s < 1)) {
+ alert(mt('_vnpage_uopt_invvote'));
+ this.selectedIndex = 0;
+ return;
+ }
+ s = s.replace(',', '.');
+ if(s == 1 && !confirm(mt('_vnpage_uopt_1vote')))
+ return;
+ if(s == 10 && !confirm(mt('_vnpage_uopt_10vote')))
+ return;
+ if(s > 0 || s == -1)
+ ulist_redirect('v', '/vote', this.name, 'v='+s);
+ };
+
+
+// VN Wishlist dropdown box (/v+)
+if(byId('wishsel'))
+ byId('wishsel').onchange = function() {
+ if(this.selectedIndex != 0)
+ ulist_redirect('v', '/wish', this.name, ';s='+this.options[this.selectedIndex].value);
+ };
+
+
+// Release & VN list dropdown box (/r+ and /v+)
+if(byId('listsel'))
+ byId('listsel').onchange = function() {
+ if(this.selectedIndex != 0)
+ ulist_redirect('[rv]', '/list', this.name, 'e='+this.options[this.selectedIndex].value);
+ };
+
+
+// NSFW VN image toggle (/v+)
+(function() {
+ var msg = byId('nsfw_show');
+ if(msg) {
+ var img = byId('nsfw_hid');
+ byName(msg, 'a')[0].onclick = function() {
+ setClass(msg, 'hidden', true);
+ setClass(img, 'hidden', false);
+ return false;
+ };
+ img.onclick = function() {
+ setClass(msg, 'hidden', false);
+ setClass(img, 'hidden', true);
+ };
+ }
+})();
+
+
+// NSFW toggle for screenshots (/v+)
+if(byId('nsfwhide'))
+ byId('nsfwhide').onclick = function() {
+ var shown = 0;
+ var l = byClass(byId('screenshots'), 'a', 'scrlnk');
+ for(var i=0; i<l.length; i++) {
+ if(hasClass(l[i], 'nsfw')) {
+ var hidden = !hasClass(l[i], 'hidden');
+ setClass(l[i], 'hidden', hidden);
+ if(!hidden)
+ shown++;
+ } else
+ shown++;
+ }
+ setText(byId('nsfwshown'), shown);
+ return false;
+ };
+
+
+// Notification list onclick
+(function(){
+ var d = byId('notifies');
+ if(!d)
+ return;
+ var l = byClass(d, 'td', 'clickable');
+ for(var i=0; i<l.length; i++)
+ l[i].onclick = function() {
+ var baseurl = location.href.replace(/\/u([0-9]+)\/notifies.*$/, '/u$1/notify/');
+ location.href = baseurl + this.id.replace(/notify_/, '');
+ };
+})();
+
+
+// BBCode spoiler tags
+(function(){
+ 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)
+(function(){
+ function sync() {
+ byId('doujin').disabled =
+ byId('resolution').disabled =
+ byId('voiced').disabled =
+ byId('ani_story').disabled =
+ byId('ani_ero').disabled =
+ byId('patch').checked;
+ };
+ if(byId('jt_box_rel_geninfo')) {
+ sync();
+ byId('patch').onclick = sync;
+ }
+})();
+
+
+// Batch edit dropdown box (/u+/wish and /u+/votes)
+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();
+ };
+
+
+// collapse/expand row groups (/u+/list)
+(function(){
+ var table = byId('expandall');
+ if(!table)
+ return;
+ while(table.nodeName.toLowerCase() != 'table')
+ table = table.parentNode;
+ var heads = byClass(table, 'td', 'collapse_but');
+ var allhid = false;
+
+ function sethid(l, h, hid) {
+ var i;
+ for(i=0; i<l.length; i++) {
+ setClass(l[i], 'hidden', hid);
+ // Set the hidden class on the input checkbox, if it exists. This
+ // prevents the "select all" functionality from selecting it if the row
+ // is not visible.
+ var sel = byName(l[i], 'input')[0];
+ if(sel)
+ setClass(sel, 'hidden', hid);
+ }
+ for(i=0; i<h.length; i++)
+ setText(h[i], allhid ? collapsed_icon : expanded_icon);
+ }
+
+ function alltoggle() {
+ allhid = !allhid;
+ setText(byId('expandall'), allhid ? collapsed_icon : expanded_icon);
+ sethid(byClass(table, 'tr', 'collapse'), heads, allhid);
+ return false;
+ }
+
+ function singletoggle() {
+ var l = byClass(table, 'tr', 'collapse_'+this.id);
+ sethid(l, [this], !hasClass(l[0], 'hidden'));
+ }
+
+ byId('expandall').onclick = alltoggle;
+ for(var i=0; i<heads.length; i++)
+ heads[i].onclick = singletoggle;
+ alltoggle();
+})();
+
+
+// mouse-over price information / disclaimer
+(function(){
+ if(byId('buynow')) {
+ var l = byClass(byId('buynow'), 'acronym', 'pricenote');
+ for(var i=0; i<l.length; i++) {
+ l[i].buynow_last = l[i].title;
+ l[i].title = null;
+ ddInit(l[i], 'bottom', function(acr) {
+ return tag('p', {onmouseover:ddHide, style:'padding: 3px'},
+ acr.buynow_last, tag('br', null),
+ '* The displayed price only serves as an indication and',
+ tag('br', null), 'usually excludes shipping. Actual price may differ.'
+ );
+ });
+ }
+ }
+})();
+
+
+// set note input box (/u+/list)
+if(byId('not') && byId('vns'))
+ byId('vns').onchange = function () {
+ if(this.options[this.selectedIndex].value == 999)
+ byId('not').value = prompt(mt('_rlist_setnote_prompt'), '');
+ return true;
+ };
+
+
+// expand/collapse release listing (/p+)
+(function(){
+ var lnk = byId('expandprodrel');
+ if(!lnk)
+ return;
+ function setexpand() {
+ var exp = !(getCookie('prodrelexpand') == 1);
+ setText(lnk, exp ? mt('_js_collapse') : mt('_js_expand'));
+ setClass(byId('prodrel'), 'collapse', !exp);
+ };
+ lnk.onclick = function () {
+ setCookie('prodrelexpand', getCookie('prodrelexpand') == 1 ? 0 : 1);
+ setexpand();
+ return false;
+ };
+ setexpand();
+})();
+
+
+// Language selector
+(function(){
+ var d = byId('lang_select');
+ var flag = byName(d, 'acronym')[0];
+ ddInit(d, 'bottom', function(lnk) {
+ var lst = tag('ul', null);
+ for(var i=0; i<VARS.l10n_lang.length; i++) {
+ var ln = VARS.l10n_lang[i];
+ var icon = tag('acronym', {'class':'icons lang '+ln[0]}, ' ');
+ lst.appendChild(tag('li', {'class':'lang_selector'}, hasClass(flag, ln[0])
+ ? tag('i', icon, ln[1])
+ : tag('a', {href:'/setlang?lang='+ln[0]+';ref='+encodeURIComponent(location.pathname+location.search)}, icon, ln[1])
+ ));
+ }
+ return lst;
+ });
+ d.onclick = function() {return false};
+})();
+
+
+// "check all" checkbox
+(function(){
+ function set() {
+ var l = byName('input');
+ for(var i=0; i<l.length; i++)
+ if(l[i].type == this.type && l[i].name == this.name && !hasClass(l[i], 'hidden'))
+ l[i].checked = this.checked;
+ }
+ var l = byClass('input', 'checkall');
+ for(var i=0; i<l.length; i++)
+ if(l[i].type == 'checkbox')
+ l[i].onclick = set;
+})();
+
+
+// search tabs
+(function(){
+ function click() {
+ var str = byId('q').value;
+ if(str.length > 1) {
+ this.href = this.href.split('?')[0];
+ if(this.href.indexOf('/g') >= 0 || this.href.indexOf('/i') >= 0)
+ this.href += '/list';
+ this.href += '?q=' + encodeURIComponent(str);
+ }
+ return true;
+ };
+ if(byId('searchtabs')) {
+ var l = byName(byId('searchtabs'), 'a');
+ for(var i=0; i<l.length; i++)
+ l[i].onclick = click;
+ }
+})();
+
+
+// spam protection on all forms
+setTimeout(function() {
+ for(var i=1; i<document.forms.length; i++)
+ document.forms[i].action = document.forms[i].action.replace(/\/nospam\?/,'');
+}, 500);
diff --git a/data/js/prodrel.js b/data/js/prodrel.js
new file mode 100644
index 00000000..2ddc100e
--- /dev/null
+++ b/data/js/prodrel.js
@@ -0,0 +1,108 @@
+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('_js_remove')))
+ ));
+
+ 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(mt('_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('_js_add'));
+
+ 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();
diff --git a/data/js/relmedia.js b/data/js/relmedia.js
new file mode 100644
index 00000000..3b1a4f4a
--- /dev/null
+++ b/data/js/relmedia.js
@@ -0,0 +1,68 @@
+function medLoad() {
+ // 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<VARS.media.length; i++)
+ msel.appendChild(tag('option', {value:VARS.media[i][0], selected: med==VARS.media[i][0]}, VARS.media[i][1]));
+
+ byId('media_div').appendChild(tag('span', qsel, msel,
+ med != '' ? tag('input', {type: 'button', 'class':'submit', onclick:medDel, value:mt('_js_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(VARS.media[med.selectedIndex][2] && !qty.selectedIndex)
+ qty.selectedIndex = 1;
+ if(!VARS.media[med.selectedIndex][2] && qty.selectedIndex)
+ qty.selectedIndex = 0;
+
+ r[r.length] = VARS.media[med.selectedIndex][0] + ' ' + qty.selectedIndex;
+ }
+ byId('media').value = r.join(',');
+}
+
+if(byId('jt_box_rel_format'))
+ medLoad();
diff --git a/data/js/relprod.js b/data/js/relprod.js
new file mode 100644
index 00000000..bb23ce28
--- /dev/null
+++ b/data/js/relprod.js
@@ -0,0 +1,105 @@
+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('_js_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('_js_add'));
+
+ 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();
diff --git a/data/js/relvns.js b/data/js/relvns.js
new file mode 100644
index 00000000..91883849
--- /dev/null
+++ b/data/js/relvns.js
@@ -0,0 +1,88 @@
+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('_js_remove')))
+ ));
+ rvnEmpty();
+}
+
+function rvnDel() {
+ var tr = this;
+ while(tr.nodeName.toLowerCase() != 'tr')
+ tr = tr.parentNode;
+ tr.parentNode.removeChild(tr);
+ rvnEmpty();
+ rvnSerialize();
+ 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 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('_js_add'));
+
+ 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();
diff --git a/data/js/staffalias.js b/data/js/staffalias.js
new file mode 100644
index 00000000..825f4d72
--- /dev/null
+++ b/data/js/staffalias.js
@@ -0,0 +1,80 @@
+function salLoad () {
+ byId('alias_tbl').appendChild(tag('tr', {id:'alias_new'},
+ tag('td', null),
+ tag('td', {colspan:3}, tag('a', {href:'#', onclick:salFormAdd}, mt('_staffe_aliases_add')))));
+
+ salAdd(byId('primary').value||0, byId('name').value, byId('original').value);
+ var aliases = jsonParse(byId('aliases').value) || [];
+ for(var i = 0; i < aliases.length; i++) {
+ salAdd(aliases[i].aid, aliases[i].name, aliases[i].orig);
+ }
+
+ byName(byId('maincontent'), 'form')[0].onsubmit = salSerialize;
+}
+
+function salAdd(aid, name, original) {
+ var tbl = byId('alias_tbl');
+ var first = tbl.rows.length <= 1;
+ tbl.insertBefore(tag('tr', first ? {id:'primary_name'} : null,
+ tag('td', {'class':'tc_id' },
+ tag('input', {type:'radio', name:'primary_id', value:aid, checked:first, onchange:salPrimary})),
+ tag('td', {'class':'tc_name' }, tag('input', {type:'text', 'class':'text', value:name})),
+ tag('td', {'class':'tc_original' }, tag('input', {type:'text', 'class':'text', value:original})),
+ tag('td', {'class':'tc_add' }, !first ?
+ tag('a', {href:'#', onclick:salDel}, mt('_js_remove')) : null)
+ ), byId('alias_new'));
+}
+
+function salPrimary() {
+ var prev = byId('primary_name')
+ prev.removeAttribute('id');
+ byClass(prev, 'td', 'tc_add')[0].appendChild(tag('a', {href:'#', onclick:salDel}, mt('_js_remove')));
+ var tr = this;
+ while (tr && tr.nodeName.toLowerCase() != 'tr')
+ tr = tr.parentNode;
+ tr.setAttribute('id', 'primary_name');
+ var td = byClass(tr, 'td', 'tc_add')[0];
+ while (td.firstChild)
+ td.removeChild(td.firstChild);
+
+ return salSerialize();
+}
+
+function salSerialize() {
+ var tbl = byName(byId('alias_tbl'), 'tr');
+ var a = [];
+ for (var i = 0; i < tbl.length; ++i) {
+ if(tbl[i].id == 'alias_new')
+ continue;
+ var id = byName(byClass(tbl[i], 'td', 'tc_id')[0], 'input')[0].value;
+ var name = byName(byClass(tbl[i], 'td', 'tc_name')[0], 'input')[0].value;
+ var orig = byName(byClass(tbl[i], 'td', 'tc_original')[0], 'input')[0].value;
+ if(tbl[i].id == 'primary_name') {
+ byId('name').value = name;
+ byId('original').value = orig;
+ byId('primary').value = id;
+ } else
+ a.push({ aid:Number(id), name:name, orig:orig });
+ }
+ byId('aliases').value = JSON.stringify(a);
+ return true;
+}
+
+function salDel() {
+ var tr = this;
+ while (tr && tr.nodeName.toLowerCase() != 'tr')
+ tr = tr.parentNode;
+ var tbl = byId('alias_tbl');
+ tbl.removeChild(tr);
+ salSerialize();
+ return false;
+}
+
+function salFormAdd() {
+ salAdd(0, '', '');
+ byName(byClass(byId('alias_new').previousSibling, 'td', 'tc_name')[0], 'input')[0].focus();
+ return false;
+}
+
+if(byId('jt_box_staffe_geninfo'))
+ salLoad();
diff --git a/data/js/tabs.js b/data/js/tabs.js
new file mode 100644
index 00000000..470bd077
--- /dev/null
+++ b/data/js/tabs.js
@@ -0,0 +1,49 @@
+/* Javascript tabs. General usage:
+ *
+ * <ul id="jt_select">
+ * <li><a href="#<name>" id="jt_sel_<name>">..</a></li>
+ * ..
+ * </ul>
+ *
+ * Can then be used to show/hide the following box:
+ *
+ * <div id="jt_box_<name>"> .. </div>
+ *
+ * The name of the active box will be set to and (at page load) read from
+ * location.hash. The parent node of the active link will get the 'tabselected'
+ * class. A link with the special name "all" will display all boxes associated
+ * with jt_select links.
+ *
+ * Only one jt_select list-of-tabs can be used on a single page.
+ */
+var links = byId('jt_select') ? byName(byId('jt_select'), 'a') : [];
+
+function init() {
+ var sel;
+ var first;
+ for(var i=0; i<links.length; i++) {
+ links[i].onclick = function() { set(this.id); return false };
+ if(!first)
+ first = links[i].id;
+ if(location.hash && links[i].id == 'jt_sel_'+location.hash.substr(1))
+ sel = links[i].id;
+ }
+ if(first)
+ set(sel||first, 1);
+}
+
+function set(which, nolink) {
+ which = which.substr(7);
+
+ for(var i=0; i<links.length; i++) {
+ var name = links[i].id.substr(7);
+ if(name != 'all')
+ setClass(byId('jt_box_'+name), 'hidden', which != 'all' && which != name);
+ setClass(links[i].parentNode, 'tabselected', name == which);
+ }
+
+ if(!nolink)
+ location.href = '#'+which;
+}
+
+init();
diff --git a/data/js/tagops.js b/data/js/tagops.js
new file mode 100644
index 00000000..3f789ee6
--- /dev/null
+++ b/data/js/tagops.js
@@ -0,0 +1,65 @@
+var l, lim, spoil = 0, cats = {};
+
+
+function init() {
+ var i;
+ l = byName(byId('tagops'), 'a');
+
+ // Categories
+ for(i=0; i<3; i++) {
+ l[i].tagops_cat = l[i].href.substr(l[i].href.indexOf('#')+1);
+ l[i].onclick = function() { cats[this.tagops_cat] = !cats[this.tagops_cat]; return set(); };
+ cats[l[i].tagops_cat] = hasClass(l[i], 'tsel');
+ }
+
+ // Spoiler level
+ for(i=3; i<6; i++) {
+ l[i].tagops_spoil = i-3;
+ l[i].onclick = function() { spoil = this.tagops_spoil; return set(); };
+ if(hasClass(l[i], 'tsel'))
+ spoil = i-3;
+ }
+
+ // Summary / all
+ for(i=6; i<8; i++) {
+ l[i].tagops_lim = i == 6;
+ l[i].onclick = function() { lim = this.tagops_lim; return set(); };
+ if(hasClass(l[i], 'tsel'))
+ lim = i == 6;
+ }
+
+ set();
+}
+
+
+function set() {
+ var i;
+
+ // Set link selection class
+ for(i=0; i<8; i++)
+ setClass(l[i], 'tsel',
+ i < 3 ? cats[l[i].tagops_cat] :
+ i < 6 ? l[i].tagops_spoil == spoil
+ : l[i].tagops_lim == lim);
+
+ // update tag visibility
+ var t = byName(byId('vntags'), 'span');
+ var n = 0;
+ for(i=0; i<t.length; i++) {
+ var v = n < (lim ? 15 : 999);
+ for(var j=0; j<3; j++)
+ if(hasClass(t[i], 'tagspl'+j))
+ v = v && j <= spoil;
+ for(var c in cats)
+ if(hasClass(t[i], 'cat_'+c))
+ v = v && cats[c];
+ setClass(t[i], 'hidden', !v);
+ n += v?1:0;
+ }
+
+ return false;
+}
+
+
+if(byId('tagops'))
+ init();
diff --git a/data/js/vncast.js b/data/js/vncast.js
new file mode 100644
index 00000000..0acb09c2
--- /dev/null
+++ b/data/js/vncast.js
@@ -0,0 +1,146 @@
+var vncImportData = [];
+
+function vncLoad() {
+ var cast = jsonParse(byId('seiyuu').value) || [];
+ var copt = byId('cast_chars').options;
+ var chars = {};
+ for(var i = 0; i < copt.length; i++) {
+ if(copt[i].value)
+ chars[copt[i].value] = copt[i].text;
+ }
+ cast.sort(function(a, b) {
+ if(chars[a.cid] < chars[b.cid]) return -1;
+ if(chars[a.cid] > chars[b.cid]) return 1;
+ return 0;
+ });
+ for(var i = 0; i < cast.length; i++) {
+ var aid = cast[i].aid;
+ if(vnsStaffData[aid]) // vnsStaffData is filled by vnsLoad()
+ vncAdd(vnsStaffData[aid], cast[i].cid, cast[i].note);
+ }
+ vncEmpty();
+
+ var cast_import = byId('cast_import');
+ if(cast_import) {
+ vncImportData = jsonParse(getText(byId('castimpdata')||{})) || [];
+ if(vncImportData.length)
+ byName(cast_import, 'a')[0].onclick = vncImport;
+ else
+ cast_import.style.display = 'none';
+ }
+ onSubmit(byName(byId('maincontent'), 'form')[0], vncSerialize);
+
+ // dropdown search
+ dsInit(byId('cast_input'), '/xml/staff.xml?q=', function(item, tr) {
+ tr.appendChild(tag('td', { style: 'text-align: right; padding-right: 5px'}, 's'+item.getAttribute('id')));
+ tr.appendChild(tag('td', item.firstChild.nodeValue));
+ }, vncFormAdd);
+}
+
+function vncImport() {
+ if (!vncImportData.length)
+ return false;
+ var c = {};
+ for (var i = 0; i < vncImportData.length; i++) {
+ var s = vncImportData[i];
+ c[s.cid] = s;
+ }
+ // exclude already credited cast from import list
+ var l = byName(byId('cast_tbl'), 'tr');
+ for (var i = 0; i < l.length; i++) {
+ if(l[i].id == 'cast_tr_none')
+ continue;
+ var role = byName(byClass(l[i], 'tc_char')[0], 'select')[0].value;
+ if (role in c)
+ delete c[role];
+ }
+ for (var cid in c) {
+ if (!c.hasOwnProperty(cid))
+ continue;
+ vncAdd({ id:c[cid].sid, aid:c[cid].aid, name:c[cid].name }, cid, '');
+ }
+ return false;
+}
+
+function vncAdd(seiyuu, chr, note) {
+ var tbl = byId('cast_tbl');
+
+ var csel = byId('cast_chars').cloneNode(true);
+ csel.removeAttribute('id');
+ csel.value = chr;
+
+ tbl.appendChild(tag('tr', {id:'vnc_a'+seiyuu.aid},
+ tag('td', {'class':'tc_char'}, csel),
+ tag('td', {'class':'tc_name'},
+ tag('input', {type:'hidden', value:seiyuu.aid}),
+ tag('a', {href:'/s'+seiyuu.id}, seiyuu.name)),
+ tag('td', {'class':'tc_note'}, tag('input', {type:'text', 'class':'text', value:note})),
+ tag('td', {'class':'tc_del'}, tag('a', {href:'#', onclick:vncDel}, mt('_js_remove')))
+ ));
+ vncEmpty();
+ vncSerialize();
+}
+
+function vncFormAdd(item) {
+ var chr = byId('cast_chars').value;
+ if (chr) {
+ var s = { id:item.getAttribute('id'), aid:item.getAttribute('aid'), name:item.firstChild.nodeValue };
+ vncAdd(s, chr, '');
+ } else
+ alert(mt('_vnedit_cast_nochar'));
+ return '';
+}
+
+function vncEmpty() {
+ var x = byId('cast_loading');
+ var tbody = byId('cast_tbl');
+ var tbl = tbody.parentNode;
+ var thead = byName(tbl, 'thead');
+ if(x)
+ tbody.removeChild(x);
+ if(byName(tbody, 'tr').length < 1) {
+ tbody.appendChild(tag('tr', {id:'cast_tr_none'},
+ tag('td', {colspan:4}, mt('_vnstaffe_none'))));
+ if (thead.length)
+ tbl.removeChild(thead[0]);
+ } else {
+ if(byId('cast_tr_none'))
+ tbody.removeChild(byId('cast_tr_none'));
+ if (thead.length < 1) {
+ thead = tag('thead', tag('tr',
+ tag('td', {'class':'tc_char'}, mt('_vnedit_cast_char')),
+ tag('td', {'class':'tc_name'}, mt('_vnedit_cast_seiyuu')),
+ tag('td', {'class':'tc_note'}, mt('_vnedit_cast_note')),
+ tag('td', '')));
+ tbl.insertBefore(thead, tbody);
+ }
+ }
+}
+
+function vncSerialize() {
+ var l = byName(byId('cast_tbl'), 'tr');
+ var c = [];
+ for (var i = 0; i < l.length; i++) {
+ if(l[i].id == 'cast_tr_none')
+ continue;
+ var aid = byName(byClass(l[i], 'tc_name')[0], 'input')[0];
+ var role = byName(byClass(l[i], 'tc_char')[0], 'select')[0];
+ var note = byName(byClass(l[i], 'tc_note')[0], 'input')[0];
+ c.push({ aid:Number(aid.value), cid:Number(role.value), note:note.value });
+ }
+ byId('seiyuu').value = JSON.stringify(c);
+ return true;
+}
+
+function vncDel() {
+ var tr = this;
+ while (tr.nodeName.toLowerCase() != 'tr')
+ tr = tr.parentNode;
+ byId('cast_tbl').removeChild(tr);
+ vncEmpty();
+ vncSerialize();
+ return false;
+}
+
+if(byId('jt_box_vn_cast'))
+ vncLoad();
diff --git a/data/js/vnrel.js b/data/js/vnrel.js
new file mode 100644
index 00000000..1a206e79
--- /dev/null
+++ b/data/js/vnrel.js
@@ -0,0 +1,122 @@
+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(',', 4);
+ vnrAdd(rel[0], rel[1], rel[2]==1?true:false, rel[3]);
+ }
+ 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, official, 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')+' ',
+ tag('input', {type: 'checkbox', onclick:vnrSerialize, id:'official_'+vid, checked:official}),
+ tag('label', {'for':'official_'+vid}, mt('_vnedit_rel_official')),
+ 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('_js_remove')))
+ ));
+
+ 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
+ byName(byClass(trs[i], 'td', 'tc_rel')[0], 'input')[0].checked ? '1' : '0', // official
+ 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 off = byName(byClass(relnew, 'td', 'tc_rel')[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(mt('_vnedit_rel_findformat'));
+ return false;
+ }
+
+ txt.disabled = sel.disabled = off.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 = off.disabled = false;
+ txt.value = '';
+ setText(lnk, mt('_js_add'));
+
+ 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, off.checked, items[0].firstChild.nodeValue);
+ sel.selectedIndex = 0;
+ vnrSerialize();
+ });
+ return false;
+}
+
+if(byId('vnrelations'))
+ vnrLoad();
diff --git a/data/js/vnreldropdown.js b/data/js/vnreldropdown.js
new file mode 100644
index 00000000..e7cb267a
--- /dev/null
+++ b/data/js/vnreldropdown.js
@@ -0,0 +1,36 @@
+function dropdown(lnk) {
+ var relid = lnk.id.substr(6);
+ var st = getText(lnk);
+ if(st == mt('_js_loading'))
+ return null;
+
+ var o = tag('ul', null);
+ for(var i=0; i<VARS.rlist_status.length; i++) {
+ var val = VARS.rlist_status[i];
+ o.appendChild(tag('li', st == val
+ ? tag('i', val)
+ : tag('a', {href:'#', rl_rid:relid, rl_act:i, onclick:change}, val)));
+ }
+ if(st != '--')
+ o.appendChild(tag('li', tag('a', {href:'#', rl_rid:relid, rl_act:-1, onclick:change}, mt('_vnpage_uopt_reldel'))));
+
+ return tag('div', o);
+}
+
+function change() {
+ var lnk = byId('rlsel_'+this.rl_rid);
+ var code = getText(byId('vnrlist_code'));
+ var act = this.rl_act;
+ ddHide();
+ setContent(lnk, tag('b', {'class': 'grayedout'}, mt('_js_loading')));
+ ajax('/xml/rlist.xml?formcode='+code+';id='+this.rl_rid+';e='+act, function(hr) {
+ setText(lnk, act == -1 ? '--' : VARS.rlist_status[act]);
+ });
+ return false;
+}
+
+if(byId('vnrlist_code')) {
+ var l = byClass('a', 'vnrlsel');
+ for(var i=0; i<l.length; i++)
+ ddInit(l[i], 'left', dropdown);
+}
diff --git a/data/js/vnscr.js b/data/js/vnscr.js
new file mode 100644
index 00000000..c62eb982
--- /dev/null
+++ b/data/js/vnscr.js
@@ -0,0 +1,233 @@
+var scrRel = [ [ 0, mt('_vnedit_scr_selrel') ] ];
+var scrStaticURL;
+var scrUplNr = 0;
+var scrDefRel;
+
+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);
+ if(scrRel.length <= 2)
+ scrRel.shift();
+ scrDefRel = scrRel[0][0];
+
+ // load the current screenshots
+ var scr = byId('screenshots').value.split(' ');
+ var siz = byId('screensizes').value.split(' ');
+ for(i=0; i<scr.length && scr[i].length>1; i++) {
+ var r = scr[i].split(',');
+ var s = siz[i].split(',');
+ scrSet(scrAdd(r[0], r[1], r[2]), s[0], s[1]);
+ }
+
+ ivInit();
+ scrLast();
+ scrSetSubmit();
+}
+
+function scrSetSubmit() {
+ var frm = byId('screenshots');
+ while(frm.nodeName.toLowerCase() != 'form')
+ frm = frm.parentNode;
+ onSubmit(frm, function() {
+ var loading = 0;
+ var norelease = 0;
+ var l = byName(byId('scr_table'), 'tr');
+ for(var i=0; i<l.length-1; i++) {
+ var rel = byName(l[i], 'select')[0];
+ if(l[i].scr_status > 0)
+ loading = 1;
+ else if(rel.options[rel.selectedIndex].value == 0)
+ norelease = 1;
+ }
+ if(loading) {
+ alert(mt('_vnedit_scr_frmloading'));
+ return false;
+ } else if(norelease) {
+ alert(mt('_vnedit_scr_frmnorel'));
+ return false;
+ }
+ return true;
+ });
+}
+
+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
+
+ var tr = tag('tr', { id:'scr_tr_'+id, scr_id: id, scr_status: 1, scr_rel: rel, scr_nsfw: nsfw},
+ tag('td', { 'class': 'thumb'}, mt('_js_loading')),
+ tag('td',
+ tag('b', mt('_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);
+ return tr;
+}
+
+function scrSet(tr, width, height) {
+ var dim = width+'x'+height;
+ tr.scr_status = 0;
+
+ // image
+ 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')})
+ )
+ );
+
+ // check full resolution with the list of DB-defined resolutions
+ var odd = true;
+ if(dim == '256x384') // special-case NDS resolution (not in the DB)
+ odd = false;
+ for(var j=0; j<VARS.resolutions.length && odd; j++) {
+ if(typeof VARS.resolutions[j][1] != 'object') {
+ if(VARS.resolutions[j][0] == dim)
+ odd = false;
+ } else {
+ for(var k=1; k<VARS.resolutions[j].length; k++)
+ if(VARS.resolutions[j][k][1] == dim)
+ odd = false;
+ }
+ }
+
+ // 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('_js_remove')), ')',
+ tag('br', null),
+ mt('_vnedit_scr_fullsize', dim),
+ odd ? tag('b', {'class':'standout', 'style':'font-weight: bold'}, ' '+mt('_vnedit_scr_nonstandard')) : null,
+ 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
+ );
+}
+
+function scrLast() {
+ if(byId('scr_last'))
+ byId('scr_table').removeChild(byId('scr_last'));
+ var full = byName(byId('scr_table'), 'tr').length >= 10;
+
+ var rel = tag('select', {onchange: function(){scrDefRel=this.options[this.selectedIndex].value}, 'class':'scr_relsel', 'id':'scradd_relsel'});
+ for(var j=0; j<scrRel.length; j++)
+ rel.appendChild(tag('option', {value: scrRel[j][0], selected: scrDefRel == scrRel[j][0]}, scrRel[j][1]));
+
+ 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),
+ rel,
+ 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})
+ )
+ ));
+}
+
+function scrDel(what) {
+ var tr = what && what.scr_status != null ? what : this;
+ while(tr.nodeName.toLowerCase() != 'tr')
+ tr = tr.parentNode;
+ tr.scr_status = null;
+ if(tr.scr_upl && byId(tr.scr_upl))
+ byId(tr.scr_upl).parentNode.removeChild(byId(tr.scr_upl));
+ byId('scr_table').removeChild(tr);
+ scrSerialize();
+ scrLast();
+ ivInit();
+ return false;
+}
+
+function scrUpload() {
+ scrUplNr++;
+
+ // create temporary form
+ var ifid = 'scr_upl_'+scrUplNr;
+ var frm = tag('form', {method: 'post', action:'/xml/screenshots.xml?upload='+scrUplNr,
+ 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
+ var upl = byId('scr_upload');
+ upl.id = upl.name = 'scr_upl_file_'+scrUplNr;
+ frm.appendChild(upl);
+ frm.submit();
+ ifr.scr_tr = scrAdd(0, 0, 0);
+ ifr.scr_upl = ifid;
+ ifr.scr_tr.scr_rel = byId('scradd_relsel').options[byId('scradd_relsel').selectedIndex].value;
+ 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 == 1) {
+ try {
+ tr.scr_id = fr.window.document.getElementsByTagName('image')[0].getAttribute('id');
+ } catch(e) {
+ tr.scr_id = -10;
+ }
+ if(tr.scr_id < 0) {
+ alert(tr.scr_id == -10 ? mt('_vnedit_scr_oops') :
+ tr.scr_id == -1 ? mt('_vnedit_scr_errformat') : mt('_vnedit_scr_errempty'));
+ scrDel(tr);
+ } else {
+ tr.id = 'scr_tr_'+tr.scr_id;
+ scrSet(tr, fr.window.document.getElementsByTagName('image')[0].getAttribute('width'), fr.window.document.getElementsByTagName('image')[0].getAttribute('height'));
+ scrSerialize();
+ ivInit();
+ }
+ }
+
+ tr.scr_upl = null;
+ /* remove the <div> in a timeout, otherwise some browsers think the page is still loading */
+ setTimeout(function() { ifr.parentNode.parentNode.removeChild(ifr.parentNode) }, 1000);
+}
+
+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') && byId('scr_table'))
+ scrLoad();
diff --git a/data/js/vnstaff.js b/data/js/vnstaff.js
new file mode 100644
index 00000000..a4fa012f
--- /dev/null
+++ b/data/js/vnstaff.js
@@ -0,0 +1,102 @@
+// vnsStaffData maps alias id to staff data { NNN: { id: ..., aid: NNN, name: ...} }
+// used to fill form fields instead of ajax queries in vnsLoad() and vncLoad()
+var vnsStaffData = {};
+
+function vnsLoad() {
+ vnsStaffData = jsonParse(getText(byId('staffdata')||{})) || {};
+ var credits = jsonParse(byId('credits').value) || [];
+ for(var i = 0; i < credits.length; i++) {
+ var aid = credits[i].aid;
+ if(vnsStaffData[aid])
+ vnsAdd(vnsStaffData[aid], credits[i].role, credits[i].note);
+ }
+ vnsEmpty();
+
+ onSubmit(byName(byId('maincontent'), 'form')[0], vnsSerialize);
+
+ // dropdown search
+ dsInit(byId('credit_input'), '/xml/staff.xml?q=', function(item, tr) {
+ tr.appendChild(tag('td', { style: 'text-align: right; padding-right: 5px'}, 's'+item.getAttribute('id')));
+ tr.appendChild(tag('td', item.firstChild.nodeValue));
+ }, vnsFormAdd);
+}
+
+function vnsAdd(staff, role, note) {
+ var tbl = byId('credits_tbl');
+
+ var rlist = tag('select', {onchange:vnsSerialize});
+ var r = VARS.staff_roles;
+ for (var i = 0; i<r.length; i++)
+ rlist.appendChild(tag('option', {value:r[i][0], selected:r[i][0]==role}, r[i][1]));
+
+ tbl.appendChild(tag('tr', {id:'vns_a'+staff.aid},
+ tag('td', {'class':'tc_name'},
+ tag('input', {type:'hidden', value:staff.aid}),
+ tag('a', {href:'/s'+staff.id}, staff.name)),
+ tag('td', {'class':'tc_role'}, rlist),
+ tag('td', {'class':'tc_note'}, tag('input', {type:'text', 'class':'text', value:note})),
+ tag('td', {'class':'tc_del'}, tag('a', {href:'#', onclick:vnsDel}, mt('_js_remove')))
+ ));
+ vnsEmpty();
+ vnsSerialize();
+}
+
+function vnsEmpty() {
+ var x = byId('credits_loading');
+ var tbody = byId('credits_tbl');
+ var tbl = tbody.parentNode;
+ var thead = byName(tbl, 'thead');
+ if(x)
+ tbody.removeChild(x);
+ if(byName(tbody, 'tr').length < 1) {
+ tbody.appendChild(tag('tr', {id:'credits_tr_none'},
+ tag('td', {colspan:4}, mt('_vnstaffe_none'))));
+ if (thead.length)
+ tbl.removeChild(thead[0]);
+ } else {
+ if(byId('credits_tr_none'))
+ tbody.removeChild(byId('credits_tr_none'));
+ if (thead.length < 1) {
+ thead = tag('thead', tag('tr',
+ tag('td', {'class':'tc_name'}, mt('_vnstaffe_form_staff')),
+ tag('td', {'class':'tc_role'}, mt('_vnstaffe_form_role')),
+ tag('td', {'class':'tc_note'}, mt('_vnstaffe_form_note')),
+ tag('td', '')));
+ tbl.insertBefore(thead, tbody);
+ }
+ }
+}
+
+function vnsSerialize() {
+ var l = byName(byId('credits_tbl'), 'tr');
+ var c = [];
+ for (var i = 0; i < l.length; i++) {
+ if(l[i].id == 'credits_tr_none')
+ continue;
+ var aid = byName(byClass(l[i], 'tc_name')[0], 'input')[0];
+ var role = byName(byClass(l[i], 'tc_role')[0], 'select')[0];
+ var note = byName(byClass(l[i], 'tc_note')[0], 'input')[0];
+ c.push({ aid:Number(aid.value), role:role.value, note:note.value });
+ }
+ byId('credits').value = JSON.stringify(c);
+ return true;
+}
+
+function vnsDel() {
+ var tr = this;
+ while (tr.nodeName.toLowerCase() != 'tr')
+ tr = tr.parentNode;
+ byId('credits_tbl').removeChild(tr);
+ vnsEmpty();
+ vnsSerialize();
+ return false;
+}
+
+function vnsFormAdd(item) {
+ var s = { id:item.getAttribute('id'), aid:item.getAttribute('aid'), name:item.firstChild.nodeValue };
+ vnsAdd(s, 'staff', '');
+ return '';
+}
+
+if(byId('jt_box_vn_staff'))
+ vnsLoad();
diff --git a/data/js/vntagmod.js b/data/js/vntagmod.js
new file mode 100644
index 00000000..108fba39
--- /dev/null
+++ b/data/js/vntagmod.js
@@ -0,0 +1,163 @@
+var tglSpoilers = [];
+
+function tglLoad() {
+ for(var i=0; i<=3; i++)
+ tglSpoilers[i] = mt('_spoil_'+(i-1)); // l10n /_spoil_-?\d+/
+
+ // 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
+ var trs = byName(byId('tagtable'), 'tr');
+ for(var i=0; i<trs.length; i++) {
+ if(hasClass(trs[i], 'tagmod_cat'))
+ continue;
+ 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');
+ tg.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('_js_ds_tag_nometa'));
+
+ var name = items[0].firstChild.nodeValue;
+ var id = items[0].getAttribute('id');
+ if(byId('tgl_'+id))
+ return alert(mt('_tagv_double'));
+
+ if(!byId('tagmod_newtags'))
+ byId('tagtable').appendChild(tag('tr', {'class':'tagmod_cat', id:'tagmod_newtags'},
+ tag('td', {colspan:7}, mt('_tagv_newlyadded'))));
+
+ 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;
+
+ var ismod = byClass(byId('tagtable').parentNode, 'td', 'tc_myover').length;
+
+ byId('tagtable').appendChild(tag('tr', {id:'tgl_'+id},
+ tag('td', {'class':'tc_tagname'}, tag('a', {href:'/g'+id}, name)),
+ vote,
+ ismod ? tag('td', {'class':'tc_myover'}, ' ') : null,
+ spoil,
+ tag('td', {'class':'tc_allvote'}, ' '),
+ tag('td', {'class':'tc_allspoil'}, ' '),
+ tag('td', {'class':'tc_allwho'}, '')
+ ));
+ tglSerialize();
+ });
+}
+
+function tglSerialize() {
+ var r = [];
+ var l = byName(byId('tagtable'), 'tr');
+ for(var i=0; i<l.length; i++) {
+ if(hasClass(l[i], 'tagmod_cat'))
+ continue;
+ 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();
diff --git a/data/script.js b/data/script.js
deleted file mode 100644
index fe42292f..00000000
--- a/data/script.js
+++ /dev/null
@@ -1,3381 +0,0 @@
-/* function/attribute prefixes:
- * ctr -> Character <-> trait linking
- * cvn -> Character <-> VN linking
- * date -> Date selector
- * dd -> dropdown
- * ds -> dropdown search
- * fil -> Filter selector
- * 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
- * vns -> VN staff
- * vnc -> VN cast
- * sal -> Staff aliases editor
- */
-
-/* Internationalization note:
- * The translation keys to be inserted in the header of this file are parsed
- * from the source code. So when using mt(), make sure it is in the following
- * format:
- * mt('<exact translation key>',<more arguments>
- * or
- * mt('<exact translation key>')
- * The single quotes and (lack of) spaces are significant!
- *
- * To use non-exact translation keys as argument to mt(), make sure to
- * indicate which keys should be inserted in the header by adding a comment
- * containing the following format:
- * l10n /<perl regex>/
- * any keys matching that regex will be included.
- *
- * In the case of an mt('<key>') without any extra arguments, the entire
- * function call may be replaced by the TL string. In such a case mt()
- * behaves similar to a preprocessor macro in C.
- */
-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, async) {
- if(!async && http_request)
- http_request.abort();
- 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.");
- if(!async)
- http_request = req;
- req.onreadystatechange = function() {
- if(!req || req.readyState != 4 || !req.responseText)
- return;
- if(req.status != 200)
- return alert('Whoops, error! :(');
- func(req);
- };
- url += (url.indexOf('?')>=0 ? ';' : '?')+(Math.floor(Math.random()*999)+1);
- req.open('GET', url, true);
- req.send(null);
-}
-
-function setCookie(n,v) {
- var date = new Date();
- date.setTime(date.getTime()+(365*24*60*60*1000));
- document.cookie = cookie_prefix+n+'='+v+'; expires='+date.toGMTString()+'; path=/';
-}
-function getCookie(n) {
- var l = document.cookie.split(';');
- n = cookie_prefix+n;
- 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 t = arguments.length == 2 && typeof arguments[0] == 'string' ? arguments[0] : arguments.length == 3 ? arguments[1] : '*';
- var c = arguments[arguments.length-1];
- var l = byName(par, t);
- 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++)
- if(arguments[i] != null)
- 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 onSubmit(form, handler) {
- var prev_handler = form.onsubmit;
- form.onsubmit = function(e) {
- if(prev_handler)
- if(!prev_handler(e))
- return false;
- return handler(e);
- }
-}
-
-
-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 */
-function mt() {
- var key = arguments[0];
- var val = L10N_STR[key] ? L10N_STR[key] : 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;
-}
-
-function jsonParse(s) {
- return s ? JSON.parse(s) : '';
-}
-
-
-
-/* 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','class':'hidden'},
- tag('b', {id:'ivimg'}, ''),
- tag('br', null),
- tag('a', {href:'#', id:'ivfull'}, ''),
- tag('a', {href:'#', onclick: ivClose, id:'ivclose'}, mt('_js_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','class':'hidden'}, 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
- setClass(view, 'hidden', false);
- setContent(byId('ivimg'), tag('img', {src:u, onclick:ivClose,
- onload: function() { setClass(byId('ivimgload'), 'hidden', true); },
- 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';
- setClass(byId('ivimgload'), 'hidden', false);
- return false;
-}
-
-function ivClose() {
- setClass(byId('iv_view'), 'hidden', true);
- setClass(byId('ivimgload'), 'hidden', true);
- 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', 'class':'hidden', dd_used: false}));
-}
-
-function ddHide() {
- var box = byId('dd_box');
- setText(box, '');
- setClass(box, 'hidden', true);
- 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 < box.dd_x-10 || mouseX > box.dd_x+box.offsetWidth+10 || mouseY < box.dd_y-10 || mouseY > box.dd_y+box.offsetHeight+10)
- || (lnk && lnk == box.dd_lnk))
- ddHide();
- }
-
- if(!box.dd_used && lnk || box.dd_used && lnk && box.dd_lnk != 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);
- setClass(box, 'hidden', false);
-
- 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.dd_x = ddx;
- box.dd_y = ddy;
- 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);
- if(st == mt('_js_loading'))
- return null;
-
- var o = tag('ul', null);
- for(var i=0; i<rlist_status.length; i++) {
- var val = rlist_status[i] == 0 ? mt('_unknown') : mt('_rlist_status_'+rlist_status[i]); // l10n /_rlist_status_\d+/
- if(st == val)
- o.appendChild(tag('li', tag('i', val)));
- else
- o.appendChild(tag('li', tag('a', {href:'#', rl_rid:relid, rl_act:rlist_status[i], onclick:rlMod}, val)));
- }
- if(st != '--')
- o.appendChild(tag('li', tag('a', {href:'#', rl_rid:relid, rl_act:-1, onclick:rlMod}, mt('_vnpage_uopt_reldel'))));
-
- return tag('div', o);
-}
-
-function rlMod() {
- var lnk = byId('rlsel_'+this.rl_rid);
- var code = getText(byId('vnrlist_code'));
- var act = this.rl_act;
- ddHide();
- setContent(lnk, tag('b', {'class': 'grayedout'}, mt('_js_loading')));
- ajax('/xml/rlist.xml?formcode='+code+';id='+this.rl_rid+';e='+act, function(hr) {
- setText(lnk, act == -1 ? '--' : act == 0 ? mt('_unknown') : mt('_rlist_status_'+act));
- });
- return false;
-}
-
-if(byId('vnrlist_code')) {
- 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();
-}
-
-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) { /* categories */
- setClass(l[i], 'tsel', !hasClass(l[i], 'tsel'));
- tvsSet();
- } else if(i < 6) { /* spoiler level */
- tvsSet(i-3);
- } else /* limit */
- tvsSet(null, i == 6);
- }
- return false;
-}
-
-function tvsSet(lvl, lim, cats) {
- /* set/get level and limit to/from the links */
- var l = byName(byId('tagops'), 'a');
- var cat = cats || [];
- for(var i=0; i<l.length; i++) {
- if(i < 3) { /* categories */
- var c = l[i].href.substr(l[i].href.indexOf('#')+1);
- if(cats) {
- for(var j=0; j<cats.length && c != cats[j]; j++) ;
- setClass(l[i], 'tsel', j != cats.length);
- } else {
- if(hasClass(l[i], 'tsel'))
- cat.push(c);
- }
- } else if(i < 6) { /* spoiler level */
- if(lvl != null)
- setClass(l[i], 'tsel', i-3 == lvl);
- if(lvl == null && hasClass(l[i], 'tsel'))
- lvl = i-3;
- } else { /* display limit (6 = summary) */
- if(lim != null)
- setClass(l[i], 'tsel', lim == (i == 6));
- if(lim == null && hasClass(l[i], 'tsel'))
- lim = i == 6;
- }
- }
-
- /* 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);
- for(var j=0; j<cat.length && !hasClass(l[i], 'cat_'+cat[j]); j++) ;
- if(thislvl <= lvl && s < lim && j != cat.length) {
- setClass(l[i], 'hidden', false);
- s++;
- } else
- setClass(l[i], 'hidden', true);
- }
-}
-
-tvsInit();
-
-
-
-
-/* D A T E I N P U T */
-
-function dateLoad(obj, serfunc) {
- var year = tag('select', {style: 'width: 70px', onfocus:serfunc, onchange: dateSerialize, tabIndex: 10},
- tag('option', {value:0}, mt('_js_date_year')),
- tag('option', {value: 9999}, 'TBA')
- );
- for(var i=(new Date()).getFullYear()+5; i>=1980; i--)
- year.appendChild(tag('option', {value: i}, i));
-
- var month = tag('select', {style: 'width: 70px', onfocus:serfunc, onchange: dateSerialize, tabIndex: 10},
- tag('option', {value:99}, mt('_js_date_month'))
- );
- for(var i=1; i<=12; i++)
- month.appendChild(tag('option', {value: i}, i));
-
- var day = tag('select', {style: 'width: 70px', onfocus:serfunc, onchange: dateSerialize, tabIndex: 10},
- tag('option', {value:99}, mt('_js_date_day'))
- );
- for(var i=1; i<=31; i++)
- day.appendChild(tag('option', {value: i}, i));
-
- var div = tag('div', {date_obj: obj, date_serfunc: serfunc, date_val: obj ? obj.value : 0}, year, month, day);
- dateSet(div, obj ? obj.value : 0);
- return obj ? obj.parentNode.insertBefore(div, obj) : div;
-}
-
-function dateSet(div, val) {
- val = typeof val == 'object' ? val[0] : val;
- val = Math.floor(val) || 0;
- val = [ Math.floor(val/10000), Math.floor(val/100)%100, val%100 ];
- if(val[1] == 0) val[1] = 99;
- if(val[2] == 0) val[2] = 99;
- var l = byName(div, 'select');
- for(var i=0; i<l.length; i++)
- for(var j=0; j<l[i].options.length; j++)
- l[i].options[j].selected = l[i].options[j].value == val[i];
- dateSerialize(div.childNodes[0], true);
-}
-
-function dateSerialize(div, nonotify) {
- var div = div && div.parentNode ? div.parentNode : 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_val = val[0] == 0 ? 0 : val[0] == 9999 ? 99999999 : val[0]*10000+val[1]*100+(val[1]==99?99:val[2]);
- if(div.date_obj)
- div.date_obj.value = div.date_val;
- if(!nonotify && div.date_serfunc)
- div.date_serfunc(div);
-}
-
-{
- 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 () { setClass(byId('ds_box'), 'hidden', true) }, 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', 'class':'hidden'}, 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(obj);
-
- setClass(byId('ds_box'), 'hidden', true);
- 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) {
- setClass(box, 'hidden', true);
- 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';
- setClass(box, 'hidden', false);
-
- // 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();
- setClass(box, 'hidden', true);
- 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(',', 4);
- vnrAdd(rel[0], rel[1], rel[2]==1?true:false, rel[3]);
- }
- 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, official, 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')+' ',
- tag('input', {type: 'checkbox', onclick:vnrSerialize, id:'official_'+vid, checked:official}),
- tag('label', {'for':'official_'+vid}, mt('_vnedit_rel_official')),
- 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('_js_remove')))
- ));
-
- 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
- byName(byClass(trs[i], 'td', 'tc_rel')[0], 'input')[0].checked ? '1' : '0', // official
- 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 off = byName(byClass(relnew, 'td', 'tc_rel')[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(mt('_vnedit_rel_findformat'));
- return false;
- }
-
- txt.disabled = sel.disabled = off.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 = off.disabled = false;
- txt.value = '';
- setText(lnk, mt('_js_add'));
-
- 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, off.checked, 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('_js_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;
-var scrDefRel;
-
-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);
- if(scrRel.length <= 2)
- scrRel.shift();
- scrDefRel = scrRel[0][0];
-
- // load the current screenshots
- var scr = byId('screenshots').value.split(' ');
- var siz = byId('screensizes').value.split(' ');
- for(i=0; i<scr.length && scr[i].length>1; i++) {
- var r = scr[i].split(',');
- var s = siz[i].split(',');
- scrSet(scrAdd(r[0], r[1], r[2]), s[0], s[1]);
- }
-
- ivInit();
- scrLast();
- scrSetSubmit();
-}
-
-function scrSetSubmit() {
- var frm = byId('screenshots');
- while(frm.nodeName.toLowerCase() != 'form')
- frm = frm.parentNode;
- onSubmit(frm, function() {
- var loading = 0;
- var norelease = 0;
- var l = byName(byId('scr_table'), 'tr');
- for(var i=0; i<l.length-1; i++) {
- var rel = byName(l[i], 'select')[0];
- if(l[i].scr_status > 0)
- loading = 1;
- else if(rel.options[rel.selectedIndex].value == 0)
- norelease = 1;
- }
- if(loading) {
- alert(mt('_vnedit_scr_frmloading'));
- return false;
- } else if(norelease) {
- alert(mt('_vnedit_scr_frmnorel'));
- return false;
- }
- return true;
- });
-}
-
-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
-
- var tr = tag('tr', { id:'scr_tr_'+id, scr_id: id, scr_status: 1, scr_rel: rel, scr_nsfw: nsfw},
- tag('td', { 'class': 'thumb'}, mt('_js_loading')),
- tag('td',
- tag('b', mt('_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);
- return tr;
-}
-
-function scrSet(tr, width, height) {
- var dim = width+'x'+height;
- tr.scr_status = 0;
-
- // image
- 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')})
- )
- );
-
- // check full resolution with the list of DB-defined resolutions
- var odd = true;
- if(dim == '256x384') // special-case NDS resolution (not in the DB)
- odd = false;
- for(var j=0; j<resolutions.length && odd; j++) {
- if(typeof resolutions[j][1] != 'object') {
- if(resolutions[j][0] == dim)
- odd = false;
- } else {
- for(var k=1; k<resolutions[j].length; k++)
- if(resolutions[j][k][1] == dim)
- odd = false;
- }
- }
-
- // 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('_js_remove')), ')',
- tag('br', null),
- mt('_vnedit_scr_fullsize', dim),
- odd ? tag('b', {'class':'standout', 'style':'font-weight: bold'}, ' '+mt('_vnedit_scr_nonstandard')) : null,
- 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
- );
-}
-
-function scrLast() {
- if(byId('scr_last'))
- byId('scr_table').removeChild(byId('scr_last'));
- var full = byName(byId('scr_table'), 'tr').length >= 10;
-
- var rel = tag('select', {onchange: function(){scrDefRel=this.options[this.selectedIndex].value}, 'class':'scr_relsel', 'id':'scradd_relsel'});
- for(var j=0; j<scrRel.length; j++)
- rel.appendChild(tag('option', {value: scrRel[j][0], selected: scrDefRel == scrRel[j][0]}, scrRel[j][1]));
-
- 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),
- rel,
- 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})
- )
- ));
-}
-
-function scrDel(what) {
- var tr = what && what.scr_status != null ? what : this;
- while(tr.nodeName.toLowerCase() != 'tr')
- tr = tr.parentNode;
- tr.scr_status = null;
- if(tr.scr_upl && byId(tr.scr_upl))
- byId(tr.scr_upl).parentNode.removeChild(byId(tr.scr_upl));
- 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?upload='+scrUplNr,
- 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
- var upl = byId('scr_upload');
- upl.id = upl.name = 'scr_upl_file_'+scrUplNr;
- frm.appendChild(upl);
- frm.submit();
- ifr.scr_tr = scrAdd(0, 0, 0);
- ifr.scr_upl = ifid;
- ifr.scr_tr.scr_rel = byId('scradd_relsel').options[byId('scradd_relsel').selectedIndex].value;
- 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 == 1) {
- try {
- tr.scr_id = fr.window.document.getElementsByTagName('image')[0].getAttribute('id');
- } catch(e) {
- tr.scr_id = -10;
- }
- if(tr.scr_id < 0) {
- alert(tr.scr_id == -10 ? mt('_vnedit_scr_oops') :
- tr.scr_id == -1 ? mt('_vnedit_scr_errformat') : mt('_vnedit_scr_errempty'));
- scrDel(tr);
- } else {
- tr.id = 'scr_tr_'+tr.scr_id;
- scrSet(tr, fr.window.document.getElementsByTagName('image')[0].getAttribute('width'), fr.window.document.getElementsByTagName('image')[0].getAttribute('height'));
- scrSerialize();
- ivInit();
- }
- }
-
- tr.scr_upl = null;
- /* remove the <div> in a timeout, otherwise some browsers think the page is still loading */
- setTimeout(function() { ifr.parentNode.parentNode.removeChild(ifr.parentNode) }, 1000);
-}
-
-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') && byId('scr_table'))
- 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('_spoil_'+(i-1)); // l10n /_spoil_-?\d+/
-
- // 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
- var trs = byName(byId('tagtable'), 'tr');
- for(var i=0; i<trs.length; i++) {
- if(hasClass(trs[i], 'tagmod_cat'))
- continue;
- 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');
- tg.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('_js_ds_tag_nometa'));
-
- var name = items[0].firstChild.nodeValue;
- var id = items[0].getAttribute('id');
- if(byId('tgl_'+id))
- return alert(mt('_tagv_double'));
-
- if(!byId('tagmod_newtags'))
- byId('tagtable').appendChild(tag('tr', {'class':'tagmod_cat', id:'tagmod_newtags'},
- tag('td', {colspan:7}, mt('_tagv_newlyadded'))));
-
- 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;
-
- var ismod = byClass(byId('tagtable').parentNode, 'td', 'tc_myover').length;
-
- byId('tagtable').appendChild(tag('tr', {id:'tgl_'+id},
- tag('td', {'class':'tc_tagname'}, tag('a', {href:'/g'+id}, name)),
- vote,
- ismod ? tag('td', {'class':'tc_myover'}, ' ') : null,
- spoil,
- tag('td', {'class':'tc_allvote'}, ' '),
- tag('td', {'class':'tc_allspoil'}, ' '),
- tag('td', {'class':'tc_allwho'}, '')
- ));
- tglSerialize();
- });
-}
-
-function tglSerialize() {
- var r = [];
- var l = byName(byId('tagtable'), 'tr');
- for(var i=0; i<l.length; i++) {
- if(hasClass(l[i], 'tagmod_cat'))
- continue;
- 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('_js_remove')))
- ));
- rvnEmpty();
-}
-
-function rvnDel() {
- var tr = this;
- while(tr.nodeName.toLowerCase() != 'tr')
- tr = tr.parentNode;
- tr.parentNode.removeChild(tr);
- rvnEmpty();
- rvnSerialize();
- 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 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('_js_add'));
-
- 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('_js_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('_js_add'));
-
- 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('_js_remove')))
- ));
-
- 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(mt('_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('_js_add'));
-
- 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();
-
-
-
-
-/* C H A R A C T E R T R A I T S (/c+/edit) */
-
-// l10n /_spoil_-?\d+/
-
-function ctrLoad() {
- // load current traits
- var l = byId('traits').value.split(' ');
- var v = {}; // tag id -> spoiler lookup table
- var q = []; // list of id=X parameters
- for(var i=0; i<l.length; i++) {
- if(l[i]) {
- var m = l[i].split(/-/);
- v[m[0]] = m[1];
- q[i] = 'id='+m[0];
- }
- }
- if(q.length > 0)
- ajax('/xml/traits.xml?r=200;'+q.join(';'), function (ht) {
- var t = ht.responseXML.getElementsByTagName('item');
- for(var i=0; i<t.length; i++)
- ctrAdd(t[i], v[t[i].getAttribute('id')]);
- }, 1);
- else
- ctrEmpty();
-
- // dropdown
- dsInit(byId('trait_input'), '/xml/traits.xml?q=', function(item, tr) {
- var g = item.getAttribute('groupname');
- g = g ? g+' / ' : '';
- tr.appendChild(tag('td', { style: 'text-align: right; padding-right: 5px'}, 'i'+item.getAttribute('id')));
- tr.appendChild(tag('td',
- tag('b', {'class':'grayedout'}, g), item.firstChild.nodeValue,
- tag('b', {'class':'grayedout'}, item.getAttribute('meta')=='yes' ? mt('_js_ds_tag_meta') : '')));
- }, ctrFormAdd);
-}
-
-function ctrEmpty() {
- var x = byId('traits_loading');
- var t = byId('traits_tbl');
- if(x)
- t.removeChild(x);
- var l = byName(t, 'tr');
- var e = byId('traits_empty');
- if(e && l.length > 1)
- t.removeChild(e);
- else if(!e && l.length < 1)
- t.appendChild(tag('tr', {id:'traits_empty',colspan:3}, tag('td', mt('_chare_traits_empty'))));
-}
-
-function ctrAdd(item, spoil) {
- var id = item.getAttribute('id');
- var name = item.firstChild.nodeValue;
- var group = item.getAttribute('groupname');
- var sp = tag('td', {'class':'tc_spoil', onclick:ctrSpoilNext, ctr_spoil:spoil}, mt('_spoil_'+spoil));
- ddInit(sp, 'left', ctrSpoilDD);
- byId('traits_tbl').appendChild(tag('tr', {ctr_id:id, ctr_spoiler:spoil},
- tag('td', {'class':'tc_name'},
- tag('b', {'class':'grayedout'}, group?group+' / ':''),
- tag('a', {'href':'/i'+id}, name)),
- sp,
- tag('td', {'class':'tc_del'}, tag('a', {href:'#', onclick:ctrDel}, mt('_js_remove')))
- ));
- ctrEmpty();
- ctrSerialize();
-}
-
-function ctrFormAdd(item) {
- var l = byName(byId('traits_tbl'), 'tr');
- for(var i=0; i<l.length; i++)
- if(l[i].ctr_id && l[i].ctr_id == item.getAttribute('id'))
- break;
- if(i < l.length)
- alert(mt('_chare_traits_present'));
- else if(item.getAttribute('meta') == 'yes')
- alert(mt('_chare_traits_nometa'));
- else
- ctrAdd(item, 0);
- return '';
-}
-
-function ctrSpoilNext() {
- if(++this.ctr_spoil > 2)
- this.ctr_spoil = 0;
- setText(this, mt('_spoil_'+this.ctr_spoil));
- ddRefresh();
- ctrSerialize();
-}
-
-function ctrSpoilDD(lnk) {
- var lst = tag('ul', null);
- for(var i=0; i<=2; i++)
- lst.appendChild(tag('li', i == lnk.ctr_spoil
- ? tag('i', mt('_spoil_'+i))
- : tag('a', {href: '#', onclick:ctrSpoilSet, ctr_td:lnk, ctr_sp:i}, mt('_spoil_'+i))
- ));
- return lst;
-}
-
-function ctrSpoilSet() {
- this.ctr_td.ctr_spoil = this.ctr_sp;
- setText(this.ctr_td, mt('_spoil_'+this.ctr_sp));
- ddHide();
- ctrSerialize();
- return false;
-}
-
-function ctrDel() {
- var tr = this;
- while(tr.nodeName.toLowerCase() != 'tr')
- tr = tr.parentNode;
- tr.parentNode.removeChild(tr);
- ctrEmpty();
- ctrSerialize();
- return false
-}
-
-function ctrSerialize() {
- var l = byName(byId('traits_tbl'), 'tr');
- var v = [];
- for(var i=0; i<l.length; i++)
- if(l[i].ctr_id)
- v.push(l[i].ctr_id+'-'+byClass(l[i], 'tc_spoil')[0].ctr_spoil);
- byId('traits').value = v.join(' ');
-}
-
-if(byId('traits_tbl'))
- ctrLoad();
-
-
-
-
-/* C H A R A C T E R < - > V N L I N K I N G (/c+/edit) */
-
-function cvnLoad() {
- // load current links
- var l = byId('vns').value.split(' ');
- var v = {}; // vid -> { rid: [ role, spoil ], .. }
- var q = []; // list of v=X parameters
- for(var i=0; i<l.length; i++) {
- if(!l[i])
- continue;
- var m = l[i].split(/-/); // vid, rid, spoil, role
- if(!v[m[0]]) {
- q.push('v='+m[0]);
- v[m[0]] = {};
- }
- v[m[0]][m[1]] = [ m[3], m[2] ];
- }
- if(q.length > 0)
- ajax('/xml/releases.xml?'+q.join(';'), function(hr) {
- var vns = byName(hr.responseXML, 'vn');
- for(var i=0; i<vns.length; i++) {
- var vid = vns[i].getAttribute('id');
- cvnVNAdd(vns[i]);
- var rels = byName(vns[i], 'release');
- for(var r=0; r<rels.length; r++) {
- var rid = rels[r].getAttribute('id');
- if(v[vid][rid])
- cvnRelAdd(vid, rid, v[vid][rid][0], v[vid][rid][1]);
- }
- if(v[vid][0])
- cvnRelAdd(vid, 0, v[vid][0][0], v[vid][0][1]);
- }
- cvnEmpty();
- }, 1);
- else
- cvnEmpty();
-
- // dropdown search
- dsInit(byId('vns_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)));
- }, cvnFormAdd);
-}
-
-function cvnEmpty() {
- var x = byId('vns_loading');
- var t = byId('vns_tbl');
- if(x)
- t.removeChild(x);
- var l = byName(t, 'tr');
- var e = byId('vns_empty');
- if(e && l.length > 1)
- t.removeChild(e);
- else if(!e && l.length < 1)
- t.appendChild(tag('tr', {id:'vns_empty',colspan:3}, tag('td', mt('_chare_vns_empty'))));
-}
-
-function cvnVNAdd(vn, rel) {
- var vid = vn.getAttribute('id');
- var rels = byName(vn, 'release');
- byId('vns_tbl').appendChild(tag('tr', {id:'cvn_v'+vid, cvn_vid:vid, cvn_rels:rels},
- tag('td', {'class':'tc_vn',colspan:4}, 'v'+vid+':',
- tag('a', {href:'/v'+vid}, vn.getAttribute('title')),
- tag('i', '(', tag('a', {href:'#', onclick:cvnRelNew}, mt('_chare_vns_addrel')), ')')
- )
- ));
- if(rel)
- cvnRelAdd(vid, 0, 'primary', 0);
- cvnEmpty();
-}
-
-function cvnRelAdd(vid, rid, role, spoil) {
- var rels = byId('cvn_v'+vid).cvn_rels;
- var rsel = tag('select', {onchange:cvnRelChange}, tag('option', {value:0}, mt('_chare_vns_other')));
- for(var i=0; i<rels.length; i++) {
- var id = rels[i].getAttribute('id');
- rsel.appendChild(tag('option', {value: id, selected:id==rid},
- '['+rels[i].getAttribute('lang')+'] '+rels[i].firstChild.nodeValue+' (r'+id+')'));
- }
-
- var lsel = tag('select', {onchange:cvnSerialize});
- for(var i=0; i<char_roles.length; i++) // l10n /^_charrole_/
- lsel.appendChild(tag('option', {value: char_roles[i], selected:char_roles[i]==role}, mt('_charrole_'+char_roles[i])));
-
- // l10n /_spoil_\d+/
- var ssel = tag('select', {onchange:cvnSerialize});
- for(var i=0; i<3; i++)
- ssel.appendChild(tag('option', {value:i, selected:i==spoil}, mt('_spoil_'+i)));
-
- var tbl = byId('vns_tbl');
- var l = byName(tbl, 'tr');
- var last = null;
- for(var i=1; i<l.length; i++)
- if(l[i-1].cvn_vid == vid && l[i].cvn_vid != vid)
- last = l[i-1];
- tbl.insertBefore(tag('tr', {id:'cvn_v'+vid+'r'+rid, cvn_vid:vid, cvn_rid:rid},
- tag('td', {'class':'tc_rel'}, rsel),
- tag('td', {'class':'tc_rol'}, lsel),
- tag('td', {'class':'tc_spl'}, ssel),
- tag('td', {'class':'tc_del'}, tag('a', {href:'#', onclick:cvnRelDel}, mt('_js_remove')))
- ), last);
-}
-
-function cvnRelChange() {
- // look for duplicates and disallow the change
- var val = this.options[this.selectedIndex].value;
- var tr = this;
- while(tr.nodeName.toLowerCase() != 'tr')
- tr = tr.parentNode;
- if(byId('cvn_v'+tr.cvn_vid+'r'+val)) {
- alert(mt('_chare_vns_relexists'));
- for(var i=0; i<this.options.length; i++)
- this.options[i].selected = this.options[i].value == tr.cvn_rid;
- return;
- }
- // otherwise, 'rename' this entry
- tr.id = 'cvn_v'+tr.cvn_vid+'r'+val;
- tr.cvn_rid = val;
- cvnSerialize();
-}
-
-function cvnRelNew() {
- var tr = this;
- while(tr.nodeName.toLowerCase() != 'tr')
- tr = tr.parentNode;
- var id = 0;
- if(byId('cvn_v'+tr.cvn_vid+'r0')) {
- for(var i=0; i<tr.cvn_rels.length; i++) {
- id = tr.cvn_rels[i].getAttribute('id');
- if(!byId('cvn_v'+tr.cvn_vid+'r'+id))
- break;
- }
- if(i == tr.cvn_rels.length) {
- alert(mt('_chare_vns_allrel'));
- return false;
- }
- }
- cvnRelAdd(tr.cvn_vid, id, 'primary', 0);
- cvnSerialize();
- return false;
-}
-
-function cvnRelDel() {
- var tbl = byId('vns_tbl');
- var tr = this;
- while(tr.nodeName.toLowerCase() != 'tr')
- tr = tr.parentNode;
- tbl.removeChild(tr);
- var l = byName(tbl, 'tr');
- var c = 0;
- for(var i=0; i<l.length; i++)
- if(l[i].cvn_vid == tr.cvn_vid)
- c++;
- if(c <= 1)
- tbl.removeChild(byId('cvn_v'+tr.cvn_vid));
- cvnSerialize();
- cvnEmpty();
- return false;
-}
-
-function cvnFormAdd(item) {
- var inpt = byId('vns_input');
- inpt.disabled = true;
-
- ajax('/xml/releases.xml?v='+item.getAttribute('id'), function(hr) {
- inpt.disabled = false;
- inpt.value = '';
-
- var items = byName(hr.responseXML, 'vn');
- if(items.length < 1) // shouldn't happen
- return alert('Oops! Error!');
-
- var id = items[0].getAttribute('id');
- if(byId('cvn_v'+id))
- return alert(mt('_chare_vns_exists'));
- cvnVNAdd(items[0], 1);
- cvnSerialize();
- }, 1);
- return mt('_js_loading');
-}
-
-function cvnSerialize() {
- var l = byName(byId('vns_tbl'), 'tr');
- var v = [];
- for(var i=0; i<l.length; i++)
- if(l[i].cvn_rid != null) {
- var rol = byName(byClass(l[i], 'tc_rol')[0], 'select')[0];
- var spl = byName(byClass(l[i], 'tc_spl')[0], 'select')[0];
- v.push(l[i].cvn_vid+'-'+l[i].cvn_rid+'-'+
- spl.options[spl.selectedIndex].value+'-'+
- rol.options[rol.selectedIndex].value);
- }
- byId('vns').value = v.join(' ');
-}
-
-if(byId('jt_box_chare_vns'))
- cvnLoad();
-
-
-
-
-/* S T A F F (/s+/edit) */
-
-function salLoad () {
- byId('alias_tbl').appendChild(tag('tr', {id:'alias_new'},
- tag('td', null),
- tag('td', {colspan:3}, tag('a', {href:'#', onclick:salFormAdd}, mt('_staffe_aliases_add')))));
-
- salAdd(byId('primary').value||0, byId('name').value, byId('original').value);
- var aliases = jsonParse(byId('aliases').value) || [];
- for(var i = 0; i < aliases.length; i++) {
- salAdd(aliases[i].aid, aliases[i].name, aliases[i].orig);
- }
-
- byName(byId('maincontent'), 'form')[0].onsubmit = salSerialize;
-}
-
-function salAdd(aid, name, original) {
- var tbl = byId('alias_tbl');
- var first = tbl.rows.length <= 1;
- tbl.insertBefore(tag('tr', first ? {id:'primary_name'} : null,
- tag('td', {'class':'tc_id' },
- tag('input', {type:'radio', name:'primary_id', value:aid, checked:first, onchange:salPrimary})),
- tag('td', {'class':'tc_name' }, tag('input', {type:'text', 'class':'text', value:name})),
- tag('td', {'class':'tc_original' }, tag('input', {type:'text', 'class':'text', value:original})),
- tag('td', {'class':'tc_add' }, !first ?
- tag('a', {href:'#', onclick:salDel}, mt('_js_remove')) : null)
- ), byId('alias_new'));
-}
-
-function salPrimary() {
- var prev = byId('primary_name')
- prev.removeAttribute('id');
- byClass(prev, 'td', 'tc_add')[0].appendChild(tag('a', {href:'#', onclick:salDel}, mt('_js_remove')));
- var tr = this;
- while (tr && tr.nodeName.toLowerCase() != 'tr')
- tr = tr.parentNode;
- tr.setAttribute('id', 'primary_name');
- var td = byClass(tr, 'td', 'tc_add')[0];
- while (td.firstChild)
- td.removeChild(td.firstChild);
-
- return salSerialize();
-}
-
-function salSerialize() {
- var tbl = byName(byId('alias_tbl'), 'tr');
- var a = [];
- for (var i = 0; i < tbl.length; ++i) {
- if(tbl[i].id == 'alias_new')
- continue;
- var id = byName(byClass(tbl[i], 'td', 'tc_id')[0], 'input')[0].value;
- var name = byName(byClass(tbl[i], 'td', 'tc_name')[0], 'input')[0].value;
- var orig = byName(byClass(tbl[i], 'td', 'tc_original')[0], 'input')[0].value;
- if(tbl[i].id == 'primary_name') {
- byId('name').value = name;
- byId('original').value = orig;
- byId('primary').value = id;
- } else
- a.push({ aid:Number(id), name:name, orig:orig });
- }
- byId('aliases').value = JSON.stringify(a);
- return true;
-}
-
-function salDel() {
- var tr = this;
- while (tr && tr.nodeName.toLowerCase() != 'tr')
- tr = tr.parentNode;
- var tbl = byId('alias_tbl');
- tbl.removeChild(tr);
- salSerialize();
- return false;
-}
-
-function salFormAdd() {
- salAdd(0, '', '');
- byName(byClass(byId('alias_new').previousSibling, 'td', 'tc_name')[0], 'input')[0].focus();
- return false;
-}
-
-if(byId('jt_box_staffe_geninfo'))
- salLoad();
-
-
-
-
-/* S T A F F < - > V N L I N K I N G (/v+/edit#vn_staff) */
-
-// vnsStaffData maps alias id to staff data { NNN: { id: ..., aid: NNN, name: ...} }
-// used to fill form fields instead of ajax queries in vnsLoad() and vncLoad()
-var vnsStaffData = {};
-
-function vnsLoad() {
- vnsStaffData = jsonParse(getText(byId('staffdata')||{})) || {};
- var credits = jsonParse(byId('credits').value) || [];
- for(var i = 0; i < credits.length; i++) {
- var aid = credits[i].aid;
- if(vnsStaffData[aid])
- vnsAdd(vnsStaffData[aid], credits[i].role, credits[i].note);
- }
- vnsEmpty();
-
- onSubmit(byName(byId('maincontent'), 'form')[0], vnsSerialize);
-
- // dropdown search
- dsInit(byId('credit_input'), '/xml/staff.xml?q=', function(item, tr) {
- tr.appendChild(tag('td', { style: 'text-align: right; padding-right: 5px'}, 's'+item.getAttribute('id')));
- tr.appendChild(tag('td', item.firstChild.nodeValue));
- }, vnsFormAdd);
-}
-
-function vnsAdd(staff, role, note) {
- var tbl = byId('credits_tbl');
-
- var rlist = tag('select', {onchange:vnsSerialize});
- for (var i = 0; i < staff_roles.length; i++) // l10n /^_credit_/
- rlist.appendChild(tag('option', {value:staff_roles[i], selected:staff_roles[i]==role},
- mt('_credit_'+staff_roles[i])));
-
- tbl.appendChild(tag('tr', {id:'vns_a'+staff.aid},
- tag('td', {'class':'tc_name'},
- tag('input', {type:'hidden', value:staff.aid}),
- tag('a', {href:'/s'+staff.id}, staff.name)),
- tag('td', {'class':'tc_role'}, rlist),
- tag('td', {'class':'tc_note'}, tag('input', {type:'text', 'class':'text', value:note})),
- tag('td', {'class':'tc_del'}, tag('a', {href:'#', onclick:vnsDel}, mt('_js_remove')))
- ));
- vnsEmpty();
- vnsSerialize();
-}
-
-function vnsEmpty() {
- var x = byId('credits_loading');
- var tbody = byId('credits_tbl');
- var tbl = tbody.parentNode;
- var thead = byName(tbl, 'thead');
- if(x)
- tbody.removeChild(x);
- if(byName(tbody, 'tr').length < 1) {
- tbody.appendChild(tag('tr', {id:'credits_tr_none'},
- tag('td', {colspan:4}, mt('_vnstaffe_none'))));
- if (thead.length)
- tbl.removeChild(thead[0]);
- } else {
- if(byId('credits_tr_none'))
- tbody.removeChild(byId('credits_tr_none'));
- if (thead.length < 1) {
- thead = tag('thead', tag('tr',
- tag('td', {'class':'tc_name'}, mt('_vnstaffe_form_staff')),
- tag('td', {'class':'tc_role'}, mt('_vnstaffe_form_role')),
- tag('td', {'class':'tc_note'}, mt('_vnstaffe_form_note')),
- tag('td', '')));
- tbl.insertBefore(thead, tbody);
- }
- }
-}
-
-function vnsSerialize() {
- var l = byName(byId('credits_tbl'), 'tr');
- var c = [];
- for (var i = 0; i < l.length; i++) {
- if(l[i].id == 'credits_tr_none')
- continue;
- var aid = byName(byClass(l[i], 'tc_name')[0], 'input')[0];
- var role = byName(byClass(l[i], 'tc_role')[0], 'select')[0];
- var note = byName(byClass(l[i], 'tc_note')[0], 'input')[0];
- c.push({ aid:Number(aid.value), role:role.value, note:note.value });
- }
- byId('credits').value = JSON.stringify(c);
- return true;
-}
-
-function vnsDel() {
- var tr = this;
- while (tr.nodeName.toLowerCase() != 'tr')
- tr = tr.parentNode;
- byId('credits_tbl').removeChild(tr);
- vnsEmpty();
- vnsSerialize();
- return false;
-}
-
-function vnsFormAdd(item) {
- var s = { id:item.getAttribute('id'), aid:item.getAttribute('aid'), name:item.firstChild.nodeValue };
- vnsAdd(s, 'staff', '');
- return '';
-}
-
-if(byId('jt_box_vn_staff'))
- vnsLoad();
-
-
-
-
-/* V N C H A R A C T E R S C A S T (/v+/edit#vn_cast) */
-
-var vncImportData = [];
-
-function vncLoad() {
- var cast = jsonParse(byId('seiyuu').value) || [];
- var copt = byId('cast_chars').options;
- var chars = {};
- for(var i = 0; i < copt.length; i++) {
- if(copt[i].value)
- chars[copt[i].value] = copt[i].text;
- }
- cast.sort(function(a, b) {
- if(chars[a.cid] < chars[b.cid]) return -1;
- if(chars[a.cid] > chars[b.cid]) return 1;
- return 0;
- });
- for(var i = 0; i < cast.length; i++) {
- var aid = cast[i].aid;
- if(vnsStaffData[aid]) // vnsStaffData is filled by vnsLoad()
- vncAdd(vnsStaffData[aid], cast[i].cid, cast[i].note);
- }
- vncEmpty();
-
- var cast_import = byId('cast_import');
- if(cast_import) {
- vncImportData = jsonParse(getText(byId('castimpdata')||{})) || [];
- if(vncImportData.length)
- byName(cast_import, 'a')[0].onclick = vncImport;
- else
- cast_import.style.display = 'none';
- }
- onSubmit(byName(byId('maincontent'), 'form')[0], vncSerialize);
-
- // dropdown search
- dsInit(byId('cast_input'), '/xml/staff.xml?q=', function(item, tr) {
- tr.appendChild(tag('td', { style: 'text-align: right; padding-right: 5px'}, 's'+item.getAttribute('id')));
- tr.appendChild(tag('td', item.firstChild.nodeValue));
- }, vncFormAdd);
-}
-
-function vncImport() {
- if (!vncImportData.length)
- return false;
- var c = {};
- for (var i = 0; i < vncImportData.length; i++) {
- var s = vncImportData[i];
- c[s.cid] = s;
- }
- // exclude already credited cast from import list
- var l = byName(byId('cast_tbl'), 'tr');
- for (var i = 0; i < l.length; i++) {
- if(l[i].id == 'cast_tr_none')
- continue;
- var role = byName(byClass(l[i], 'tc_char')[0], 'select')[0].value;
- if (role in c)
- delete c[role];
- }
- for (var cid in c) {
- if (!c.hasOwnProperty(cid))
- continue;
- vncAdd({ id:c[cid].sid, aid:c[cid].aid, name:c[cid].name }, cid, '');
- }
- return false;
-}
-
-function vncAdd(seiyuu, chr, note) {
- var tbl = byId('cast_tbl');
-
- var csel = byId('cast_chars').cloneNode(true);
- csel.removeAttribute('id');
- csel.value = chr;
-
- tbl.appendChild(tag('tr', {id:'vnc_a'+seiyuu.aid},
- tag('td', {'class':'tc_char'}, csel),
- tag('td', {'class':'tc_name'},
- tag('input', {type:'hidden', value:seiyuu.aid}),
- tag('a', {href:'/s'+seiyuu.id}, seiyuu.name)),
- tag('td', {'class':'tc_note'}, tag('input', {type:'text', 'class':'text', value:note})),
- tag('td', {'class':'tc_del'}, tag('a', {href:'#', onclick:vncDel}, mt('_js_remove')))
- ));
- vncEmpty();
- vncSerialize();
-}
-
-function vncFormAdd(item) {
- var chr = byId('cast_chars').value;
- if (chr) {
- var s = { id:item.getAttribute('id'), aid:item.getAttribute('aid'), name:item.firstChild.nodeValue };
- vncAdd(s, chr, '');
- } else
- alert(mt('_vnedit_cast_nochar'));
- return '';
-}
-
-function vncEmpty() {
- var x = byId('cast_loading');
- var tbody = byId('cast_tbl');
- var tbl = tbody.parentNode;
- var thead = byName(tbl, 'thead');
- if(x)
- tbody.removeChild(x);
- if(byName(tbody, 'tr').length < 1) {
- tbody.appendChild(tag('tr', {id:'cast_tr_none'},
- tag('td', {colspan:4}, mt('_vnstaffe_none'))));
- if (thead.length)
- tbl.removeChild(thead[0]);
- } else {
- if(byId('cast_tr_none'))
- tbody.removeChild(byId('cast_tr_none'));
- if (thead.length < 1) {
- thead = tag('thead', tag('tr',
- tag('td', {'class':'tc_char'}, mt('_vnedit_cast_char')),
- tag('td', {'class':'tc_name'}, mt('_vnedit_cast_seiyuu')),
- tag('td', {'class':'tc_note'}, mt('_vnedit_cast_note')),
- tag('td', '')));
- tbl.insertBefore(thead, tbody);
- }
- }
-}
-
-function vncSerialize() {
- var l = byName(byId('cast_tbl'), 'tr');
- var c = [];
- for (var i = 0; i < l.length; i++) {
- if(l[i].id == 'cast_tr_none')
- continue;
- var aid = byName(byClass(l[i], 'tc_name')[0], 'input')[0];
- var role = byName(byClass(l[i], 'tc_char')[0], 'select')[0];
- var note = byName(byClass(l[i], 'tc_note')[0], 'input')[0];
- c.push({ aid:Number(aid.value), cid:Number(role.value), note:note.value });
- }
- byId('seiyuu').value = JSON.stringify(c);
- return true;
-}
-
-function vncDel() {
- var tr = this;
- while (tr.nodeName.toLowerCase() != 'tr')
- tr = tr.parentNode;
- byId('cast_tbl').removeChild(tr);
- vncEmpty();
- vncSerialize();
- return false;
-}
-
-if(byId('jt_box_vn_cast'))
- vncLoad();
-
-
-
-/* F I L T E R S Y S T E M */
-
-
-/* Filter box definition:
- * [ <title>,
- * [ <category_name>,
- * [ <fieldcode>, <fieldname>, <fieldcontents>, <fieldreadfunc>, <fieldwritefunc> ], ..
- * ], ..
- * ]
- * Where:
- * <title> human-readable title of the filter box
- * <category_name> human-readable name of the category. ignored if there's only one category
- * <fieldcode> code of this field, refers to the <field> in the filter format. Empty string for just a <tr>
- * <fieldname> human-readanle name of the field. Empty to not display a label. Space for always-enabled items (without checkbox)
- * <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
- *
- * Filter string format:
- * <field>-<value1>~<value2>.<field2>-<value>.<field3>-<value1>~<value2>
- * Where:
- * <field> = [a-z0-9]+
- * <value> = [a-zA-Z0-9_]+ and any UTF-8 characters not in the ASCII range
- * Escaping of the <value>:
- * "_<two-number-code>"
- * Where <two-number-code> is the decimal index to the following array:
- * _ <space> ! " # $ % & ' ( ) * + , - . / : ; < = > ? @ [ \ ] ^ ` { | } ~
- * 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 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]);
- p.appendChild(a);
- p.appendChild(tag(' '));
-
- // category contents
- var t = tag('table', {'class':'formtable', fil_num: idx}, null);
- setClass(t, 'hidden', true);
- 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),
- fd[1] ? tag('td', {'class':'label'},
- tag('label', {'for':'fil_check_'+fd[0]}, lab),
- typeof fd[1] == 'object' ? tag('b', fd[1][1]) : null
- ) : null,
- 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);
-
- fil_cats[idx] = a;
- }
-
- addBody(tag('div', { id: 'fil_div', 'class':'hidden' },
- tag('a', {href:'#', onclick:filShow, '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');
- 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();
-}
-
-function filSaveDefault() {
- var but = this;
- var note = byId('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) {
- 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);
- }
- 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
-}
-
-function filSelectField(obj) {
- var t = obj && obj.parentNode ? obj : this;
- setClass(byId('fil_savenote'), 'hidden', true);
- // update checkbox and label
- var o = t;
- while(o.nodeName.toLowerCase() != 'tr')
- o = o.parentNode;
- var c = byId('fil_check_'+o.fil_code);
- if(c != t)
- c.checked = true;
- if(hasClass(c, 'hidden'))
- c.checked = true;
- setClass(byName(o, 'label')[0], 'active', c.checked);
-
- // update category link
- while(o.nodeName.toLowerCase() != 'table')
- o = o.parentNode;
- var l = byName(o, 'input');
- 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)
- n++;
- setClass(fil_cats[o.fil_num], 'active', n>0);
-
- // serialize
- filSerialize();
- return true;
-}
-
-function filSerialize() {
- var num = 0;
- var values = {};
- for(var f in fil_cats[0]) {
- if(!byId('fil_check_'+f).checked)
- continue;
- if(!hasClass(byId('fil_check_'+f), '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('~');
- }
- 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+')' : '');
-}
-
-function filDeSerialize() {
- var d = byId('fil').value;
- var fs = d.split('.');
- var f = new Object;
- for(var i=0; i<fs.length; i++) {
- var v = fs[i].split('-');
- if(fil_cats[0][v[0]])
- f[v[0]] = v[1];
- }
- for(var fn in fil_cats[0])
- if(!f[fn])
- f[fn] = '';
- for(var fn in f) {
- var c = byId('fil_check_'+fn);
- if(!c)
- continue;
- c.checked = f[fn] == '' ? false : true;
- 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
- // batched after all fields have been updated, and in some cases the
- // writefunc() triggers the same filSelectField() as well
- filSelectField(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;
- 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';
-
- return false;
-}
-
-var curSlider = null;
-function filFSlider(c, n, min, max, def, unit) {
- var bw = 200; var pw = 1; // slidebar width and pointer width
- var s = tag('p', {fil_val:def, 'class':'slider'});
- var b = tag('div', {style:'width:'+(bw-2)+'px;', s:s});
- var p = tag('div', {style:'width:'+pw+'px;', s:s});
- var v = tag('span', def+' '+unit);
- s.appendChild(b);
- b.appendChild(p);
- s.appendChild(v);
-
- var set = function (e, v) {
- var w = bw-pw-6;
- var s,x;
-
- if(v) {
- s = e;
- x = v[0] == '' ? def : parseInt(v[0]);
- x = (x-min)*w/(max-min);
- } else {
- s = curSlider;
- if(!e) e = window.event;
- x = (!e) ? (def-min)*w/(max-min)
- : (e.pageX || e.clientX + document.body.scrollLeft - document.body.clientLeft)-5;
- var o = s.childNodes[0];
- while(o.offsetParent) {
- x -= o.offsetLeft;
- o = o.offsetParent;
- }
- }
-
- if(x<0) x = 0; if(x>w) x = w;
- s.fil_val = min + Math.floor(x*(max-min)/w);
- s.childNodes[1].innerHTML = s.fil_val+' '+unit;
- s.childNodes[0].childNodes[0].style.left = x+'px';
- return false;
- }
-
- b.onmousedown = p.onmousedown = function (e) {
- curSlider = this.s;
- if(!curSlider.oldmousemove) curSlider.oldmousemove = document.onmousemove;
- if(!curSlider.oldmouseup) curSlider.oldmouseup = document.onmouseup;
- document.onmouseup = function () {
- document.onmousemove = curSlider.oldmousemove;
- curSlider.oldmousemove = null;
- document.onmouseup = curSlider.oldmouseup;
- curSlider.oldmouseup = null;
- filSelectField(curSlider);
- return false;
- }
- document.onmousemove = set;
- return set(e);
- }
-
- return [c, n, s, function (c) { return [ c.fil_val ]; }, set ];
-}
-
-function filFSelect(c, n, lines, opts) {
- var s = tag('select', {onfocus: filSelectField, onchange: filSerialize, 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]));
- else {
- var g = tag('optgroup', {label: opts[i][0]});
- for(var j=1; j<opts[i].length; j++)
- g.appendChild(tag('option', {name: opts[i][j][0]}, opts[i][j][1]));
- s.appendChild(g);
- }
- }
- return [ c, lines > 1 ? [ n, mt('_js_fil_boolor') ] : n, s,
- function (c) {
- var l = [];
- for(var i=0; i<c.options.length; i++)
- if(c.options[i].selected)
- l.push(c.options[i].name);
- return l;
- },
- function (c, f) {
- for(var i=0; i<c.options.length; i++) {
- for(var j=0; j<f.length; j++)
- if(c.options[i].name+'' == f[j]+'') // beware of JS logic: 0 == '', but '0' != ''
- break;
- c.options[i].selected = j != f.length;
- }
- }
- ];
-}
-
-function filFOptions(c, n, opts) {
- var p = tag('p', {'class':'opts', fil_val:opts[0][0]});
- var sel = function (e) {
- var o = typeof e == 'string' ? e : this.fil_n;
- var l = byName(p, 'a');
- for(var i=0; i<l.length; i++)
- setClass(l[i], 'tsel', l[i].fil_n+'' == o+'');
- p.fil_val = o;
- if(typeof e != 'string')
- filSelectField(p);
- return false
- };
- for(var i=0; i<opts.length; i++) {
- p.appendChild(tag('a', {href:'#', fil_n: opts[i][0], onclick:sel}, opts[i][1]));
- if(i<opts.length-1)
- p.appendChild(tag('b', '|'));
- }
- return [ c, n, p,
- function (c) { return [ c.fil_val ] },
- function (c, v) { sel(v[0]) }
- ];
-}
-
-function filFTagInput(name, label, type) {
- var src = type=='tag' ? '/xml/tags.xml' : '/xml/traits.xml';
-
- var visible = false;
- var remove = function() {
- ;
- };
- var addtag = function(ul, id, name, group) {
- ul.appendChild(
- tag('li', { fil_id: id },
- type=='trait' && group ? tag('b', {'class':'grayedout'}, group+' / ') : null,
- type=='tag' ? tag('a', {href:'/g'+id}, name||'g'+id) : tag('a', {href:'/i'+id}, name||'i'+id),
- ' (', tag('a', {href:'#',
- onclick:function () {
- // a -> li -> ul -> div
- var ul = this.parentNode.parentNode;
- ul.removeChild(this.parentNode);
- filSelectField(ul.parentNode);
- return false
- }
- }, mt('_js_remove')), ')'
- ));
- }
- var fetch = function(c) {
- var v = c.fil_val;
- var ul = byName(c, 'ul')[0];
- var txt = byName(c, 'input')[0];
- if(v == null)
- return;
- if(!v[0]) {
- setText(ul, '');
- txt.disabled = false;
- txt.value = '';
- return;
- }
- if(!visible)
- setText(ul, '');
- var q = [];
- for(var i=0; i<v.length; i++) {
- q.push('id='+v[i]);
- if(!visible)
- addtag(ul, v[i]);
- }
- txt.value = mt('_js_loading');
- txt.disabled = true;
- if(visible)
- ajax(src+'?'+q.join(';'), function (hr) {
- var l = [];
- var items = hr.responseXML.getElementsByTagName('item');
- setText(ul, '');
- for(var i=0; i<items.length; i++)
- addtag(ul, items[i].getAttribute('id'), items[i].firstChild.nodeValue, items[i].getAttribute('groupname'));
- txt.value = '';
- txt.disabled = false;
- c.fil_val = null;
- }, 1);
- };
- var input = tag('input', {type:'text', 'class':'text', style:'width:300px', onfocus:filSelectField});
- var list = tag('ul', null);
- dsInit(input, src+'?q=',
- function(item, tr) {
- var g = item.getAttribute('groupname');
- tr.appendChild(tag('td',
- type=='trait' && g ? tag('b', {'class':'grayedout'}, g+' / ') : null,
- shorten(item.firstChild.nodeValue, 40), // l10n /_js_ds_(tag|trait)_(meta|mod)/
- item.getAttribute('meta') == 'yes' ? tag('b', {'class': 'grayedout'}, ' '+mt('_js_ds_'+type+'_meta')) : null,
- item.getAttribute('state') == 0 ? tag('b', {'class': 'grayedout'}, ' '+mt('_js_ds_'+type+'_mod')) : null
- ));
- },
- function(item, obj) {
- if(item.getAttribute('meta') == 'yes') // l10n /_js_ds_(tag|trait)_nometa/
- alert(mt('_js_ds_'+type+'_nometa'));
- else {
- addtag(byName(obj.parentNode, 'ul')[0], item.getAttribute('id'), item.firstChild.nodeValue, item.getAttribute('groupname'));
- filSelectField(obj);
- }
- return '';
- },
- function(o) { filSelectField(o); false }
- );
-
- return [
- name, label, tag('div', list, input),
- function(c) {
- var v = []; var l = byName(c, 'li');
- for(var i=0; i<l.length; i++)
- v.push(l[i].fil_id);
- return v;
- },
- function(c,v) { c.fil_val = v; fetch(c) },
- function(c) { visible = true; fetch(c); }
- ];
-}
-
-function filChars() {
- var gend = genders;
- for(var i=0; i<gend.length; i++) // l10n /_gender_.+/
- gend[i] = [ gend[i], mt('_gender_'+gend[i]) ];
- var bloodt = blood_types;
- for(var i=0; i<bloodt.length; i++) // l10n /_bloodt_.+/
- bloodt[i] = [ bloodt[i], bloodt[i] == 'unknown' ? mt('_unknown') : mt('_bloodt_'+bloodt[i]) ];
- var roles = char_roles;
- for(var i=0; i<roles.length; i++) // l10n /_charrole_.+/
- roles[i] = [ roles[i], mt('_charrole_'+roles[i]) ];
-
- var ontraitpage = location.pathname.indexOf('/c/') < 0;
-
- return [
- mt('_charb_fil_title'),
- [ mt('_charb_general'),
- filFSelect('gender', mt('_charb_gender'), 4, gend),
- filFSelect('bloodt', mt('_charb_bloodt'), 5, bloodt),
- '',
- filFSlider('bust_min', mt('_charb_bust_min'), 20, 120, 40, 'cm'),
- filFSlider('bust_max', mt('_charb_bust_max'), 20, 120, 100, 'cm'),
- filFSlider('waist_min', mt('_charb_waist_min'), 20, 120, 40, 'cm'),
- filFSlider('waist_max', mt('_charb_waist_max'), 20, 120, 100, 'cm'),
- filFSlider('hip_min', mt('_charb_hip_min'), 20, 120, 40, 'cm'),
- filFSlider('hip_max', mt('_charb_hip_max'), 20, 120, 100, 'cm'),
- '',
- filFSlider('height_min', mt('_charb_height_min'), 0, 300, 60, 'cm'),
- filFSlider('height_max', mt('_charb_height_max'), 0, 300, 240, 'cm'),
- filFSlider('weight_min', mt('_charb_weight_min'), 0, 400, 80, 'kg'),
- filFSlider('weight_max', mt('_charb_weight_max'), 0, 400, 320, 'kg'),
- ],
- ontraitpage ? [ mt('_charb_traits'),
- [ '', ' ', tag(mt('_charb_traitnothere')) ],
- ] : [ mt('_charb_traits'),
- [ '', ' ', tag(mt('_js_fil_booland')) ],
- filFTagInput('trait_inc', mt('_charb_traitinc'), 'trait'),
- filFTagInput('trait_exc', mt('_charb_traitexc'), 'trait'),
- filFOptions('tagspoil', ' ', [[0, mt('_spoilset_0')],[1, mt('_spoilset_1')],[2, mt('_spoilset_2')]]),
- ],
- [ mt('_charb_roles'), filFSelect('role', mt('_charb_roles'), 4, roles) ]
- ];
-}
-
-function filReleases() {
- var types = release_types;
- for(var i=0; i<types.length; i++) // l10n /_rtype_.+/
- types[i] = [ types[i], mt('_rtype_'+types[i]) ];
- var ages = age_ratings;
- for(var i=0; i<ages.length; i++)
- ages[i] = [ ages[i], ages[i] == -1 ? mt('_unknown') : ages[i] == 0 ? mt('_minage_all') : mt('_minage_age', ages[i]) ];
- var lang = languages;
- for(var i=0; i<lang.length; i++) // l10n /_lang_.+/
- lang[i] = [ lang[i], mt('_lang_'+lang[i]) ];
- var plat = platforms;
- for(var i=0; i<plat.length; i++) // l10n /_plat_.+/
- plat[i] = [ plat[i], mt('_plat_'+plat[i]) ];
- plat.splice(0, 0, [ 'unk', mt('_unknown') ]);
- var med = media;
- for(var i=0; i<med.length; i++) // l10n /_med_.+/
- med[i] = [ med[i], mt('_med_'+med[i]) ];
- med.splice(0, 0, [ 'unk', mt('_unknown') ]);
- var voi = voiced;
- for(var i=0; i<voi.length; i++) // l10n /_voiced_.+/
- voi[i] = [ voi[i], voi[i] == 0 ? mt('_unknown') : mt('_voiced_'+voi[i]) ];
- var ani = animated;
- for(var i=0; i<ani.length; i++) // l10n /_animated_.+/
- ani[i] = [ ani[i], ani[i] == 0 ? mt('_unknown') : mt('_animated_'+ani[i]) ];
- return [
- mt('_rbrowse_fil_title'),
- [ mt('_rbrowse_general'),
- filFOptions('type', mt('_rbrowse_type'), types),
- 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] }, dateSet ],
- [ 'date_before', mt('_rbrowse_datebefore'), dateLoad(null, filSelectField), function (c) { return [c.date_val] }, dateSet ],
- filFOptions('released', mt('_rbrowse_released'),[ [1, mt('_rbrowse_released_yes')], [0, mt('_rbrowse_released_no')] ])
- ],
- [ mt('_rbrowse_minage'), filFSelect('minage', mt('_rbrowse_minage'), 15, ages) ],
- [ mt('_rbrowse_language'), filFSelect('lang', mt('_rbrowse_language'), 20, lang) ],
- [ mt('_rbrowse_olang'), filFSelect('olang', mt('_rbrowse_olang'), 20, lang) ],
- [ mt('_rbrowse_resolution'), filFSelect('resolution', mt('_rbrowse_resolution'), 15, resolutions) ],
- [ mt('_rbrowse_platform'), filFSelect('plat', mt('_rbrowse_platform'), 20, plat) ],
- [ mt('_rbrowse_medium'), filFSelect('med', mt('_rbrowse_medium'), 10, med) ],
- [ mt('_rbrowse_voiced'), filFSelect('voiced', mt('_rbrowse_voiced'), 5, voi) ],
- [ mt('_rbrowse_animation'),
- filFSelect('ani_story', mt('_rbrowse_ani_story'), 5, ani),
- filFSelect('ani_ero', mt('_rbrowse_ani_ero'), 5, ani)
- ]
- ];
-}
-
-function filVN() {
- var lang = languages;
- for(var i=0; i<lang.length; i++) // l10n /_lang_.+/
- lang[i] = [ lang[i], mt('_lang_'+lang[i]) ];
- var plat = platforms;
- for(var i=0; i<plat.length; i++) // l10n /_plat_.+/
- plat[i] = [ plat[i], mt('_plat_'+plat[i]) ];
- var len = vn_lengths;
- for(var i=0; i<len.length; i++) // l10n /_vnlength_.+/
- len[i] = [ len[i], len[i] == 0 ? mt('_unknown') : mt('_vnlength_'+len[i]) ];
-
- var ontagpage = location.pathname.indexOf('/v/') < 0;
-
- return [
- mt('_vnbrowse_fil_title'),
- [ mt('_vnbrowse_general'),
- filFSelect( 'length', mt('_vnbrowse_length'), 6, len),
- filFOptions('hasani', mt('_vnbrowse_anime'), [[1, mt('_vnbrowse_anime_yes')],[0, mt('_vnbrowse_anime_no')]])
- ],
- ontagpage ? [ mt('_vnbrowse_tags'),
- [ '', ' ', tag(mt('_vnbrowse_tagnothere')) ],
- ] : [ mt('_vnbrowse_tags'),
- [ '', ' ', tag(mt('_js_fil_booland')) ],
- [ '', ' ', byId('pref_code') ? tag(mt('_vnbrowse_tagactive')) : null ],
- filFTagInput('tag_inc', mt('_vnbrowse_taginc'), 'tag'),
- filFTagInput('tag_exc', mt('_vnbrowse_tagexc'), 'tag'),
- filFOptions('tagspoil', ' ', [[0, mt('_spoilset_0')],[1, mt('_spoilset_1')],[2, mt('_spoilset_2')]])
- ],
- [ mt('_vnbrowse_language'), filFSelect('lang', mt('_vnbrowse_language'), 20, lang) ],
- [ mt('_vnbrowse_olang'), filFSelect('olang',mt('_vnbrowse_olang'), 20, lang) ],
- [ mt('_vnbrowse_platform'), filFSelect('plat', mt('_vnbrowse_platform'), 20, plat) ],
- !byId('pref_code') ? null : [
- mt('_vnbrowse_ul'),
- filFOptions('ul_notblack', mt('_vnbrowse_ul_notblack'), [[1, mt('_vnbrowse_ul_notblackmsg')]]),
- filFOptions('ul_onwish', mt('_vnbrowse_ul_onwish'), [[0, mt('_vnbrowse_ul_onwishno')],[1, mt('_vnbrowse_ul_onwishyes')]]),
- filFOptions('ul_voted', mt('_vnbrowse_ul_voted'), [[0, mt('_vnbrowse_ul_votedno')], [1, mt('_vnbrowse_ul_votedyes') ]]),
- filFOptions('ul_onlist', mt('_vnbrowse_ul_onlist'), [[0, mt('_vnbrowse_ul_onlistno')],[1, mt('_vnbrowse_ul_onlistyes')]])
- ],
- ];
-}
-
-function filStaff() {
- var gend = [
- ['unknown', mt('_gender_unknown')],
- ['m', mt('_gender_m')],
- ['f', mt('_gender_f')],
- ];
- var roles = [];
- for(var i=0; i<staff_roles.length; i++) { // l10n /_credit_.+/
- if(staff_roles[i] == 'staff') {
- roles.push(['seiyuu', mt('_credit_seiyuu')]);
- roles.push(['staff', mt('_credit_other')]);
- } else
- roles.push([ staff_roles[i], mt('_credit_'+staff_roles[i]) ]);
- }
- return [
- mt('_sbrowse_fil_title'),
- [ mt('_sbrowse_general'),
- filFOptions('truename', mt('_sbrowse_names'), [[1, mt('_sbrowse_names_primary')],[0, mt('_sbrowse_names_all')]]),
- filFSelect('role', mt('_sbrowse_roles'), roles.length, roles),
- '',
- filFSelect('gender', mt('_sbrowse_gender'), gend.length, gend),
- ]
- ];
-}
-
-if(byId('filselect'))
- filLoad();
-
-
-
-
-/* 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 == -2)
- s = prompt(mt('_vnpage_uopt_othervote'), '');
- if(!s || s == -3)
- return;
- if(s != -1 && (!s.match(/^([1-9]|10)([\.,][0-9])?$/) || s > 10 || s < 1)) {
- alert(mt('_vnpage_uopt_invvote'));
- this.selectedIndex = 0;
- return;
- }
- s = s.replace(',', '.');
- if(s == 1 && !confirm(mt('_vnpage_uopt_1vote')))
- return;
- if(s == 10 && !confirm(mt('_vnpage_uopt_10vote')))
- return;
- if(s > 0 || s == -1)
- location.href = location.href.replace(/#.*/, '').replace(/\/(chars|staff)/, '').replace(/(v\d+)\.\d+/, '$1')+'/vote?formcode='+this.name+';v='+s;
- };
-}
-
-// Advanced search (/v/*)
-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;
- };
-}
-
-// 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 = byClass(byId('screenshots'), 'a', 'scrlnk');
- for(var i=0; i<l.length; i++) {
- if(hasClass(l[i], 'nsfw')) {
- var hidden = !hasClass(l[i], 'hidden');
- setClass(l[i], 'hidden', hidden);
- 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(/#.*/, '').replace(/\/(chars|staff)/, '').replace(/\.[0-9]+/, '')
- +'/wish?formcode='+this.name+';s='+this.options[this.selectedIndex].value;
- };
-}
-
-// Release & VN list dropdown box (/r+ and /v+)
-if(byId('listsel')) {
- byId('listsel').onchange = function() {
- if(this.selectedIndex != 0)
- location.href = location.href.replace(/#.*/, '').replace(/\/(chars|staff)/, '').replace(/\.[0-9]+/, '')
- +'/list?formcode='+this.name+';e='+this.options[this.selectedIndex].value+';ref='+encodeURIComponent(location.pathname+location.search);
- };
-}
-
-// Notification list onclick
-if(byId('notifies')) {
- var l = byClass(byId('notifies'), 'td', 'clickable');
- for(var i=0; i<l.length; i++)
- l[i].onclick = function() {
- var baseurl = location.href.replace(/\/u([0-9]+)\/notifies.*$/, '/u$1/notify/');
- location.href = baseurl + this.id.replace(/notify_/, '');
- };
-}
-
-// 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 dropdown box (/u+/wish and /u+/votes)
-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();
- };
-}
-
-// collapse/expand row groups (/u+/list)
-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);
- var sel = byName(l[i], 'input')[0];
- if(sel) setClass(sel, 'hidden', allhid);
- }
- setText(byId('expandall'), allhid ? collapsed_icon : expanded_icon);
- for(var i=0; i<heads.length; i++)
- setText(heads[i], 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);
- var sel = byName(l[i], 'input')[0];
- if(sel) setClass(sel, 'hidden', hid);
- }
- setText(this, hid ? collapsed_icon : expanded_icon);
- };
- for(var i=0; i<heads.length; i++)
- heads[i].onclick = singletoggle;
-}
-
-
-// char spoil/sexual preferences
-if(byId('charops')) { (function() {
- var k = byClass('charspoil');
- var h = byName(byId('charops'), 'a');
- var t = byClass('table', 'stripe');
- var spoil;
- var sexual;
- // Fixes the commas between trait names and the hidden status of the entire row
- var fixrow = function(c) {
- var l = byName(byName(c, 'td')[1], 'span');
- var first = 1;
- for(var i=0; i<l.length; i+=2)
- if(!hasClass(l[i], 'hidden')) {
- setClass(l[i+1], 'hidden', first);
- first = 0;
- }
- setClass(c, 'hidden', first);
- };
- var restripe = function() {
- for(var i=0; i<t.length; i++) {
- var b = byName(t[i], 'tbody');
- if(!b.length)
- continue;
- setClass(t[i], 'stripe', false);
- var r = 1;
- var rows = byName(b[0], 'tr');
- for(var j=0; j<rows.length; j++) {
- if(hasClass(rows[j], 'traitrow'))
- fixrow(rows[j]);
- if(!hasClass(rows[j], 'nostripe') && !hasClass(rows[j], 'hidden'))
- setClass(rows[j], 'odd', r++&1);
- }
- }
- };
- var setall = function() {
- for(var i=0; i<k.length; i++)
- setClass(k[i], 'hidden',
- !sexual && hasClass(k[i], 'sexual') ? true :
- hasClass(k[i], 'charspoil_0') ? false :
- hasClass(k[i], 'charspoil_-1') ? spoil > 1 :
- hasClass(k[i], 'charspoil_1') ? spoil < 1 : spoil < 2);
- for(var i=0; i<3; i++)
- setClass(h[i], 'sel', spoil == i);
- if(h[3])
- setClass(h[3], 'sel', sexual);
- if(k.length)
- restripe();
- return false;
- };
- for(var i=0; i<3; i++) {
- h[i].num = i;
- h[i].onclick = function() {
- spoil = this.num;
- return setall();
- };
- if(hasClass(h[i], 'sel'))
- spoil = i;
- };
- if(h[3]) {
- h[3].onclick = function() {
- sexual = !sexual;
- return setall();
- };
- sexual = hasClass(h[3], 'sel');
- }
- setall();
-})(); }
-
-
-// mouse-over price information / disclaimer
-if(byId('buynow')) {
- var l = byClass(byId('buynow'), 'acronym', 'pricenote');
- for(var i=0; i<l.length; i++) {
- l[i].buynow_last = l[i].title;
- l[i].title = null;
- ddInit(l[i], 'bottom', function(acr) {
- return tag('p', {onmouseover:ddHide, style:'padding: 3px'},
- acr.buynow_last, tag('br', null),
- '* The displayed price only serves as an indication and',
- tag('br', null), 'usually excludes shipping. Actual price may differ.'
- );
- });
- }
-}
-
-
-// set note input box (/u+/list)
-if(byId('not') && byId('vns'))
- byId('vns').onchange = function () {
- if(this.options[this.selectedIndex].value == 999)
- byId('not').value = prompt(mt('_rlist_setnote_prompt'), '');
- return true;
- };
-
-
-// expand/collapse release listing (/p+)
-if(byId('expandprodrel')) {
- var lnk = byId('expandprodrel');
- setexpand = function() {
- var exp = !(getCookie('prodrelexpand') == 1);
- setText(lnk, exp ? mt('_js_collapse') : mt('_js_expand'));
- setClass(byId('prodrel'), 'collapse', !exp);
- };
- setexpand();
- lnk.onclick = function () {
- setCookie('prodrelexpand', getCookie('prodrelexpand') == 1 ? 0 : 1);
- setexpand();
- return false;
- };
-}
-
-// Language selector
-if(byId('lang_select')) {
- var d = byId('lang_select');
- var curlang = byName(d, 'acronym')[0].className.substr(11, 2);
- 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[0]}, ' ');
- lst.appendChild(tag('li', {'class':'lang_selector'}, curlang == ln[0]
- ? tag('i', icon, mt('_lang_'+ln[0]))
- : tag('a', {href:'/setlang?lang='+ln[0]+';ref='+encodeURIComponent(location.pathname+location.search)}, icon, ln[1])
- ));
- }
- return lst;
- });
- d.onclick = function() {return false};
-}
-
-// "check all" checkbox
-{
- var f = function() {
- var l = byName('input');
- for(var i=0; i<l.length; i++)
- if(l[i].type == this.type && l[i].name == this.name && !hasClass(l[i], 'hidden'))
- l[i].checked = this.checked;
- };
- var l = byClass('input', 'checkall');
- for(var i=0; i<l.length; i++)
- if(l[i].type == 'checkbox')
- l[i].onclick = f;
-}
-
-// search tabs
-if(byId('searchtabs')) {
- var f = function() {
- var str = byId('q').value;
- if(str.length > 1) {
- this.href = this.href.split('?')[0];
- if(this.href.indexOf('/g') >= 0 || this.href.indexOf('/i') >= 0)
- this.href += '/list';
- this.href += '?q=' + encodeURIComponent(str);
- }
- return true;
- };
- var l = byName(byId('searchtabs'), 'a');
- for(var i=0; i<l.length; i++)
- l[i].onclick = f;
-}
-
-// 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 a9ff0813..d88d404b 100644
--- a/data/style.css
+++ b/data/style.css
@@ -333,7 +333,7 @@ div.vnimg i { display: block; width: 100%; text-align: center;
div.vnimg p { text-align: center; padding: 0px; margin: 0; }
.vndesc h2 { margin: 5px 0 0 0; }
.vndesc p { padding: 0 0 0 5px; }
-p#nsfw_hid { display: none; cursor: pointer; }
+p#nsfw_hid { display: block; cursor: pointer; }
div.vndetails table { float: left; width: 530px; }
div.vndetails table td.key { width: 80px; }
div.vndetails table dt { float: left; font-style: italic; }
diff --git a/lib/VNDB/Handler/Releases.pm b/lib/VNDB/Handler/Releases.pm
index 805329af..35cf824c 100644
--- a/lib/VNDB/Handler/Releases.pm
+++ b/lib/VNDB/Handler/Releases.pm
@@ -433,12 +433,7 @@ sub _form {
end;
h2 mt '_redit_form_media';
- div id => 'media_div';
- Select;
- option value => $_, class => $self->{media}{$_} ? 'qty' : 'noqty', mt "_med_$_", 1
- for (sort keys %{$self->{media}});
- end;
- end;
+ div id => 'media_div', '';
}],
],
diff --git a/lib/VNDB/Handler/ULists.pm b/lib/VNDB/Handler/ULists.pm
index 92383e4e..3fd7d41d 100644
--- a/lib/VNDB/Handler/ULists.pm
+++ b/lib/VNDB/Handler/ULists.pm
@@ -27,14 +27,15 @@ sub vnvote {
return if !$self->authCheckCode;
my $f = $self->formValidate(
- { get => 'v', regex => qr/^(-1|([1-9]|10)(\.[0-9])?)$/ }
+ { get => 'v', regex => qr/^(-1|([1-9]|10)(\.[0-9])?)$/ },
+ { get => 'ref', required => 0, default => "/v$id" }
);
return $self->resNotFound if $f->{_err} || ($f->{v} != -1 && ($f->{v} > 10 || $f->{v} < 1));
$self->dbVoteDel($uid, $id) if $f->{v} == -1;
$self->dbVoteAdd($id, $uid, $f->{v}*10) if $f->{v} > 0;
- $self->resRedirect('/v'.$id, 'temp');
+ $self->resRedirect($f->{ref}, 'temp');
}
@@ -46,14 +47,15 @@ sub vnwish {
return if !$self->authCheckCode;
my $f = $self->formValidate(
- { get => 's', enum => [ -1, @{$self->{wishlist_status}} ] }
+ { get => 's', enum => [ -1, @{$self->{wishlist_status}} ] },
+ { get => 'ref', required => 0, default => "/v$id" }
);
return $self->resNotFound if $f->{_err};
$self->dbWishListDel($uid, $id) if $f->{s} == -1;
$self->dbWishListAdd($id, $uid, $f->{s}) if $f->{s} != -1;
- $self->resRedirect('/v'.$id, 'temp');
+ $self->resRedirect($f->{ref}, 'temp');
}
diff --git a/lib/VNDB/Handler/VNEdit.pm b/lib/VNDB/Handler/VNEdit.pm
index 8a85b989..e7350d16 100644
--- a/lib/VNDB/Handler/VNEdit.pm
+++ b/lib/VNDB/Handler/VNEdit.pm
@@ -529,6 +529,8 @@ sub scrxml {
if(!$id) {
my $im = Image::Magick->new;
$im->BlobToImage($imgdata);
+ $im->Set(background => '#000000');
+ $im->Set(alpha => 'Remove');
$im->Set(magick => 'JPEG');
$im->Set(quality => 90);
($ow, $oh) = ($im->Get('width'), $im->Get('height'));
diff --git a/lib/VNDB/Handler/VNPage.pm b/lib/VNDB/Handler/VNPage.pm
index f4b4b6e5..1f6a553b 100644
--- a/lib/VNDB/Handler/VNPage.pm
+++ b/lib/VNDB/Handler/VNPage.pm
@@ -361,12 +361,12 @@ sub page {
if(!$v->{image}) {
p mt '_vnpage_noimg';
} else {
- p $v->{img_nsfw} ? (id => 'nsfw_hid', style => $self->authPref('show_nsfw') ? 'display: block' : '') : ();
+ p $v->{img_nsfw} ? (id => 'nsfw_hid', $self->authPref('show_nsfw') ? () : (class => 'hidden')) : ();
img src => imgurl(cv => $v->{image}), alt => $v->{title};
i mt '_vnpage_imgnsfw_foot' if $v->{img_nsfw};
end;
if($v->{img_nsfw}) {
- p id => 'nsfw_show', $self->authPref('show_nsfw') ? (style => 'display: none') : ();
+ p id => 'nsfw_show', $self->authPref('show_nsfw') ? (class => 'hidden') : ();
txt mt('_vnpage_imgnsfw_msg');
br; br;
a href => '#', mt '_vnpage_imgnsfw_show';
diff --git a/util/jsgen.pl b/util/jsgen.pl
index eb7d2a8a..72c78d9f 100755
--- a/util/jsgen.pl
+++ b/util/jsgen.pl
@@ -6,7 +6,7 @@ use strict;
use warnings;
use Encode 'encode_utf8';
use Cwd 'abs_path';
-eval { require JavaScript::Minifier::XS; };
+use JSON::XS;
our($ROOT, %S, %O);
BEGIN { ($ROOT = abs_path $0) =~ s{/util/jsgen\.pl$}{}; }
@@ -36,6 +36,40 @@ sub l10n_load {
}
+# Remove formatting codes from L10N strings that the Javascript mt() does not support.
+# [quant,_n,x,..] -> x
+# [index,_n,x,..] -> x
+# [_n] is supported by Javascript mt(). It is left alone if no arguments are
+# given, otherwise it is replaced. All other codes result in an error.
+sub l10nstr {
+ my($lang, $key, @args) = @_;
+ local $_ = $lang{$lang}{$key} || $lang{'en'}{$key} || '';
+
+ # Simplify parsing
+ s/~\[/JSGEN_QBEGIN/g;
+ s/~]/JSGEN_QENDBR/g;
+ s/~,/JSGEN_QCOMMA/g;
+
+ # Replace quant/index
+ s/\[(?:quant|index),_[0-9]+,([^,\]]*)[^\]]*\]/$1/g;
+
+ # Replace [_n]
+ for my $i (0..$#args) {
+ my $v = $i+1;
+ s/\[_$v\]/$args[$i]/g;
+ }
+
+ # Check for unhandled codes
+ die "Unsupported formatting code in $lang:$key\n" if /\[[^_]/;
+
+ # Convert back
+ s/JSGEN_QBEGIN/~[/g;
+ s/JSGEN_QENDBR/~]/g;
+ s/JSGEN_QCOMMA/,/g; # No need to escape, at this point there are no codes with arguments
+ $_;
+}
+
+
sub l10n {
my($lang, $js) = @_;
@@ -43,96 +77,123 @@ sub l10n {
my @keys;
$js =~ s{(?:mt\('([a-z0-9_]+)'([,\)])|l10n /([^/]+)/)}#
my($k, $s, $q) = ($1, $2, $3);
- my $v = $k ? $lang{$lang}{$k} || $lang{'en'}{$k} : '';
- if($q) { $q ne '<perl regex>' && push @keys, qr/$q/; '' }
- elsif($s eq ')' && $v && $v !~ /[\~\[\]]/) {
+ my $v = $k && l10nstr($lang, $k);
+ if($q) {
+ $q ne '<perl regex>' && push @keys, qr/$q/; ''
+ } elsif($s eq ')' && $v && $v !~ /[\~\[\]]/) {
$v =~ s/"/\\"/g;
$v =~ s/\n/\\n/g;
qq{"$v"}
} else {
- push @keys, quotemeta($k);
+ push @keys, '^'.quotemeta($k).'$';
"mt('$k'$s"
}
#eg;
- # generate header
- my $r = "L10N_STR = {\n";
- my $first = 1;
+ my %keys;
for my $key (sort keys %{$lang{$lang}}) {
next if !grep $key =~ /$_/, @keys;
- $r .= ",\n" if !$first;
- $first = 0;
- my $val = $lang{$lang}{$key} || $lang{'en'}{$key};
- $val =~ s/"/\\"/g;
- $val =~ s/\n/\\n/g;
- $val =~ s/\[index,.+$// if $key =~ /^_vnlength_/; # special casing the VN lengths, since the JS mt() doesn't handle [index]
- $r .= sprintf qq| %s: "%s"|, $key !~ /^[a-z0-9_]+$/ ? "'$key'" : $key, $val;
+ $keys{$key} = l10nstr($lang, $key);
}
- $r .= "\n};";
- return ("$r\n", $js);
+ (\%keys, $js);
}
# screen resolution information, suitable for usage in filFSelect()
sub resolutions {
my $ln = shift;
- my $res_cat = '';
- my $resolutions = '';
- my $comma = 0;
+ my $cat = '';
+ my @r;
+ my $push = \@r;
for my $i (0..$#{$S{resolutions}}) {
my $r = $S{resolutions}[$i];
- if($res_cat ne $r->[1]) {
- my $cat = $r->[1] =~ /^_/ ? $lang{$ln}{$r->[1]}||$lang{'en'}{$r->[1]} : $r->[1];
- $resolutions .= ']' if $res_cat;
- $resolutions .= ",['$cat',";
- $res_cat = $r->[1];
- $comma = 0;
+ if($cat ne $r->[1]) {
+ push @r, [$r->[1] =~ /^_/ ? l10nstr($ln, $r->[1]) : $r->[1]];
+ $cat = $r->[1];
+ $push = $r[$#r];
}
- my $n = $r->[0] =~ /^_/ ? $lang{$ln}{$r->[0]}||$lang{'en'}{$r->[0]} : $r->[0];
- $resolutions .= ($comma ? ',' : '')."[$i,'$n']";
- $comma = 1;
+ my $n = $r->[0] =~ /^_/ ? l10nstr($ln, $r->[0]) : $r->[0];
+ push @$push, [$i, $n];
}
- $resolutions .= ']' if $res_cat;
- return "resolutions = [ $resolutions ];\n";
+ \@r
}
-sub jsgen {
- l10n_load();
- my $common = '';
- $common .= sprintf "rlist_status = [ %s ];\n", join ', ', map qq{"$_"}, @{$S{rlist_status}};
- $common .= sprintf "cookie_prefix = '%s';\n", $O{cookie_prefix};
- $common .= sprintf "age_ratings = [ %s ];\n", join ',', @{$S{age_ratings}};
- $common .= sprintf "languages = [ %s ];\n", join ', ', map qq{"$_"}, @{$S{languages}};
- $common .= sprintf "platforms = [ %s ];\n", join ', ', map qq{"$_"}, @{$S{platforms}};
- $common .= sprintf "char_roles = [ %s ];\n", join ', ', map qq{"$_"}, @{$S{char_roles}};
- $common .= sprintf "media = [ %s ];\n", join ', ', map qq{"$_"}, sort keys %{$S{media}};
- $common .= sprintf "release_types = [ %s ];\n", join ', ', map qq{"$_"}, @{$S{release_types}};
- $common .= sprintf "animated = [ %s ];\n", join ', ', @{$S{animated}};
- $common .= sprintf "voiced = [ %s ];\n", join ', ', @{$S{voiced}};
- $common .= sprintf "vn_lengths = [ %s ];\n", join ', ', @{$S{vn_lengths}};
- $common .= sprintf "blood_types = [ %s ];\n", join ', ', map qq{"$_"}, @{$S{blood_types}};
- $common .= sprintf "genders = [ %s ];\n", join ', ', map qq{"$_"}, @{$S{genders}};
- $common .= sprintf "char_roles = [ %s ];\n", join ', ', map qq{"$_"}, @{$S{char_roles}};
- $common .= sprintf "staff_roles = [ %s ];\n", join ', ', map qq{"$_"}, @{$S{staff_roles}};
- $common .= sprintf "L10N_LANG = [ %s ];\n", join(', ', map
- sprintf('["%s","%s"]', $_, $lang{$_}{"_lang_$_"}||$lang{en}{"_lang_$_"}),
- VNDB::L10N::languages());
-
- open my $JS, '<:utf8', "$ROOT/data/script.js" or die $!;
- my $js .= join '', <$JS>;
+sub vars {
+ my($lang, $l10n) = @_;
+ my %vars = (
+ rlist_status => [ map l10nstr($lang, $_?"_rlist_status_$_":'_unknown'), @{$S{rlist_status}} ],
+ cookie_prefix => $O{cookie_prefix},
+ age_ratings => [ map [ $_, l10nstr($lang, $_ == -1 ? ('_unknown') : $_ == 0 ? ('_minage_all') : ('_minage_age', $_)) ], @{$S{age_ratings}} ],
+ languages => [ map [ $_, l10nstr($lang, "_lang_$_") ], @{$S{languages}} ],
+ platforms => [ map [ $_, l10nstr($lang, "_plat_$_") ], @{$S{platforms}} ],
+ char_roles => [ map [ $_, l10nstr($lang, "_charrole_$_") ], @{$S{char_roles}} ],
+ media => [ map [ $_, l10nstr($lang, "_med_$_"), $S{media}{$_} ], sort keys %{$S{media}} ],
+ release_types => [ map [ $_, l10nstr($lang, "_rtype_$_") ], @{$S{release_types}} ],
+ animated => [ map [ 1*$_, l10nstr($lang, $_?"_animated_$_":'_unknown' ) ], @{$S{animated}} ],
+ voiced => [ map [ 1*$_, l10nstr($lang, $_?"_voiced_$_":'_unknown' ) ], @{$S{voiced}} ],
+ vn_lengths => [ map [ 1*$_, l10nstr($lang, $_?"_vnlength_$_":'_unknown' ) ], @{$S{vn_lengths}} ],
+ blood_types => [ map [ $_, l10nstr($lang, $_ eq 'unknown' ? '_unknown' : "_bloodt_$_") ], @{$S{blood_types}} ],
+ genders => [ map [ $_, l10nstr($lang, "_gender_$_") ], @{$S{genders}} ],
+ staff_roles => [ map [ $_, l10nstr($lang, "_credit_$_") ], @{$S{staff_roles}} ],
+ resolutions => scalar resolutions($lang),
+ l10n_lang => [ map [ $_, l10nstr($_, "_lang_$_") ], VNDB::L10N::languages() ],
+ l10n_str => $l10n,
+ );
+ JSON::XS->new->encode(\%vars);
+}
+
+
+# Reads main.js and any included files.
+sub readjs {
+ my $f = shift || 'main.js';
+ open my $JS, '<:utf8', "$ROOT/data/js/$f" or die $!;
+ local $/ = undef;
+ local $_ = <$JS>;
close $JS;
+ s{^//include (.+)$}{'(function(){'.readjs($1).'})();'}meg;
+ $_;
+}
+
+
+sub save {
+ my($f, $body) = @_;
+ my $content = encode_utf8($body);
+
+ unlink "$f~";
+ if(!$VNDB::JSGEN{compress}) {
+ open my $F, '>', "$f~" or die $!;
+ print $F $content;
+ close $F;
+
+ } elsif($VNDB::JSGEN{compress} eq 'JavaScript::Minifier::XS') {
+ require JavaScript::Minifier::XS;
+ open my $F, '>', "$f~" or die $!;
+ print $F JavaScript::Minifier::XS::minify($content);
+ close $F;
+
+ } elsif($VNDB::JSGEN{compress} =~ /^\|/) { # External command
+ (my $cmd = $VNDB::JSGEN{compress}) =~ s/^\|//;
+ open my $C, '|-', "$cmd >'$f~'" or die $!;
+ print $C $content;
+ close $C or die $!;
+
+ } else {
+ die "Unrecognized compression option: '$VNDB::JSGEN{compress}'\n";
+ }
+
+ rename "$f~", $f or die $!;
+}
+
+sub jsgen {
+ my $js = readjs 'main.js';
for my $l (VNDB::L10N::languages()) {
- my($head, $body) = l10n($l, $js);
- $head .= resolutions($l);
- # JavaScript::Minifier::XS doesn't correctly handle perl's unicode, so manually encode
- my $content = encode_utf8($head . $common . $body);
- open my $NEWJS, '>', "$ROOT/static/f/js/$l.js" or die $!;
- print $NEWJS $JavaScript::Minifier::XS::VERSION ? JavaScript::Minifier::XS::minify($content) : $content;
- close $NEWJS;
+ my($l10n, $body) = l10n($l, $js);
+ $body =~ s{/\*VARS\*/}{vars($l, $l10n)}eg;
+ save "$ROOT/static/f/js/$l.js", $body;
}
}
+l10n_load;
jsgen;
-