diff options
author | Yorhel <git@yorhel.nl> | 2011-03-14 13:12:54 +0100 |
---|---|---|
committer | Yorhel <git@yorhel.nl> | 2011-03-14 13:12:54 +0100 |
commit | 17d4f695b114f970c3b3c4c17c4b8915a0356297 (patch) | |
tree | 5e12ab47403fcf7919ebbde2a961c92506331850 | |
parent | 4582a5dce7c0793fe64806d4cba5ab9d386ea878 (diff) |
chardb: Added Char<->VN linking edit interface
Still somewhat quircky, but it works.
-rw-r--r-- | data/global.pl | 1 | ||||
-rw-r--r-- | data/script.js | 207 | ||||
-rw-r--r-- | data/style.css | 12 | ||||
-rw-r--r-- | lib/VNDB/DB/Releases.pm | 2 | ||||
-rw-r--r-- | lib/VNDB/Handler/Chars.pm | 14 | ||||
-rw-r--r-- | lib/VNDB/Handler/Releases.pm | 34 | ||||
-rw-r--r-- | lib/VNDB/Handler/Traits.pm | 2 | ||||
-rwxr-xr-x | util/jsgen.pl | 1 |
8 files changed, 267 insertions, 6 deletions
diff --git a/data/global.pl b/data/global.pl index 3f1ba3b6..5fd2bf85 100644 --- a/data/global.pl +++ b/data/global.pl @@ -108,6 +108,7 @@ our %S = (%S, vnlist_status => [ 0..4 ], blood_types => [qw| unknown a b ab o |], genders => [qw| unknown m f b |], + char_roles => [qw| main primary side appears |], atom_feeds => { # num_entries, title, id announcements => [ 10, 'VNDB Site Announcements', '/t/an' ], changes => [ 25, 'VNDB Recent Changes', '/hist' ], diff --git a/data/script.js b/data/script.js index f7b6eb1b..1aa60efa 100644 --- a/data/script.js +++ b/data/script.js @@ -1,5 +1,6 @@ /* function/attribute prefixes: * ctr -> Character <-> trait linking + * cvn -> Character <-> VN linking * date -> Date selector * dd -> dropdown * ds -> dropdown search @@ -1774,7 +1775,7 @@ function ctrLoad() { 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(); @@ -1786,7 +1787,7 @@ function ctrLoad() { 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 () {}); + }, ctrFormAdd); } function ctrEmpty() { @@ -1884,6 +1885,208 @@ if(byId('traits_tbl')) +/* C H A R A C T E R < - > V N L I N K I N G (/c+/edit) */ + +// TODO: translate! + +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]); + } + }, 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', 'No visual novels selected.'))); +} + +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}, 'add release'), ')') + ) + )); + 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}, 'All / others')); + 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++) + lsel.appendChild(tag('option', {value: char_roles[i], selected:char_roles[i]==role}, 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}, '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('Release already selected.'); + 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('All releases already selected'); + 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('VN already present.')); + 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(); + + + + /* F I L T E R S Y S T E M */ diff --git a/data/style.css b/data/style.css index 27d0aeec..de55a177 100644 --- a/data/style.css +++ b/data/style.css @@ -872,6 +872,18 @@ div.chardetails table td.key { width: 80px; } #jt_box_chare_traits td.tc_name { width: 200px } #jt_box_chare_traits td.tc_name input { width: 280px; } #jt_box_chare_traits td.tc_spoil { width: 80px; } +#jt_box_chare_vns table { margin-bottom: 10px; margin-left: 10px; } +#jt_box_chare_vns h2 { margin: 0 0 3px 0px; } +#jt_box_chare_vns td.tc_vn { font-weight: bold; padding: 5px 0 3px 0 } +#jt_box_chare_vns td.tc_vn i{ font-weight: normal; padding-left: 5px; font-style: normal } +#jt_box_chare_vns td.tc_rel { width: 340px; padding-left: 15px } +#jt_box_chare_vns td.tc_rel select { width: 340px; } +#jt_box_chare_vns td.tc_rol, +#jt_box_chare_vns td.tc_spl, +#jt_box_chare_vns td.tc_rol select, +#jt_box_chare_vns td.tc_spl select { width: 100px } +#jt_box_chare_vns td.tc_del { padding-left: 5px } +#jt_box_chare_vns td.tc_vnadd input { width: 280px } /***** Documentation pages *****/ diff --git a/lib/VNDB/DB/Releases.pm b/lib/VNDB/DB/Releases.pm index 87685ce6..bd2d5bd3 100644 --- a/lib/VNDB/DB/Releases.pm +++ b/lib/VNDB/DB/Releases.pm @@ -24,7 +24,7 @@ sub dbReleaseGet { !$o{id} && !$o{rev} ? ( 'r.hidden = FALSE' => 0 ) : (), $o{id} ? ( 'r.id = ?' => $o{id} ) : (), $o{rev} ? ( 'c.rev = ?' => $o{rev} ) : (), - $o{vid} ? ( 'rv.vid = ?' => $o{vid} ) : (), + $o{vid} ? ( 'rv.vid IN(!l)' => [ ref $o{vid} ? $o{vid} : [$o{vid}] ] ) : (), $o{pid} ? ( 'rp.pid = ?' => $o{pid} ) : (), defined $o{patch} ? ( 'rr.patch = ?' => $o{patch} == 1 ? 1 : 0) : (), defined $o{freeware} ? ( 'rr.freeware = ?' => $o{freeware} == 1 ? 1 : 0) : (), diff --git a/lib/VNDB/Handler/Chars.pm b/lib/VNDB/Handler/Chars.pm index 64e7df2d..fa937915 100644 --- a/lib/VNDB/Handler/Chars.pm +++ b/lib/VNDB/Handler/Chars.pm @@ -141,6 +141,7 @@ sub page { # vns # TODO: handle spoilers! + # TODO: translate! if(@{$r->{vns}}) { my %vns; push @{$vns{$_->{vid}}}, $_ for(sort { !defined($a->{rid})?1:!defined($b->{rid})?-1:$a->{rtitle} cmp $b->{rtitle} } @{$r->{vns}}); @@ -334,7 +335,18 @@ sub edit { ], chare_vns => [ mt('_chare_vns'), - [ input => short => 'vns', name => 'VNs (debug)' ], + [ hidden => short => 'vns' ], + [ static => nolabel => 1, content => sub { + h2 'Selected visual novels'; + table; tbody id => 'vns_tbl'; + Tr id => 'vns_loading'; td colspan => '4', mt('_js_loading'); end; + end; end; + h2 'Add visual novel'; + table; Tr; + td class => 'tc_vnadd'; input id => 'vns_input', type => 'text', class => 'text'; end; + td colspan => 3, ''; + end; end; + }], ]); $self->htmlFooter; } diff --git a/lib/VNDB/Handler/Releases.pm b/lib/VNDB/Handler/Releases.pm index a3ecbd42..159b7c3f 100644 --- a/lib/VNDB/Handler/Releases.pm +++ b/lib/VNDB/Handler/Releases.pm @@ -3,7 +3,7 @@ package VNDB::Handler::Releases; use strict; use warnings; -use TUWF ':html', 'uri_escape'; +use TUWF ':html', ':xml', 'uri_escape'; use VNDB::Func; @@ -13,6 +13,7 @@ TUWF::register( qr{r} => \&browse, qr{r(?:([1-9]\d*)(?:\.([1-9]\d*))?/(edit|copy))} => \&edit, + qr{xml/releases.xml} => \&relxml, ); @@ -590,5 +591,36 @@ sub _fil_compat { } +sub relxml { + my $self = shift; + + my $f = $self->formValidate( + { get => 'v', required => 1, multi => 1, mincount => 1, template => 'int' } + ); + return $self->resNotFound if $f->{_err}; + + my $list = $self->dbReleaseGet(vid => $f->{v}, results => 100, what => 'vn'); + my %vns = map +($_,0), @{$f->{v}}; + for my $r (@$list) { + for my $v (@{$r->{vn}}) { + next if !exists $vns{$v->{vid}}; + $vns{$v->{vid}} = [ $v ] if !$vns{$v->{vid}}; + push @{$vns{$v->{vid}}}, $r; + } + } + $self->resHeader('Content-type' => 'text/xml; charset=UTF-8'); + xml; + tag 'vns'; + for (sort { $a->[0]{title} cmp $b->[0]{title} } values %vns) { + my $v = shift @$_; + tag 'vn', id => $v->{vid}, title => $v->{title}; + tag 'release', id => $_->{id}, lang => join(',', @{$_->{languages}}), $_->{title} + for (@$_); + end; + } + end; +} + + 1; diff --git a/lib/VNDB/Handler/Traits.pm b/lib/VNDB/Handler/Traits.pm index a66721a8..887890b6 100644 --- a/lib/VNDB/Handler/Traits.pm +++ b/lib/VNDB/Handler/Traits.pm @@ -423,7 +423,7 @@ sub traitxml { $self->resHeader('Content-type' => 'text/xml; charset=UTF-8'); xml; - tag 'tags', more => $np ? 'yes' : 'no'; + tag 'traits', more => $np ? 'yes' : 'no'; for(@$list) { tag 'item', id => $_->{id}, meta => $_->{meta} ? 'yes' : 'no', group => $_->{group}||'', groupname => $_->{groupname}||'', state => $_->{state}, $_->{name}; } diff --git a/util/jsgen.pl b/util/jsgen.pl index 112bfb7e..f046fe1a 100755 --- a/util/jsgen.pl +++ b/util/jsgen.pl @@ -105,6 +105,7 @@ sub jsgen { $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}}; |