diff options
author | Yorhel <git@yorhel.nl> | 2015-08-17 16:02:40 +0200 |
---|---|---|
committer | Yorhel <git@yorhel.nl> | 2015-08-17 16:02:40 +0200 |
commit | 6f424c8a6765c00c5df7b359ab4c8d409c758738 (patch) | |
tree | 1abebbf903341f50f8da90026425c8e0198d612e | |
parent | e064b8c2a70ab02417a58d2db83bb31fcb59fbcf (diff) | |
parent | c24962392d8959eb912be14b225d2e8a08ae3f54 (diff) |
Merge branch 'jscleanup'
35 files changed, 3544 insertions, 3464 deletions
@@ -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 @@ -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; - |