mirror of
				https://github.com/qurator-spk/neat.git
				synced 2025-10-31 16:54:14 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			697 lines
		
	
	
		
			No EOL
		
	
	
		
			20 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			697 lines
		
	
	
		
			No EOL
		
	
	
		
			20 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| 
 | |
| function loadFile(evt, onComplete) {
 | |
| 
 | |
|     var file = evt.target.files[0];
 | |
| 
 | |
|     var urls = null;
 | |
| 
 | |
|     var reader = new FileReader();
 | |
| 
 | |
|     reader.onload =
 | |
|         function(event) {
 | |
| 
 | |
|             let link_detector = /(https?:\/\/[^\s]+)/g;
 | |
| 
 | |
|             let lines = event.target.result.split(/\r\n|\n/);
 | |
|             for(var i = 0; i < lines.length; i++){
 | |
| 
 | |
|                 let line = lines[i];
 | |
| 
 | |
|                 if (!line.startsWith('#')) continue;
 | |
| 
 | |
|                 let tmp = line.match(link_detector);
 | |
| 
 | |
|                 if (tmp == null) continue;
 | |
| 
 | |
|                 if (urls == null) {
 | |
|                     urls = tmp;
 | |
|                 }
 | |
|                 else {
 | |
|                     urls.push(tmp[0])
 | |
|                 }
 | |
|             };
 | |
|         };
 | |
| 
 | |
|     reader.readAsText(file);
 | |
| 
 | |
|     Papa.parse(file, {
 | |
|         header: true,
 | |
|         delimiter: '\t',
 | |
|         quoteChar: String.fromCharCode(0),
 | |
| 	    escapeChar: String.fromCharCode(0),
 | |
|         comments: "#",
 | |
|         skipEmptyLines: true,
 | |
|         dynamicTyping: true,
 | |
|         complete: function(results) { onComplete(results, file, urls); }
 | |
|     });
 | |
| }
 | |
| 
 | |
