$c->stash->{template} = 'directory.tt';
}
-=head2 variantgraph
+=head1 AJAX methods for traditions and their properties
- GET /variantgraph/$textid
+=head2 newtradition
+
+ POST /newtradition,
+ { name: <name>,
+ language: <language>,
+ public: <is_public>,
+ file: <fileupload> }
-Returns the variant graph for the text specified at $textid, in SVG form.
+Creates a new tradition belonging to the logged-in user, with the given name
+and the collation given in the uploaded file. The file type is indicated via
+the filename extension (.csv, .txt, .xls, .xlsx, .xml). Returns the ID and
+name of the new tradition.
+
+=cut
+
+sub newtradition :Local :Args(0) {
+ my( $self, $c ) = @_;
+ return _json_error( $c, 403, 'Cannot save a tradition without being logged in' )
+ unless $c->user_exists;
+
+ my $user = $c->user->get_object;
+ # Grab the file upload, check its name/extension, and call the
+ # appropriate parser(s).
+ my $upload = $c->request->upload('file');
+ my $name = $c->request->param('name') || 'Uploaded tradition';
+ my $lang = $c->request->param( 'language' ) || 'Default';
+ my $public = $c->request->param( 'public' ) ? 1 : undef;
+ my( $ext ) = $upload->filename =~ /\.(\w+)$/;
+ my %newopts = (
+ 'name' => $name,
+ 'language' => $lang,
+ 'public' => $public,
+ 'file' => $upload->tempname
+ );
+
+ my $tradition;
+ my $errmsg;
+ if( $ext eq 'xml' ) {
+ # Try the different XML parsing options to see if one works.
+ foreach my $type ( qw/ CollateX CTE TEI / ) {
+ try {
+ $tradition = Text::Tradition->new( %newopts, 'input' => $type );
+ } catch ( Text::Tradition::Error $e ) {
+ $errmsg = $e->message;
+ } catch {
+ $errmsg = "Unexpected parsing error";
+ }
+ last if $tradition;
+ }
+ } elsif( $ext =~ /^(txt|csv|xls(x)?)$/ ) {
+ # If it's Excel we need to pass excel => $ext;
+ # otherwise we need to pass sep_char => [record separator].
+ if( $ext =~ /xls/ ) {
+ $newopts{'excel'} = $ext;
+ } else {
+ $newopts{'sep_char'} = $ext eq 'txt' ? "\t" : ',';
+ }
+ try {
+ $tradition = Text::Tradition->new(
+ %newopts,
+ 'input' => 'Tabular',
+ );
+ } catch ( Text::Tradition::Error $e ) {
+ $errmsg = $e->message;
+ } catch {
+ $errmsg = "Unexpected parsing error";
+ }
+ } else {
+ # Error unless we have a recognized filename extension
+ return _json_error( $c, 500, "Unrecognized file type extension $ext" );
+ }
+
+ # Save the tradition if we have it, and return its data or else the
+ # error that occurred trying to make it.
+ if( $errmsg ) {
+ return _json_error( $c, 500, "Error parsing tradition .$ext file: $errmsg" );
+ } elsif( !$tradition ) {
+ return _json_error( $c, 500, "No error caught but tradition not created" );
+ }
+
+ my $m = $c->model('Directory');
+ $user->add_tradition( $tradition );
+ my $id = $c->model('Directory')->store( $tradition );
+ $c->model('Directory')->store( $user );
+ $c->stash->{'result'} = { 'id' => $id, 'name' => $tradition->name };
+ $c->forward('View::JSON');
+}
+
+=head2 textinfo
+
+ GET /textinfo/$textid
+ POST /textinfo/$textid,
+ { name: $new_name,
+ language: $new_language,
+ public: $is_public,
+ owner: $new_userid } # only admin users can update the owner
+
+Returns information about a particular text.
=cut
-sub variantgraph :Local :Args(1) {
+sub textinfo :Local :Args(1) {
my( $self, $c, $textid ) = @_;
my $tradition = $c->model('Directory')->tradition( $textid );
+ unless( $tradition ) {
+ return _json_error( $c, 500, "No tradition with ID $textid" );
+ }
my $ok = _check_permission( $c, $tradition );
return unless $ok;
+ if( $c->req->method eq 'POST' ) {
+ return _json_error( $c, 403,
+ 'You do not have permission to update this tradition' )
+ unless $ok eq 'full';
+ my $params = $c->request->parameters;
+ # Handle changes to owner-accessible parameters
+ my $m = $c->model('Directory');
+ my $changed;
+ # Handle scalar params
+ foreach my $param ( qw/ name language / ) {
+ if( exists $params->{$param} ) {
+ my $newval = delete $params->{$param};
+ unless( $tradition->$param eq $newval ) {
+ try {
+ $tradition->$param( $newval );
+ } catch {
+ return _json_error( $c, 500, "Error setting $param to $newval" );
+ }
+ $changed = 1;
+ }
+ }
+ }
+ # Handle our boolean
+ if( delete $params->{'public'} ) { # if it's any true value...
+ $tradition->public( 1 );
+ }
+ # Handle ownership changes
+ my $newuser;
+ if( exists $params->{'owner'} ) {
+ # Only admins can update user / owner
+ my $newownerid = delete $params->{'owner'};
+ unless( $tradition->has_user && $tradition->user->id eq $newownerid ) {
+ unless( $c->user->get_object->is_admin ) {
+ return _json_error( $c, 403,
+ "Only admin users can change tradition ownership" );
+ }
+ $newuser = $m->lookup_user( $params->{'owner'} );
+ unless( $newuser ) {
+ return _json_error( $c, 500, "No such user " . $params->{'owner'} );
+ }
+ $newuser->add_tradition( $tradition );
+ $changed = 1;
+ }
+ }
+ # TODO check for rogue parameters
+ if( scalar keys %$params ) {
+ my $rogueparams = join( ', ', keys %$params );
+ return _json_error( $c, 403, "Request parameters $rogueparams not recognized" );
+ }
+ # If we safely got to the end, then write to the database.
+ $m->save( $tradition ) if $changed;
+ $m->save( $newuser ) if $newuser;
+ }
- my $collation = $tradition->collation;
- $c->stash->{'result'} = $collation->as_svg;
- $c->forward('View::SVG');
+ # Now return the current textinfo, whether GET or successful POST.
+ my $textinfo = {
+ textid => $textid,
+ name => $tradition->name,
+ language => $tradition->language,
+ public => $tradition->public,
+ owner => $tradition->user ? $tradition->user->id : undef,
+ witnesses => [ map { $_->sigil } $tradition->witnesses ],
+ };
+ my @stemmasvg = map { $_->as_svg({ size => [ 500, 375 ] }) } $tradition->stemmata;
+ map { $_ =~ s/\n/ /mg } @stemmasvg;
+ $textinfo->{stemmata} = \@stemmasvg;
+ $c->stash->{'result'} = $textinfo;
+ $c->forward('View::JSON');
}
-
-=head2 alignment
- GET /alignment/$textid
+=head2 variantgraph
-Returns an alignment table for the text specified at $textid.
+ GET /variantgraph/$textid
+
+Returns the variant graph for the text specified at $textid, in SVG form.
=cut
-sub alignment :Local :Args(1) {
+sub variantgraph :Local :Args(1) {
my( $self, $c, $textid ) = @_;
my $tradition = $c->model('Directory')->tradition( $textid );
+ unless( $tradition ) {
+ return _json_error( $c, 500, "No tradition with ID $textid" );
+ }
my $ok = _check_permission( $c, $tradition );
return unless $ok;
my $collation = $tradition->collation;
- my $alignment = $collation->alignment_table;
-
- # Turn the table, so that witnesses are by column and the rows
- # are by rank.
- my $wits = [ map { $_->{'witness'} } @{$alignment->{'alignment'}} ];
- my $rows;
- foreach my $i ( 0 .. $alignment->{'length'} - 1 ) {
- my @rankrdgs = map { $_->{'tokens'}->[$i]->{'t'} }
- @{$alignment->{'alignment'}};
- push( @$rows, { 'rank' => $i+1, 'readings' => \@rankrdgs } );
- }
- $c->stash->{'witnesses'} = $wits;
- $c->stash->{'table'} = $rows;
- $c->stash->{'template'} = 'alignment.tt';
+ $c->stash->{'result'} = $collation->as_svg;
+ $c->forward('View::SVG');
}
-
+
=head2 stemma
- GET /stemma/$textid/$stemmaid
- POST /stemma/$textid, { 'dot' => $dot_string }
+ GET /stemma/$textid/$stemmaseq
+ POST /stemma/$textid/$stemmaseq, { 'dot' => $dot_string }
-Returns an SVG representation of the stemma hypothesis for the text. If
-the URL is called with POST and a new dot string, updates the stemma and
-returns the SVG as with GET.
+Returns an SVG representation of the given stemma hypothesis for the text.
+If the URL is called with POST, the stemma at $stemmaseq will be altered
+to reflect the definition in $dot_string. If $stemmaseq is 'n', a new
+stemma will be added.
=cut
-sub stemma :Local :Args {
+sub stemma :Local :Args(2) {
my( $self, $c, $textid, $stemmaid ) = @_;
my $m = $c->model('Directory');
my $tradition = $m->tradition( $textid );
+ unless( $tradition ) {
+ return _json_error( $c, 500, "No tradition with ID $textid" );
+ }
my $ok = _check_permission( $c, $tradition );
return unless $ok;
- $stemmaid = 0 unless defined $stemmaid;
$c->stash->{'result'} = '';
- if( $tradition ) {
- if( $c->req->method eq 'POST' ) {
- # Update the stemma
+ my $stemma;
+ if( $c->req->method eq 'POST' ) {
+ if( $ok eq 'full' ) {
my $dot = $c->request->body_params->{'dot'};
- $tradition->add_stemma( $dot );
+ try {
+ if( $stemmaid eq 'n' ) {
+ # We are adding a new stemma.
+ $stemma = $tradition->add_stemma( 'dot' => $dot );
+ } elsif( $stemmaid < $tradition->stemma_count ) {
+ # We are updating an existing stemma.
+ $stemma = $tradition->stemma( $stemmaid );
+ $stemma->alter_graph( $dot );
+ } else {
+ # Unrecognized stemma ID
+ return _json_error( $c, 500, "No stemma at index $stemmaid, cannot update" );
+ }
+ } catch ( Text::Tradition::Error $e ) {
+ return _json_error( $c, 500, $e->message );
+ }
$m->store( $tradition );
- $stemmaid = scalar( $tradition->stemma_count ) - 1;
+ } else {
+ # No permissions to update the stemma
+ return _json_error( $c, 403,
+ 'You do not have permission to update stemmata for this tradition' );
}
-
- $c->stash->{'result'} = $tradition->stemma_count > $stemmaid
- ? $tradition->stemma( $stemmaid )->as_svg( { size => [ 500, 375 ] } )
- : '';
}
+
+ # For a GET or a successful POST request, return the SVG representation
+ # of the stemma in question, if any.
+ $c->log->debug( "Received Accept header: " . $c->req->header('Accept') );
+ if( !$stemma && $tradition->stemma_count > $stemmaid ) {
+ $stemma = $tradition->stemma( $stemmaid );
+ }
+ $c->stash->{'result'} = $stemma
+ ? $stemma->as_svg( { size => [ 500, 375 ] } ) : '';
$c->forward('View::SVG');
}
=head2 stemmadot
- GET /stemmadot/$textid
+ GET /stemmadot/$textid/$stemmaseq
Returns the 'dot' format representation of the current stemma hypothesis.
=cut
-sub stemmadot :Local :Args(1) {
- my( $self, $c, $textid ) = @_;
+sub stemmadot :Local :Args(2) {
+ my( $self, $c, $textid, $stemmaid ) = @_;
my $m = $c->model('Directory');
my $tradition = $m->tradition( $textid );
+ unless( $tradition ) {
+ return _json_error( $c, 500, "No tradition with ID $textid" );
+ }
my $ok = _check_permission( $c, $tradition );
return unless $ok;
-
- $c->response->body( $tradition->stemma->editable );
- $c->forward('View::Plain');
-}
-
-=head1 AJAX methods for index page
-
-=head2 textinfo
-
- GET /textinfo/$textid
-
-Returns information about a particular text.
-
-=cut
-
-sub textinfo :Local :Args(1) {
- my( $self, $c, $textid ) = @_;
- my $tradition = $c->model('Directory')->tradition( $textid );
- my $ok = _check_permission( $c, $tradition );
- return unless $ok;
-
- # Need text name, witness list, scalar readings, scalar relationships, stemmata
- my $textinfo = {
- textid => $textid,
- traditionname => $tradition->name,
- witnesses => [ map { $_->sigil } $tradition->witnesses ],
- readings => scalar $tradition->collation->readings,
- relationships => scalar $tradition->collation->relationships
- };
- my @stemmasvg = map { $_->as_svg({ size => [ 500, 375 ] }) } $tradition->stemmata;
- map { $_ =~ s/\n/ /mg } @stemmasvg;
- $textinfo->{stemmata} = \@stemmasvg;
- $c->stash->{'result'} = $textinfo;
+ my $stemma = $tradition->stemma( $stemmaid );
+ unless( $stemma ) {
+ return _json_error( $c, 500, "Tradition $textid has no stemma ID $stemmaid" );
+ }
+ # Get the dot and transmute its line breaks to literal '|n'
+ $c->stash->{'result'} = { 'dot' => $stemma->editable( { linesep => '|n' } ) };
$c->forward('View::JSON');
}
-# TODO alter text parameters
+####################
+### Helper functions
+####################
-=head2 new
-
- POST /newtradition { name: <name>, inputfile: <fileupload> }
-
-Creates a new tradition belonging to the logged-in user, according to the detected
-file type. Returns the ID and name of the new tradition.
-
-=cut
-
-sub newtradition :Local :Args(0) {
- my( $self, $c ) = @_;
- if( $c->user_exists ) {
- my $user = $c->user->get_object;
- # Grab the file upload, check its name/extension, and call the
- # appropriate parser(s).
- my $upload = $c->request->upload('file');
- my $name = $c->request->param('name') || 'Uploaded tradition';
- my( $ext ) = $upload->filename =~ /\.(\w+)$/;
- my %newopts = (
- 'name' => $name,
- 'file' => $upload->tempname
- );
- my $tradition;
- my $errmsg;
- if( $ext eq 'xml' ) {
- # Try the different XML parsing options to see if one works.
- foreach my $type ( qw/ CollateX CTE TEI / ) {
- try {
- $tradition = Text::Tradition->new( %newopts, 'input' => $type );
- } catch ( Text::Tradition::Error $e ) {
- $errmsg = $e->message;
- } catch {
- $errmsg = "Unexpected parsing error";
- }
- last if $tradition;
- }
- } elsif( $ext =~ /^(txt|csv|xls(x)?)$/ ) {
- # If it's Excel we need to pass excel => $ext;
- # otherwise we need to pass sep_char => [record separator].
- if( $ext =~ /xls/ ) {
- $newopts{'excel'} = $ext;
- } else {
- $newopts{'sep_char'} = $ext eq 'txt' ? "\t" : ',';
- }
- try {
- $tradition = Text::Tradition->new(
- %newopts,
- 'input' => 'Tabular',
- );
- } catch ( Text::Tradition::Error $e ) {
- $errmsg = $e->message;
- } catch {
- $errmsg = "Unexpected parsing error";
- }
- } elsif( $ext eq 'xlsx' ) {
- $c->stash->{'result'} =
- { 'error' => "Excel XML parsing not supported yet" };
- $c->response->status( 500 );
- } else {
- # Error unless we have a recognized filename extension
- $c->stash->{'result'} =
- { 'error' => "Unrecognized file type extension $ext" };
- $c->response->status( 500 );
- }
-
- # Save the tradition if we have it, and return its data or else the
- # error that occurred trying to make it.
- if( $tradition ) {
- my $m = $c->model('Directory');
- $user->add_tradition( $tradition );
- my $id = $c->model('Directory')->store( $tradition );
- $c->model('Directory')->store( $user );
- $c->stash->{'result'} = { 'id' => $id, 'name' => $tradition->name };
- } else {
- $c->stash->{'result'} =
- { 'error' => "Error parsing tradition .$ext file: $errmsg" };
- $c->response->status( 500 );
- }
- } else {
- $c->stash->{'result'} =
- { 'error' => 'Cannot save a tradition without being logged in' };
- $c->response->status( 403 );
- }
- $c->forward('View::JSON');
-}
-
+# Helper to check what permission, if any, the active user has for
+# the given tradition
sub _check_permission {
my( $c, $tradition ) = @_;
my $user = $c->user_exists ? $c->user->get_object : undef;
return 'readonly' if $tradition->public;
# ...nope. Forbidden!
- $c->response->status( 403 );
- $c->response->body( 'You do not have permission to view this tradition.' );
- $c->detach( 'View::Plain' );
+ return _json_error( $c, 403, 'You do not have permission to view this tradition.' );
+}
+
+# Helper to throw a JSON exception
+sub _json_error {
+ my( $c, $code, $errmsg ) = @_;
+ $c->response->status( $code );
+ $c->stash->{'result'} = { 'error' => $errmsg };
+ $c->forward('View::JSON');
return 0;
}
function loadTradition( textid, textname, editable ) {
selectedTextID = textid;
// First insert the placeholder image and register an error handler
- var basepath = window.location.pathname
- if( basepath.lastIndexOf('/') == basepath.length - 1 ) {
- basepath = basepath.slice( 0, basepath.length - 1)
- };
$('#textinfo_load_status').empty();
$('#stemma_graph').empty();
$('#textinfo_waitbox').show();
- $('#textinfo_container').ajaxError(
- function ( e, jqxhr, settings, exception ) {
- if ( settings.url.indexOf( 'textinfo' ) > -1 ) {
- $('#textinfo_waitbox').hide();
- var msg = "An error occurred: ";
- var msghtml = $('<span>').attr('class', 'error').text(
- msg + jqxhr.status + " " + jqxhr.statusText);
- $("#textinfo_load_status").append( msghtml ).show();
- }
+ $('#textinfo_container').hide().ajaxError(
+ function(event, jqXHR, ajaxSettings, thrownError) {
+ if( ajaxSettings.url.indexOf( 'textinfo' ) > -1 && ajaxSettings.type == 'GET' ) {
+ $('#textinfo_waitbox').hide();
+ $('#textinfo_container').show();
+ display_error( jqXHR, $("#textinfo_load_status") );
}
- );
+ });
+
+ // Hide the functionality that is irrelevant
+ if( editable ) {
+ $('#add_new_stemma').show();
+ $('#edit_current_stemma').show();
+ $('#edit_textinfo').show();
+ } else {
+ $('#add_new_stemma').hide();
+ $('#edit_current_stemma').hide();
+ $('#edit_textinfo').hide();
+ }
+
// Then get and load the actual content.
// TODO: scale #stemma_graph both horizontally and vertically
// TODO: load svgs from SVG.Jquery (to make scaling react in Safari)
$.getJSON( basepath + "/textinfo/" + textid, function (textdata) {
// Add the scalar data
- $('#textinfo_waitbox').hide();
- $('#textinfo_container').show();
- $('.texttitle').empty().append( textdata.traditionname );
- $('#witness_num').empty().append( textdata.witnesses.size );
- $('#witness_list').empty().append( textdata.witnesses.join( ', ' ) );
- $('#reading_num').empty().append( textdata.readings );
- $('#relationship_num').empty().append( textdata.relationships );
- // Add the stemma(ta) and set up the stexaminer button
+ selectedTextInfo = textdata;
+ load_textinfo();
+ // Add the stemma(ta) and set up the stexaminer button
stemmata = textdata.stemmata;
if( stemmata.length ) {
selectedStemmaID = 0;
});
}
-function load_stemma( idx, basepath ) {
+function load_textinfo() {
+ $('#textinfo_waitbox').hide();
+ $('#textinfo_load_status').empty();
+ $('#textinfo_container').show();
+ $('.texttitle').empty().append( selectedTextInfo.name );
+ // Witnesses
+ $('#witness_num').empty().append( selectedTextInfo.witnesses.size );
+ $('#witness_list').empty().append( selectedTextInfo.witnesses.join( ', ' ) );
+ // Who the owner is
+ $('#owner_id').empty().append('no one');
+ if( selectedTextInfo.owner ) {
+ $('#owner_id').empty().append( selectedTextInfo.owner );
+ }
+ // Whether or not it is public
+ $('#not_public').empty();
+ if( selectedTextInfo['public'] == false ) {
+ $('#not_public').append('NOT ');
+ }
+ // What language setting it has, if any
+ $('#marked_language').empty().append('no language set');
+ if( selectedTextInfo.language && selectedTextInfo.language != 'Default' ) {
+ $('#marked_language').empty().append( selectedTextInfo.language );
+ }
+}
+
+function load_stemma( idx ) {
if( idx > -1 ) {
selectedStemmaID = idx;
$('#stemma_graph').empty();
$('#run_stexaminer').attr( 'action', stexpath );
}
}
+
+function display_error( jqXHR, el ) {
+ var errobj = jQuery.parseJSON( jqXHR.responseText );
+ var msg;
+ if( errobj ) {
+ msg = "An error occurred: " + errobj.error;
+ } else {
+ msg = "An error occurred; perhaps the server went down?"
+ }
+ var msghtml = $('<span>').attr('class', 'error').text( msg );
+ $(el).empty().append( msghtml ).show();
+}
\ No newline at end of file
applicationjs = c.uri_for( 'js/componentload.js' )
%]
<script type="text/javascript">
+var basepath = window.location.pathname
+if( basepath.lastIndexOf('/') == basepath.length - 1 ) {
+ basepath = basepath.slice( 0, basepath.length - 1)
+};
var selectedTextID;
+var selectedTextInfo;
var selectedStemmaID = -1;
var stemmata = [];
$('#textinfo_container').hide();
$('#textinfo_waitbox').hide();
refreshDirectory();
+
+ // Set up the textinfo edit dialog
+ $('#textinfo-edit-dialog').dialog({
+ autoOpen: false,
+ height: 200,
+ width: 300,
+ modal: true,
+ buttons: {
+ Save: function (evt) {
+ $(evt.target).button("disable");
+ var requrl = "[% c.uri_for( '/textinfo' ) %]/" + selectedTextID;
+ var reqparam = $('#edit_textinfo').serialize();
+ $.post( requrl, reqparam, function (data) {
+ // Reload the selected text fields
+ selectedTextInfo = data;
+ load_textinfo();
+ // Reenable the button and close the form
+ $(evt.target).button("enable");
+ $('#textinfo-edit-dialog').dialog('close');
+ }, 'json' );
+ },
+ Cancel: function() {
+ $('#textinfo-edit-dialog').dialog('close');
+ }
+ },
+ open: function() {
+ $("#edit_textinfo_status").empty();
+ // Populate the form fields with the current values
+ // edit_(name, language, public, owner)
+ $.each([ 'name', 'language', 'owner' ], function( idx, k ) {
+ var fname = '#edit_' + k;
+ $(fname).val( selectedTextInfo[k] );
+ });
+ if( selectedTextInfo['public'] == true ) {
+ $('#edit_public').attr('checked','true');
+ } else {
+ $('#edit_public').removeAttr('checked');
+ }
+ },
+ }).ajaxError( function(event, jqXHR, ajaxSettings, thrownError) {
+ $(event.target).parent().find('.ui-button').button("enable");
+ if( ajaxSettings.url.indexOf( 'textinfo' ) > -1
+ && ajaxSettings.type == 'POST' ) {
+ display_error( jqXHR, $("#edit_textinfo_status") );
+ }
+ });
+
+
+ // Set up the stemma editor dialog
+ $('#stemma-edit-dialog').dialog({
+ autoOpen: false,
+ height: 700,
+ width: 600,
+ modal: true,
+ buttons: {
+ Save: function (evt) {
+ $(evt.target).button("disable");
+ var stemmaseq = $('#stemmaseq').val();
+ var requrl = "[% c.uri_for( '/stemma' ) %]/" + selectedTextID + "/" + stemmaseq;
+ var reqparam = { 'dot': $('#dot_field').val() };
+ // TODO We need to stash the literal SVG string in stemmata
+ // somehow. Implement accept header on server side to decide
+ // whether to send application/json or application/xml?
+ $.post( requrl, reqparam, function (data) {
+ // We received a stemma SVG string in return.
+ // Update the current stemma sequence number
+ if( stemmaseq == 'n' ) {
+ selectedStemmaID = stemmata.length;
+ } else {
+ selectedStemmaID = stemmaseq;
+ }
+ // Strip the carriage returns from the answer
+ var newsvg = data.replace(/(\r\n|\n|\r)/gm," ");
+ // Stash the answer in our SVG array
+ stemmata[selectedStemmaID] = newsvg;
+ // Display the new stemma
+ load_stemma( selectedStemmaID );
+ // Reenable the button and close the form
+ $(evt.target).button("disable");
+ $('#stemma-edit-dialog').dialog('close');
+ }, 'xml' );
+ },
+ Cancel: function() {
+ $('#stemma-edit-dialog').dialog('close');
+ }
+ },
+ open: function(evt) {
+ $("#edit_stemma_status").empty();
+ var stemmaseq = $('#stemmaseq').val();
+ if( stemmaseq == 'n' ) {
+ // If we are creating a new stemma, populate the textarea with a
+ // bare digraph.
+ $(evt.target).dialog('option', 'title', 'Add a new stemma')
+ $('#dot_field').val( "digraph stemma {\n\n}" );
+ } else {
+ // If we are editing a stemma, grab its stemmadot and populate the
+ // textarea with that.
+ $(evt.target).dialog('option', 'title', 'Edit selected stemma')
+ $('#dot_field').val( 'Loading, please wait...' );
+ var doturl = "[% c.uri_for( '/stemmadot' ) %]/" + selectedTextID + "/" + stemmaseq;
+ $.getJSON( doturl, function (data) {
+ // Re-insert the line breaks
+ var dotstring = data.dot.replace(/\|n/gm, "\n");
+ $('#dot_field').val( dotstring );
+ });
+ }
+ },
+ }).ajaxError( function(event, jqXHR, ajaxSettings, thrownError) {
+ $(event.target).parent().find('.ui-button').button("enable");
+ if( ajaxSettings.url.indexOf( 'stemma' ) > -1
+ && ajaxSettings.type == 'POST' ) {
+ display_error( jqXHR, $("#edit_stemma_status") );
+ }
+ });
+
$('#upload-collation-dialog').dialog({
autoOpen: false,
height: 325,
return false;
}
},
- cancel: function() {
+ Cancel: function() {
$('#upload-collation-dialog').dialog('close');
}
},
[% END %]
</div>
<div id="textinfo_waitbox">
- <img src="[% c.uri_for( 'images', 'ajax-loader.gif' ) %]" alt="Loading tradition info..."/>
+ <h3>Loading tradition information, please wait...</h3>
+ <img src="[% c.uri_for( 'images', 'ajax-loader.gif' ) %]" alt="Loading tradition info..." />
</div>
<div id="textinfo_container">
<div id="textinfo_load_status"></div>
<h2>Text <span class="texttitle"></span></h2>
<ul>
+ <li>is owned by <span id="owner_id"></span></li>
+ <li>is <span id="not_public"></span>public</li>
+ <li>has <span id="marked_language"></span> as its primary language</li>
<li>has <span id="witness_num"></span> witnesses: <span id="witness_list"></span></li>
- <li>has <span id="reading_num"></span> distinct readings</li>
- <li>has <span id="relationship_num"></span> distinct reading relationships</li>
</ul>
<!-- TODO buttons on either side of the graph div to flip through the stemmata -->
<div id="textinfo_container_buttons">
+ <form id="open_textinfo_edit" action="" method="GET" name="edit_textinfo">
+ <div class="button" id="edit_textinfo_button"
+ onClick="$('#textinfo-edit-dialog').dialog('open')">
+ <span>Modify information about this tradition</span>
+ </div>
+ </form>
<form id="stemma_pager" action="" method="GET" name="stemma_pager">
<div class="button" id="stemma_pager_button" onClick="$('#stemma_pager').submit()">
<span>Left & right go here</span>
</div>
</form>
+ <form id="open_stemma_add" action="" method="GET" name="add_new_stemma">
+ <div class="button" id="stemma_add_button"
+ onClick="$('#stemmaseq').val('n'); $('#stemma-edit-dialog').dialog('open');">
+ <span>Add a new stemma</span>
+ </div>
+ </form>
+ <form id="open_stemma_edit" action="" method="GET" name="edit_current_stemma">
+ <div class="button" id="stemma_edit_button"
+ onClick="$('#stemmaseq').val(selectedStemmaID); $('#stemma-edit-dialog').dialog('open');">
+ <span>Edit this stemma</span>
+ </div>
+ </form>
<form id="run_stexaminer" action="" method="GET" name="run_stexaminer">
<div class="button" id="stexaminer_button" onClick="$('#run_stexaminer').submit()">
<span>Examine variants against this stemma</span>
<!-- Interim 'loading' message for directory box -->
<div id="loading_message">
<h3>Loading texts, please wait...</h3>
- <img src="[% c.uri_for( 'images', 'ajax-loader.gif' ) %]" />
+ <img src="[% c.uri_for( 'images', 'ajax-loader.gif' ) %]" alt="Loading tradition list..."/>
+ </div>
+
+ <!-- Textinfo editor dialog -->
+ <div id="textinfo-edit-dialog" title="Edit information about this tradition">
+ <div id="textinfo_edit_container">
+ <form id="edit_textinfo">
+ <label for="edit_name">Tradition name: </label>
+ <input id="edit_name" type="text" size="30" name="name"/><br/>
+ <label for="edit_language">Language: </label>
+ <input id="edit_language" type="text" size="12" name="language"/>
+ <label for="edit_public">Publicly viewable: </label>
+ <input id="edit_public" type="checkbox" name="public"/><br/>
+[% IF c.user_exists -%]
+[% IF c.user.get_object.is_admin -%]
+ <label for="edit_owner">Publicly viewable: </label>
+ <input id="edit_owner" type="text" size="30" name="owner"/><br/>
+[% END -%]
+[% END -%]
+ </form>
+ <div id="edit_textinfo_status"></div>
+ </div>
+ </div>
+
+ <!-- Stemma dot editor dialog, simple textarea for now -->
+ <div id="stemma-edit-dialog">
+ <div id="stemma_edit_container">
+ <form id="edit_stemma">
+ <label for="dot_field">Dot definition for this stemma: </label><br/>
+ <textarea id="dot_field" rows="30" cols="40"></textarea>
+ <input id="stemmaseq" type="hidden" name="stemmaseq" val="n"/>
+ <div id="edit_instructions">
+ <p>All definitions begin with the line
+ <pre>digraph stemma {</pre>
+ and end with the line
+ <pre>}</pre>Please do not change these lines.</p>
+ <p>First list each witness in your stemma, whether extant or lost /
+ reconstructed / hypothetical, and assign them a class of either "extant"
+ or "hypothetical". For example:</p><pre>
+ α [ class=hypothetical ]
+ C [ class=extant ]
+ </pre>
+ <p>Next, list the direct links between witnesses, one per line. For example, if
+ witness C descends directly from witness α, note it as follows:</p><pre>
+ α -> C
+ </pre>
+ <p>A witness may be the exemplar for any number of other witnesses, whether
+ extant or not; likewise, a witness may inherit from any number of other
+ witnesses. Use as may "A -> B" pairings as necessary to describe the links.</p>
+ </div>
+ </form>
+ <div id="edit_stemma_status"></div>
+ </div>
</div>
<!-- File upload dialog box -->
<div id="upload-collation-dialog" title="Upload a collation">
<div id="upload_container">
<form id="new_tradition">
- <label for="traditionname">Name of this text / tradition: </label>
- <input id="traditionname" type="text" name="name" size="40"/><br/>
+ <label for="new_name">Name of this text / tradition: </label>
+ <input id="new_name" type="text" name="name" size="40"/><br/>
+ <label for="new_lang">Primary language of the text: </label>
+ <input id="new_lang" type="text" name="language" size="20"/><br/>
+ <label for="new_public">Allow public display: </label>
+ <input id="new_public" name="public" type="checkbox"/><br/>
<div id="filelist"></div>
<form>
<div id="upload_status"></div>