--- /dev/null
+// General-purpose error-handling function.
+// TODO make sure this gets used throughout, where appropriate.
+function display_error( jqXHR, el ) {
+ var errmsg;
+ if( jqXHR.responseText == "" ) {
+ errmsg = "perhaps the server went down?"
+ } else {
+ var errobj;
+ try {
+ errobj = jQuery.parseJSON( jqXHR.responseText );
+ errmsg = errobj.error;
+ } catch ( parse_err ) {
+ errmsg = "something went wrong on the server."
+ }
+ }
+ var msghtml = $('<span>').attr('class', 'error').text( "An error occurred: " + errmsg );
+ $(el).empty().append( msghtml ).show();
+}
+
+// Event to enable the upload button when a file has been selected
+function file_selected( e ) {
+ if( e.files.length == 1 ) {
+ $('#upload_button').button('enable');
+ $('#new_file_name_container').html( '<span id="new_file_name">' + e.files[0].name + '</span>' );
+ } else {
+ $('#upload_button').button('disable');
+ $('#new_file_name_container').html( '(Use \'pick file\' to select a tradition file to upload.)' );
+ }
+}
+
+// Implement our own AJAX method that uses the features of XMLHttpRequest2
+// but try to let it have a similar interface to jquery.post
+// The data var needs to be a FormData() object.
+// The callback will be given a single argument, which is the response data
+// of the given type.
+
+function post_xhr2( url, data, cb, type ) {
+ if( !type ) {
+ type = 'json';
+ }
+ var xhr = new XMLHttpRequest();
+ // Set the expected response type
+ if( type === 'data' ) {
+ xhr.responseType = 'blob';
+ } else if( type === 'xml' ) {
+ xhr.responseType = 'document';
+ }
+ // Post the form
+ // Gin up an AJAX settings object
+ $.ajaxSetup({ url: url, type: 'POST' });
+ xhr.open( 'POST', url, true );
+ // Handle the results
+ xhr.onload = function( e ) {
+ // Get the response and parse it
+ // Call the callback with the response, whatever it was
+ var xhrs = e.target;
+ if( xhrs.status > 199 && xhrs.status < 300 ) { // Success
+ var resp;
+ if( type === 'json' ) {
+ resp = $.parseJSON( xhrs.responseText );
+ } else if ( type === 'xml' ) {
+ resp = xhrs.responseXML;
+ } else if ( type === 'text' ) {
+ resp = xhrs.responseText;
+ } else {
+ resp = xhrs.response;
+ }
+ cb( resp );
+ } else {
+ // Trigger the ajaxError...
+ _trigger_ajaxerror( e );
+ }
+ };
+ xhr.onerror = _trigger_ajaxerror;
+ xhr.onabort = _trigger_ajaxerror;
+ xhr.send( data );
+}
+
+function _trigger_ajaxerror( e ) {
+ var xhr = e.target;
+ var thrown = xhr.statusText || 'Request error';
+ jQuery.event.trigger( 'ajaxError', [ xhr, $.ajaxSettings, thrown ]);
+}
+
+function upload_new () {
+ // Serialize the upload form, get the file and attach it to the request,
+ // POST the lot and handle the response.
+ var newfile = $('#new_file').get(0).files[0];
+ var reader = new FileReader();
+ reader.onload = function( evt ) {
+ var data = new FormData();
+ $.each( $('#new_tradition').serializeArray(), function( i, o ) {
+ data.append( o.name, o.value );
+ });
+ data.append( 'file', newfile );
+ var upload_url = _get_url([ 'newtradition' ]);
+ post_xhr2( upload_url, data, function( ret ) {
+ if( ret.id ) {
+ $('#upload-collation-dialog').dialog('close');
+ refreshDirectory();
+ loadTradition( ret.id, ret.name, 1 );
+ } else if( ret.error ) {
+ $('#upload_status').empty().append(
+ $('<span>').attr('class', 'error').append( ret.error ) );
+ }
+ }, 'json' );
+ };
+ reader.onerror = function( evt ) {
+ var err_resp = 'File read error';
+ if( e.name == 'NotFoundError' ) {
+ err_resp = 'File not found';
+ } else if ( e.name == 'NotReadableError' ) {
+ err_resp == 'File unreadable - is it yours?';
+ } else if ( e.name == 'EncodingError' ) {
+ err_resp == 'File cannot be encoded - is it too long?';
+ } else if ( e.name == 'SecurityError' ) {
+ err_resp == 'File read security error';
+ }
+ // Fake a jqXHR object that we can pass to our generic error handler.
+ var jqxhr = { responseText: '{error:"' + err_resp + '"}' };
+ display_error( jqxhr, $('#upload_status') );
+ $('#upload_button').button('disable');
+ }
+
+ reader.readAsBinaryString( newfile );
+}
+
+// Utility function to neatly construct an application URL
+function _get_url( els ) {
+ return basepath + els.join('/');
+}
+
+// TODO Attach unified ajaxError handler to document
+$(document).ready( function() {
+ // See if we have the browser functionality we need
+ // TODO Also think of a test for SVG readiness
+ if( !!window.FileReader && !!window.File ) {
+ $('#compatibility_check').empty();
+ }
+
+ $('#upload-collation-dialog').dialog({
+ autoOpen: false,
+ height: 360,
+ width: 480,
+ modal: true,
+ buttons: {
+ upload: {
+ text: 'Upload',
+ id: 'upload_button',
+ click: function() {
+ $('#upload_status').empty();
+ $('#upload_button').button("disable");
+ upload_new();
+ }
+ },
+ pick_file: {
+ text: 'Pick File',
+ id: 'pick_file_button',
+ click: function() {
+ $('#new_file').click();
+ }
+ },
+ Cancel: function() {
+ $('#upload-collation-dialog').dialog('close');
+ }
+ },
+ open: function() {
+ // Set the upload button to its correct state based on
+ // whether a file is loaded
+ file_selected( $('#new_file').get(0) );
+ $('#upload_status').empty();
+ }
+ }).ajaxError( function(event, jqXHR, ajaxSettings, thrownError) {
+ // Reset button state
+ file_selected( $('#new_file').get(0) );
+ // Display error message if applicable
+ if( ajaxSettings.url.indexOf( 'newtradition' ) > -1
+ && ajaxSettings.type == 'POST' ) {
+ display_error( jqXHR, $("#upload_status") );
+ }
+ });;
+
+});
--- /dev/null
+[% PROCESS header.tt
+ pagetitle = "Stemmaweb - Test a file upload"
+ applicationjs = c.uri_for( '/js/fileparse.js' )
+%]
+
+ <div id="main_page">
+ <div id="topbanner">
+ <h1 class="title">Stemmaweb - Test a file upload</h1>
+ <p id="compatibility_check"><span class="error">This browser does not support
+ the functions necessary for Stemmaweb to work properly. We recommend use of
+ <a href="http://mozilla.org/firefox">Mozilla Firefox</a> or
+ <a href="http://www.google.com/chrome">Google Chrome</a>.</span></p>
+ </div>
+
+ <div id="upload-collationform" title="Upload a collation">
+ <div id="docco">
+ <form id="new_tradition" method="POST" action="[% c.uri_for( '/microservice/parse' ) %]" enctype="multipart/form-data">
+ <label for="new_file" id="new_file_label">Collation file: </label>
+ <input id="new_file" name="file" type="file" onchange="file_selected(this)" required>
+ <label for="new_type">File type of this text / tradition: </label>
+ <select id="new_type" name="type">
+ <option value="CollateX">CollateX GraphML</option>
+ <option value="csv">Comma-separated values</option>
+ <option value="CTE">Classical Text Editor XML export</option>
+ <option value="tsv">Tab-separated values</option>
+ <option value="TEI">TEI P5 parallel segmentation</option>
+ <option value="xls">Excel 1997-2004</option>
+ <option value="xlsx">Excel 2007+</option>
+ </select>
+ <label for="new_submit">Does it work?</label>
+ <input id="new_submit" type="submit" value="Find out">
+ </form>
+ <div id="upload_status"></div>
+ <div>
+ <h4>Supported file types / extensions:</h4>
+ <ul>
+ <li>*.txt - spreadsheet collation, tab-separated values</li>
+ <li>*.csv - spreadsheet collation, comma-separated values</li>
+ <li>*.xls - spreadsheet collation, Excel 97-2004 format</li>
+ <li>*.xlsx - spreadsheet collation, Excel 2007 XML format</li>
+ <li>*.xml - TEI XML parallel segmentation format</li>
+ <li>*.xml - TEI XML export from Classical Text Editor</li>
+ <li>*.xml - GraphML export from the CollateX tool</li>
+ </ul>
+ <p>All spreadsheet collations should be arranged with the witness sigla in the first row, and the words aligned by row each in its correct witness column.</p>
+ </div>
+ </div>
+ </div>
+
+ <div id="parseresults" style="padding-left: 30px;">
+ [% IF result %]
+ <h3>Success! Probably.</h3>
+ <p>A tradition was successfully created, with the name [% result.name %].</p>
+ [% ELSIF errormsg %]
+ <h3>Uh-oh, something went wrong.</h3>
+ <p> A tradition was not created. Here is the error message:</p>
+ <pre class="error">[% errormsg %]</pre>
+ [% END %]
+ [% IF warnings %]
+ <h3>Information and Warnings</h3>
+ <pre class="warning">[% warnings %]</pre>
+ [% END %]
+ </div>
+ </div>
+
+[% PROCESS footer.tt %]
\ No newline at end of file