diff --git a/README.md b/README.md index 7882b2d..77eb610 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,5 @@ # ner.edith ![Screenshot](https://github.com/cneud/ner.edith/blob/master/screenshot.png) + +- use mouse wheel to scroll up and down +- use navigation << and >> to move faster \ No newline at end of file diff --git a/ner-edith.js b/ner-edith.js new file mode 100644 index 0000000..381debf --- /dev/null +++ b/ner-edith.js @@ -0,0 +1,272 @@ +var data; + +var file = null; + +var displayRows=30 +var startIndex=0; +var endIndex=displayRows; + +function loadFile(evt) { + + let table_html = + ` + + + + + + + + + + + +
OFFSETPOSITIONTOKENNE-TAGNE-EMB
+
+
+ `; + + let save_html = + `` + + $("#tableregion").html(table_html) + + $("#btn-region").html(save_html) + + $('.saveButton').on('click', saveFile) + + let editingTd; + + function finishTdEdit(td, isOk) { + + if (isOk) { + let newValue = $('#edit-area').val(); + + $(td).html(newValue); + + let tableInfo = $(td).data('tableInfo'); + + data.data[tableInfo.nRow][tableInfo.column] = newValue; + } + else { + $(td).html(editingTd.data); + } + + editingTd = null; + } + + function makeTdEditable(td) { + + editingTd = { + elem: td, + data: td.innerHTML + }; + + 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 = + `
+ + +
` + + td.insertAdjacentHTML("beforeEnd", edit_html); + + $('#edit-ok').on('click', + function(evt) { + finishTdEdit(editingTd.elem, true); + }); + + $('#edit-cancel').on('click', + function(evt) { + finishTdEdit(editingTd.elem, false); + }); + } + + $('#table').on('click', + function(event) { + + let target = event.target.closest('.editable'); + + if (editingTd) { + + if (target == editingTd.elem) return; + + finishTdEdit(editingTd.elem, true); + } + + if (!$.contains($('#table')[0], target)) return + + makeTdEditable(target); + }); + + file = evt.target.files[0]; + + // TODO: adapt to streaming with 'chunk' callback for large file support, see https://www.papaparse.com/docs + Papa.parse(file, { + header: true, + delimiter: '\t', + comments: "#", + skipEmptyLines: true, + dynamicTyping: true, + complete: function(results) { + //console.log(results); + data = results; + + updateTable(); + + $("#file-region").html('

' + file.name + '

'); + + $('#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(); + } + ); + } + }); +} + +function updateTable() { + + let editable_html = + ` + + `; + + $('#table-body').empty(); + + $.each(data.data, + function(nRow, el) { + + if (nRow < startIndex) return; + if (nRow >= endIndex) return; + + var row = $(""); + row.append($("").text(nRow)); + + $.each(el, + function(column, content) { + row.append( + $(editable_html). + text(content). + data('tableInfo', { 'nRow': nRow, 'column': column }) + ); + }); + + $("#table tbody").append(row); + }); + + $("#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-OTH')").addClass('ner_oth'); + $("#table td:contains('I-OTH')").addClass('ner_oth'); +} + +function saveFile(evt) { + + let csv = + Papa.unparse(data, + { + header: true, + delimiter: '\t', + comments: "#", + skipEmptyLines: true, + dynamicTyping: true + }); + + openSaveFileDialog (csv, file.name, null) +} + + +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)); + +} + +$(document).ready( + function() { + $('#tsv-file').change(loadFile); + } +); \ No newline at end of file diff --git a/ner.edith.html b/ner.edith.html index d32a80f..07899ae 100644 --- a/ner.edith.html +++ b/ner.edith.html @@ -3,130 +3,66 @@ ner.edith + -

ner.edith: named entity recognition editor in html

- Please upload a TSV (tab-separated-values) file in the GermEval2014 data format[i]:

- - - - - - - - - - - -
OFFSETPOSITIONTOKENNE-TAGNE-EMB
- +
+ +
+ +
+
+
+
+
+
+

ner.edith:
named entity recognition editor in html

+
+
+
+
+
+
+
+
+
+
+ Please upload a TSV (tab-separated-values) file in the GermEval2014 data format + [i]: +
+



+

+
+
+
+
+
+
+
+
+
+
+
+
+ +
+ + \ No newline at end of file diff --git a/screenshot.png b/screenshot.png index 5e17850..3e94285 100644 Binary files a/screenshot.png and b/screenshot.png differ