| function setupInterface(data, file, urls) {
 | |
| 
 | |
|     let displayRows=20
 | |
|     let startIndex=0;
 | |
|     let endIndex=displayRows;
 | |
| 
 | |
|     let has_changes = false;
 | |
|     var save_timeout = null;
 | |
| 
 | |
|     function notifyChange() {
 | |
|         if (save_timeout != null) clearTimeout(save_timeout);
 | |
|         has_changes = true;
 | |
| 
 | |
|         $("#save").attr('disabled', false);
 | |
|     }
 | |
| 
 | |
|     function resetChanged() {
 | |
|         if (save_timeout != null) clearTimeout(save_timeout);
 | |
| 
 | |
|         $("#save").attr('disabled', true);
 | |
|         has_changes = false;
 | |
|     }
 | |
| 
 | |
|     function checkForSave (csv) {
 | |
| 
 | |
|         if (save_timeout != null) clearTimeout(save_timeout);
 | |
| 
 | |
|         // This is a work-around that checks if the user actually saved the file or if the save dialog was cancelled.
 | |
|         let counter = 0;
 | |
|         let checker =
 | |
|             function() {
 | |
|                 console.log('checker ...', counter);
 | |
| 
 | |
|                 if (counter > 20) return;
 | |
| 
 | |
|                 let reader = new FileReader();
 | |
| 
 | |
|                 reader.onload =
 | |
|                     function(event) {
 | |
| 
 | |
|                         let content = event.target.result;
 | |
| 
 | |
|                         if (content == csv) { // ok content of the file is actually equal to desired content.
 | |
|                             console.log('Save success ...');
 | |
|                             resetChanged();
 | |
|                             return;
 | |
|                         }
 | |
| 
 | |
|                         counter++;
 | |
|                         save_timeout = setTimeout(checker, 3000);
 | |
|                     };
 | |
| 
 | |
|                 reader.readAsText(file);
 | |
|             };
 | |
| 
 | |
|         save_timeout = setTimeout(checker, 3000);
 | |
|     };
 | |
| 
 | |
|     // public interface
 | |
|     var that =
 | |
|         {
 | |
|             hasChanges: function () { return has_changes; }
 | |
|         };
 | |
| 
 | |
|     function updatePreview(nRow) {
 | |
| 
 | |
|         if (urls == null) return;
 | |
| 
 | |
|         let img_url = urls[data.data[nRow]['url_id']];
 | |
| 
 | |
|         if (img_url == "http://empty")
 | |
|             return
 | |
| 
 | |
|         let left = data.data[nRow]['left'];
 | |
|         let right = data.data[nRow]['right'];
 | |
|         let top = data.data[nRow]['top'];
 | |
|         let bottom = data.data[nRow]['bottom'];
 | |
| 
 | |
|         top = Math.max(0, top - 25);
 | |
|         bottom = bottom + 25;
 | |
|         left = Math.max(0, left - 50);
 | |
|         right = right + 50;
 | |
| 
 | |
|         let width = right - left;
 | |
|         let height = bottom - top;
 | |
| 
 | |
|         img_url = img_url.replace('left',  left.toString());
 | |
|         img_url = img_url.replace('right', right.toString());
 | |
|         img_url = img_url.replace('top',   top.toString());
 | |
|         img_url = img_url.replace('bottom',bottom.toString());
 | |
|         img_url = img_url.replace('width', width.toString());
 | |
|         img_url = img_url.replace('height', height.toString());
 | |
| 
 | |
|         //console.log(img_url)
 | |
| 
 | |
|         $("#preview").attr("src", img_url);
 | |
| 
 | |
|         img_url = urls[data.data[nRow]['url_id']];
 | |
| 
 | |
|         top = Math.max(0, top - 200);
 | |
|         bottom = bottom + 200;
 | |
| 
 | |
|         left = Math.max(0, left - 400);
 | |
|         right = right + 400;
 | |
| 
 | |
|         width = right - left;
 | |
|         height = bottom - top;
 | |
| 
 | |
|         img_url = img_url.replace('left',  left.toString());
 | |
|         img_url = img_url.replace('right', right.toString());
 | |
|         img_url = img_url.replace('top',   top.toString());
 | |
|         img_url = img_url.replace('bottom',bottom.toString());
 | |
|         img_url = img_url.replace('width', width.toString());
 | |
|         img_url = img_url.replace('height', height.toString());
 | |
| 
 | |
|         $("#preview-link").attr("href", img_url);
 | |
|     }
 | |
| 
 | |
|     function gotoLocation(evt) {
 | |
| 
 | |
|         if (urls == null) return;
 | |
| 
 | |
|         let nRow = parseInt($(evt.target).text());
 | |
| 
 | |
|         updatePreview(nRow)
 | |
|     }
 | |
| 
 | |
|     function colorCode() {
 | |
|         $("#table td:contains('B-PER')").addClass('ner_per');
 | |
|         $("#table td:contains('I-PER')").addClass('ner_per');
 | |
|         $("#table td:contains('B-LOC')").addClass('ner_loc');
 | |
|         $("#table td:contains('I-LOC')").addClass('ner_loc');
 | |
|         $("#table td:contains('B-ORG')").addClass('ner_org');
 | |
|         $("#table td:contains('I-ORG')").addClass('ner_org');
 | |
|         $("#table td:contains('B-WORK')").addClass('ner_work');
 | |
|         $("#table td:contains('I-WORK')").addClass('ner_work');
 | |
|         $("#table td:contains('B-CONF')").addClass('ner_conf');
 | |
|         $("#table td:contains('I-CONF')").addClass('ner_conf');
 | |
|         $("#table td:contains('B-EVT')").addClass('ner_evt');
 | |
|         $("#table td:contains('I-EVT')").addClass('ner_evt');
 | |
|         $("#table td:contains('B-TODO')").addClass('ner_todo');
 | |
|         $("#table td:contains('I-TODO')").addClass('ner_todo');
 | |
|     }
 | |
| 
 | |
|     let editingTd;
 | |
| 
 | |
|     function makeTdEditable(td) {
 | |
| 
 | |
|         editingTd = {
 | |
|             elem: td,
 | |
|             data: td.innerHTML,
 | |
|             finish:
 | |
|                 function (td, isOk) {
 | |
| 
 | |
|                     if (isOk) {
 | |
| 
 | |
|                         let newValue = $('#edit-area').val();
 | |
| 
 | |
|                         $(td).html(newValue);
 | |
| 
 | |
|                         let tableInfo = $(td).data('tableInfo');
 | |
| 
 | |
|                         data.data[tableInfo.nRow][tableInfo.column] = newValue;
 | |
| 
 | |
|                         notifyChange();
 | |
|                     }
 | |
|                     else {
 | |
|                         $(td).html(editingTd.data);
 | |
|                     }
 | |
| 
 | |
|                     editingTd = null;
 | |
|                 }
 | |
|         };
 | |
| 
 | |
|         let textArea = document.createElement('textarea');
 | |
|         textArea.style.width = td.clientWidth + 'px';
 | |
|         textArea.style.height = td.clientHeight + 'px';
 | |
|         textArea.id = 'edit-area';
 | |
| 
 | |
|         $(textArea).val($(td).html());
 | |
|         $(td).html('');
 | |
|         $(td).append(textArea);
 | |
|         textArea.focus();
 | |
| 
 | |
|         let edit_html =
 | |
|             `<div class="edit-controls">
 | |
|                 <button class="btn btn-secondary btn-sm" id="edit-ok">OK</button>
 | |
|                 <button class="btn btn-secondary btn-sm" id="edit-cancel">CANCEL</button>
 | |
|              </div>`
 | |
| 
 | |
|         td.insertAdjacentHTML("beforeEnd", edit_html);
 | |
| 
 | |
|         $('#edit-ok').on('click',
 | |
|             function(evt) {
 | |
|                 editingTd.finish(editingTd.elem, true);
 | |
|             });
 | |
| 
 | |
|         $('#edit-cancel').on('click',
 | |
|             function(evt) {
 | |
|                 editingTd.finish(editingTd.elem, false);
 | |
|             });
 | |
|     }
 | |
| 
 | |
|     function makeLineSplitMerge(td) {
 | |
| 
 | |
|         editingTd = {
 | |
|             elem: td,
 | |
|             data: td.innerHTML,
 | |
|             tokenizer_action: null,
 | |
|             finish: function(td, isOk) {
 | |
|                 $(td).html(editingTd.data);
 | |
|                 $(td).addClass('editable');
 | |
| 
 | |
|                 if (editingTd.tokenizer_action == null) {
 | |
|                     editingTd = null;
 | |
|                     return;
 | |
|                 }
 | |
| 
 | |
|                 let tableInfo = $(td).data('tableInfo');
 | |
| 
 | |
|                 if (editingTd.tokenizer_action.includes('merge')) {
 | |
| 
 | |
|                     if (tableInfo.nRow < 1) {
 | |
|                         editingTd = null;
 | |
|                         return;
 | |
|                     }
 | |
| 
 | |
|                     let pos = tableInfo.nRow + 1;
 | |
|                     word_pos = data.data[tableInfo.nRow - 1]['No.'] + 1
 | |
|                     while((pos < data.data.length) && (data.data[pos]['No.'] > 1)) {
 | |
|                         data.data[pos]['No.'] = word_pos;
 | |
|                         pos++;
 | |
|                         word_pos++;
 | |
|                     }
 | |
| 
 | |
|                     data.data[tableInfo.nRow - 1]['TOKEN'] += data.data[tableInfo.nRow]['TOKEN'];
 | |
| 
 | |
|                     data.data.splice(tableInfo.nRow, 1);
 | |
| 
 | |
|                     notifyChange();
 | |
|                 }
 | |
|                 else if (editingTd.tokenizer_action.includes('split')) {
 | |
| 
 | |
|                     data.data.splice(tableInfo.nRow, 0, JSON.parse(JSON.stringify(data.data[tableInfo.nRow])));
 | |
|                     data.data[tableInfo.nRow + 1]['No.'] += 1
 | |
| 
 | |
|                     let pos = tableInfo.nRow + 2;
 | |
|                     while ((pos < data.data.length) && (data.data[pos]['No.'] > 1)) {
 | |
|                         data.data[pos]['No.']++;
 | |
|                         pos++;
 | |
|                     }
 | |
| 
 | |
|                     notifyChange();
 | |
|                 }
 | |
|                 else if (editingTd.tokenizer_action.includes('sentence')) {
 | |
| 
 | |
|                     let pos = tableInfo.nRow;
 | |
| 
 | |
|                     let new_row = JSON.parse(JSON.stringify(data.data[pos]))
 | |
| 
 | |
|                     console.log(new_row)
 | |
| 
 | |
|                     data.data.splice(pos, 0, new_row);
 | |
|                     data.data[pos]['No.'] = 0
 | |
|                     data.data[pos]['TOKEN'] = ''
 | |
|                     pos += 1
 | |
| 
 | |
|                     let word_pos = 1;
 | |
|                     while ((pos < data.data.length) && (data.data[pos]['No.'] != 1) && (data.data[pos]['TOKEN'] != '') ) {
 | |
|                         data.data[pos]['No.'] = word_pos;
 | |
|                         pos++;
 | |
|                         word_pos++;
 | |
|                     }
 | |
| 
 | |
|                     notifyChange();
 | |
|                 }
 | |
| 
 | |
|                 editingTd = null;
 | |
| 
 | |
|                 updateTable();
 | |
|             }
 | |
|         };
 | |
| 
 | |
|         let edit_html = `
 | |
|             <div class="accordion" id="tokenizer" style="display:block;">
 | |
|                 <section class="accordion-item tokenizer-action">↕  split</section>
 | |
|                 <section class="accordion-item tokenizer-action">⟳ merge</section>
 | |
|                 <section class="accordion-item tokenizer-action">☇ sentence</section>
 | |
|             </div>
 | |
|         `;
 | |
| 
 | |
|         $(td).removeClass();
 | |
|         $(td).html(edit_html);
 | |
|         $('#tokenizer').mouseleave(
 | |
|             function(event) {
 | |
|                 editingTd.finish(editingTd.elem, false);
 | |
|             });
 | |
| 
 | |
|         $('.tokenizer-action').click(
 | |
|             function(event) {
 | |
|                 editingTd.tokenizer_action = $(event.target).text();
 | |
|             });
 | |
|     }
 | |
| 
 | |
|     function makeTagEdit(td) {
 | |
| 
 | |
|         editingTd = {
 | |
|             elem: td,
 | |
|             data: td.innerHTML,
 | |
|             finish: function(td, isOk) {
 | |
| 
 | |
|                 let tableInfo = $(td).data('tableInfo');
 | |
| 
 | |
|                 data.data[tableInfo.nRow][tableInfo.column] = editingTd.data;
 | |
| 
 | |
|                 $(td).html(editingTd.data);
 | |
|                 $(td).addClass('editable');
 | |
| 
 | |
|                 editingTd = null;
 | |
| 
 | |
|                 colorCode();
 | |
| 
 | |
|                 notifyChange();
 | |
|             }
 | |
|         };
 | |
| 
 | |
|         let edit_html = `
 | |
|             <div class="accordion" id="tagger" style="display:block;">
 | |
|                 <section class="accordion-item type_select">O
 | |
|                 </section>
 | |
|                 <section class="accordion-item">B
 | |
|                     <div class="accordion-item-content">
 | |
|                         <div class="ner_per type_select">B-PER</div>
 | |
|                         <div class="ner_loc type_select">B-LOC</div>
 | |
|                         <div class="ner_org type_select">B-ORG</div>
 | |
|                         <div class="ner_work type_select">B-WORK</div>
 | |
|                         <div class="ner_conf type_select">B-CONF</div>
 | |
|                         <div class="ner_evt type_select">B-EVT</div>
 | |
|                         <div class="ner_todo type_select">B-TODO</div>
 | |
|                     </div>
 | |
|                 </section>
 | |
|                 <section class="accordion-item">I
 | |
|                     <div class="accordion-item-content">
 | |
|                         <div class="ner_per type_select">I-PER</div>
 | |
|                         <div class="ner_loc type_select">I-LOC</div>
 | |
|                         <div class="ner_org type_select">I-ORG</div>
 | |
|                         <div class="ner_work type_select">I-WORK</div>
 | |
|                         <div class="ner_conf type_select">I-CONF</div>
 | |
|                         <div class="ner_evt type_select">I-EVT</div>
 | |
|                         <div class="ner_todo type_select">I-TODO</div>
 | |
|                     </div>
 | |
|                 </section>
 | |
|             </div>
 | |
|         `;
 | |
| 
 | |
|         $(td).removeClass();
 | |
|         $(td).html(edit_html);
 | |
|         $('#tagger').mouseleave(
 | |
|             function(event) {
 | |
|                 editingTd.finish(editingTd.elem, false);
 | |
|             });
 | |
| 
 | |
|         $('.type_select').click(
 | |
|             function(event) {
 | |
|                 editingTd.data = $(event.target).text();
 | |
|             });
 | |
|     }
 | |
| 
 | |
|     function updateTable() {
 | |
| 
 | |
|         let do_not_display = new Set(['url_id', 'left', 'right', 'top', 'bottom']);
 | |
| 
 | |
|         editingTd = null;
 | |
| 
 | |
|         let editable_html =
 | |
|             `
 | |
|                 <td class="editable">
 | |
|             `;
 | |
| 
 | |
|         $('#table-body').empty();
 | |
| 
 | |
|         $.each(data.data,
 | |
|               function(nRow, el) {
 | |
| 
 | |
|                   if (nRow < startIndex) return;
 | |
|                   if (nRow >= endIndex) return;
 | |
| 
 | |
|                   var row = $("<tr/>");
 | |
|                   row.append($('<td> <button class="btn btn-link btn-xs py-0 offset" onmouseover="click();">' +
 | |
|                                       nRow + '</button>  </td>'));
 | |
| 
 | |
|                   $.each(el,
 | |
|                       function(column, content) {
 | |
| 
 | |
|                           if (do_not_display.has(column)) return
 | |
| 
 | |
|                           var clickAction = function() { console.log('Do something different');}
 | |
| 
 | |
|                           if (column == 'No.')
 | |
|                             clickAction = makeLineSplitMerge
 | |
| 
 | |
|                           if ((column == 'TOKEN') || (column == 'GND-ID'))
 | |
|                             clickAction = makeTdEditable
 | |
| 
 | |
|                           if ((column == 'NE-TAG') || (column == 'NE-EMB'))
 | |
|                             clickAction = makeTagEdit
 | |
| 
 | |
|                           row.append(
 | |
|                               $(editable_html).
 | |
|                                   text(content).
 | |
|                                   data('tableInfo', { 'nRow': nRow, 'column': column , 'clickAction': clickAction })
 | |
|                           );
 | |
|                       });
 | |
| 
 | |
|                   $("#table tbody").append(row);
 | |
|               });
 | |
| 
 | |
|         colorCode();
 | |
| 
 | |
|         $(".offset").on('click', gotoLocation);
 | |
| 
 | |
|         //updatePreview(startIndex)
 | |
| 
 | |
|         if ($("#docpos").val() != startIndex) {
 | |
| 
 | |
|             $("#docpos").val(data.data.length - startIndex);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     let slider_pos = data.data.length - startIndex;
 | |
|     let slider_min = displayRows;
 | |
|     let slider_max = data.data.length;
 | |
| 
 | |
|     let range_html =
 | |
|             `
 | |
|             <input type="range" orient="vertical" class="form-control-range"
 | |
|                 style="-webkit-appearance: slider-vertical;height:100%;outline: 0 none !important;"
 | |
|                 min="${slider_min}" max="${slider_max}" value="${slider_pos}" id="docpos" />
 | |
|             `;
 | |
| 
 | |
|     $("#region-right").html(range_html)
 | |
| 
 | |
|     $("#docpos").change(
 | |
|         function(evt) {
 | |
| 
 | |
|             if (startIndex == data.data.length - this.value) return;
 | |
| 
 | |
|             startIndex = data.data.length - this.value;
 | |
|             endIndex = startIndex + displayRows;
 | |
| 
 | |
|             updateTable();
 | |
|         });
 | |
| 
 | |
|     $('#docpos').slider();
 | |
| 
 | |
|      let table_html =
 | |
|         `
 | |
|         <table id="table">
 | |
|             <thead>
 | |
|             <tr>
 | |
|                 <th><button class="btn btn-link" id="back"><<</button>LOCATION</th>
 | |
|                 <th>POSITION</th>
 | |
|                 <th>TOKEN</th>
 | |
|                 <th>NE-TAG</th>
 | |
|                 <th>NE-EMB</th>
 | |
|                 <th>GND-ID<button class="btn btn-link" id="next">>></button></th>
 | |
|             </tr>
 | |
|             </thead>
 | |
|             <tbody id="table-body"></tbody>
 | |
|         </table>
 | |
|         <br/>
 | |
|         <br/>
 | |
|         `;
 | |
| 
 | |
|     let save_html =
 | |
|         `<button class="btn btn-primary saveButton" id="save" disabled>Save Changes</button>`
 | |
| 
 | |
|     $("#tableregion").html(table_html)
 | |
| 
 | |
|     $("#btn-region").html(save_html)
 | |
| 
 | |
|     $("#file-region").html('<h3>' + file.name + '</h3>');
 | |
| 
 | |
|     function saveFile(evt) {
 | |
| 
 | |
|         let csv =
 | |
|             Papa.unparse(data,
 | |
|                 {
 | |
|                     header: true,
 | |
|                     delimiter: '\t',
 | |
|                     comments: "#",
 | |
|                     quoteChar: String.fromCharCode(0),
 | |
|                     escapeChar: String.fromCharCode(0),
 | |
|                     skipEmptyLines: true,
 | |
|                     dynamicTyping: true
 | |
|                 });
 | |
| 
 | |
|         let lines = csv.split(/\r\n|\n/);
 | |
| 
 | |
|         csv = [ lines[0] ];
 | |
|         let url_id = -1;
 | |
| 
 | |
|         for(var i = 0; i < data.data.length; i++){
 | |
|             if (data.data[i]['url_id'] > url_id) {
 | |
| 
 | |
|                 url_id = data.data[i]['url_id'];
 | |
| 
 | |
|                 csv.push("# " + urls[url_id]);
 | |
|             }
 | |
|             csv.push(lines[i+1]);
 | |
|         }
 | |
| 
 | |
|         csv = csv.join('\n');
 | |
| 
 | |
|         openSaveFileDialog (csv, file.name, null);
 | |
| 
 | |
|         checkForSave(csv);
 | |
|     }
 | |
| 
 | |
|     function openSaveFileDialog (data, filename, mimetype) {
 | |
| 
 | |
|         if (!data) return;
 | |
| 
 | |
|         var blob = data.constructor !== Blob
 | |
|           ? new Blob([data], {type: mimetype || 'application/octet-stream'})
 | |
|           : data ;
 | |
| 
 | |
|         if (navigator.msSaveBlob) {
 | |
|           navigator.msSaveBlob(blob, filename);
 | |
|           return;
 | |
|         }
 | |
| 
 | |
|         var lnk = document.createElement('a'),
 | |
|             url = window.URL,
 | |
|             objectURL;
 | |
| 
 | |
|         if (mimetype) {
 | |
|           lnk.type = mimetype;
 | |
|         }
 | |
| 
 | |
|         lnk.download = filename || 'untitled';
 | |
|         lnk.href = objectURL = url.createObjectURL(blob);
 | |
|         lnk.dispatchEvent(new MouseEvent('click'));
 | |
|         setTimeout(url.revokeObjectURL.bind(url, objectURL));
 | |
|     }
 | |
| 
 | |
|     $('.saveButton').on('click', saveFile)
 | |
| 
 | |
|     $('#table').on('click',
 | |
|         function(event) {
 | |
| 
 | |
|             let target = event.target.closest('.editable');
 | |
| 
 | |
|             if (editingTd) {
 | |
| 
 | |
|                 if (target == editingTd.elem) return;
 | |
| 
 | |
|                 editingTd.finish(editingTd.elem, true);
 | |
|             }
 | |
| 
 | |
|             if (!$.contains($('#table')[0], target)) return
 | |
| 
 | |
|             $(target).data('tableInfo').clickAction(target);
 | |
|         });
 | |
| 
 | |
|     updateTable();
 | |
| 
 | |
|     $('#tableregion')[0].addEventListener("wheel",
 | |
|         function(event) {
 | |
| 
 | |
|             if (event.deltaY < 0) {
 | |
| 
 | |
|                 if (startIndex <= 0) return;
 | |
| 
 | |
|                 startIndex -= 1;
 | |
|                 endIndex -= 1;
 | |
|             }
 | |
|             else {
 | |
| 
 | |
|                 if (endIndex >= data.data.length) return;
 | |
| 
 | |
|                 startIndex += 1;
 | |
|                 endIndex += 1;
 | |
|             }
 | |
| 
 | |
|             updateTable();
 | |
|         });
 | |
| 
 | |
|     $('#back').on('click',
 | |
|         function(evt) {
 | |
| 
 | |
|             if (startIndex >= displayRows) {
 | |
|                 startIndex -= displayRows;
 | |
|                 endIndex -= displayRows;
 | |
|             }
 | |
|             else {
 | |
|                 startIndex = 0;
 | |
|                 endIndex = displayRows;
 | |
|             }
 | |
| 
 | |
|             updateTable();
 | |
|         }
 | |
|     );
 | |
| 
 | |
|     $('#next').on('click',
 | |
|         function(evt) {
 | |
| 
 | |
|             if (endIndex + displayRows < data.data.length) {
 | |
|                 endIndex += displayRows;
 | |
|                 startIndex = endIndex - displayRows;
 | |
|             }
 | |
|             else {
 | |
|                 endIndex = data.data.length;
 | |
|                 startIndex = endIndex - displayRows;
 | |
|             }
 | |
| 
 | |
|             updateTable();
 | |
|         }
 | |
|     );
 | |
| 
 | |
|     return that;
 | |
| }
 | |
| 
 | |
| 
 | |
| $(document).ready(
 | |
|     function() {
 | |
| 
 | |
|         $('#tsv-file').change(
 | |
|             function(evt) {
 | |
| 
 | |
|                 loadFile ( evt,
 | |
|                     function(results, file, urls) {
 | |
| 
 | |
|                         var neath = setupInterface(results, file, urls);
 | |
| 
 | |
|                         $(window).bind("beforeunload",
 | |
|                             function() {
 | |
| 
 | |
|                                 console.log(neath.hasChanges());
 | |
| 
 | |
|                                 if (neath.hasChanges())
 | |
|                                     return confirm("You have unsaved changes. Do you want to save them before leaving?");
 | |
|                             }
 | |
|                         );
 | |
|                     })
 | |
|             }
 | |
|         );
 | |
|     }
 | |
| ); |