Skip to content

Commit d7aed99

Browse files
committed
allow drag and drop single file
1 parent 85af19c commit d7aed99

File tree

5 files changed

+95
-119
lines changed

5 files changed

+95
-119
lines changed

css/style.css

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
#fill {
2+
/* ensure body takes up entire window */
3+
position:absolute;
4+
z-index:-1;
5+
top:0;
6+
left:0;
7+
width:100%;
8+
height:100%;
9+
}
110
body {
211
margin:0;
312
-moz-osx-font-smoothing:grayscale;
@@ -65,16 +74,16 @@ header > button:hover {
6574

6675

6776
/* input */
68-
#brat-input {
77+
#input-modal {
6978
}
70-
#brat-input textarea {
79+
#input-modal textarea {
7180
width:100%;
7281
height:300px;
7382
padding:5px;
7483
font-size:15px;
7584
}
7685
label[for="file-input"] {
77-
padding:15px;
86+
padding:10px 15px;
7887
cursor:pointer;
7988
background:steelblue;
8089
color:white;

index.html

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99

1010
<body>
1111

12+
<div id="fill"></div>
13+
1214
<header id="header">
1315
Examples:
1416
<select id="dataset">
@@ -27,7 +29,7 @@
2729
<button id="options-toggle">Options</button>
2830
</header>
2931

30-
<div id="brat-input" class="modal">
32+
<div id="input-modal" class="modal">
3133
<div>
3234
<header>
3335
<span class="tab active">Custom annotation</span>
@@ -37,13 +39,11 @@
3739
<label for="file-input">Upload file(s)</label>
3840
<input type="file" id="file-input" style="display:none;" multiple>
3941
</form>
40-
<textarea id="text-input"></textarea>
41-
<div id="error-message"></div>
42+
<pre id="message"></pre>
4243
</div>
4344
</div>
4445
</div>
4546

46-
4747
<div id="main">
4848
<div id="tooltip">
4949
</div>

js/components/link.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -390,10 +390,10 @@ class Link {
390390

391391
// helper function to return a path string for an arrowhead
392392
arrowhead(handle) {
393-
const s = 4;
393+
const s = 4, s2 = 6;
394394
return this.top ?
395-
'M' + [handle.x - s, handle.y - s] + 'l' + [s,s * 1.5] + 'l' + [s,-s * 1.5] :
396-
'M' + [handle.x - s, handle.y + s] + 'l' + [s, -s * 1.5] + 'l' + [s, s * 1.5];
395+
'M' + [handle.x - s, handle.y - s] + 'l' + [s, s2] + 'l' + [s, -s2] :
396+
'M' + [handle.x - s, handle.y + s] + 'l' + [s, -s2] + 'l' + [s, s2];
397397
}
398398

399399
remove() {

js/main.js

Lines changed: 62 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -207,8 +207,9 @@ const Main = (function() {
207207
});
208208

209209
document.getElementById('custom-annotation').onclick = function() {
210-
document.getElementById('brat-input').classList.add('open');
210+
document.getElementById('input-modal').classList.add('open');
211211
}
212+
212213
document.getElementById('options-toggle').onclick = function() {
213214
setActiveTab('options');
214215
}
@@ -221,111 +222,56 @@ const Main = (function() {
221222
}
222223
});
223224

224-
document.getElementById('file-input').onchange = loadFile;
225-
}
225+
// upload file
226+
document.getElementById('file-input').onchange = uploadFile;
226227

227-
function printErrorMessage(text) {
228-
document.getElementById('error-message').textContent = text;
229-
}
230-
function clearErrorMessage() {
231-
document.getElementById('error-message').textContent = '';
232-
}
233-
function clearFile() {
234-
document.getElementById('form').reset();
235-
document.getElementById('text-input') = '';
228+
// upload file via drag and drop
229+
document.body.addEventListener('dragenter', (e) => e.preventDefault());
230+
document.body.addEventListener('dragover', (e) => e.preventDefault());
231+
document.body.addEventListener('drop', uploadFile);
236232
}
237233

238-
/**
239-
* loadFile: read file
240-
*/
241-
function loadFile(e) {
242-
let files = e.target.files;
243-
console.log(files);
244-
245-
// get extension
246-
if (files.length === 1) {
247-
const file = files[0];
248-
let fr = new FileReader();
234+
/* read an externally loaded file */
235+
function uploadFile(e) {
236+
e.preventDefault();
237+
let files = (this === document.body) ? e.dataTransfer.files : e.target.files;
238+
239+
// read blobs with FileReader
240+
const promises = [...files].map(file => {
241+
const fr = new FileReader();
249242
fr.readAsText(file);
250-
fr.onload = parseFile;
251-
252-
function parseFile() {
253-
clearErrorMessage();
254-
const text = fr.result;
255-
256-
// try to coerce it into an accepted format
257-
try {
258-
let w, l, c;
259-
if (file.type === 'application/json') {
260-
// reach json
261-
parser.parseJson(JSON.parse(text));
262-
[w, l, c] = buildWordsAndLinks();
263-
} else if (!file.type) {
264-
// brat standoff
265-
[w, l, c] = buildWordsLinksAnn(text);
266-
} else {
267-
printErrorMessage("Could not read file type: " + file.type);
268-
}
269-
270-
if (w && l && c) {
271-
clear();
272-
[words, links, clusters] = [w, l, c];
273-
setSyntaxVisibility();
274-
draw();
275-
document.getElementById('text-input').textContent = text;
276-
}
277-
} catch(e) {
278-
console.log(fr.result, e);
279-
printErrorMessage("See error in console");
243+
return new Promise((resolve, reject) => {
244+
fr.onload = function() {
245+
resolve({
246+
name: file.name,
247+
type: file.type,
248+
content: fr.result
249+
});
250+
};
251+
});
252+
});
253+
254+
Promise.all(promises).then(files => {
255+
try {
256+
let message = parser.parseFiles(files);
257+
if (message) {
258+
redrawVisualization();
259+
printMessage(message);
280260
}
281261
}
282-
}
283-
else if (files.length > 1) {
284-
// search for matching .ann and .txt file
285-
286-
// sort by name
287-
let sortedFiles = [].slice.call(files).sort((a,b) => a.name.localeCompare(b.name));
288-
289-
let prev = 0;
290-
291-
for (let i = 1; i < sortedFiles.length; ++i) {
292-
let f1 = sortedFiles[prev].name.toLowerCase(),
293-
f2 = sortedFiles[i].name.toLowerCase();
294-
let prefixesMatch = f1.slice(0, f1.lastIndexOf('.')) === f2.slice(0, f2.lastIndexOf('.'));
295-
let typesMatch = f1.endsWith('.ann') || f2.endsWith('.ann');
296-
if (prefixesMatch && typesMatch && f1 !== f2) {
297-
// matching files found
298-
let fr = new FileReader();
299-
fr.readAsText(sortedFiles[prev]);
300-
fr.onload = function() {
301-
let f1Text = fr.result;
302-
fr.readAsText(sortedFiles[i]);
303-
fr.onload = function() {
304-
let w, l, c, text;
305-
if (f1.endsWith('.ann')) {
306-
text = fr.result;
307-
[w, l, c] = buildWordsLinksAnn(fr.result, f1Text);
308-
}
309-
else {
310-
text = f1Text;
311-
[w, l, c] = buildWordsLinksAnn(f1Text, fr.result);
312-
}
313-
314-
if (w && l && c) {
315-
clear();
316-
[words, links, clusters] = [w, l, c];
317-
setSyntaxVisibility();
318-
draw();
319-
document.getElementById('text-input').textContent = text;
320-
}
321-
}
322-
};
323-
break;
324-
} else {
325-
prev = i;
326-
}
262+
catch(err) {
263+
console.log('ERROR: ', err);
264+
printMessage(err);
327265
}
328-
}
266+
document.getElementById('form').reset();
267+
});
268+
}
269+
270+
function printMessage(text) {
271+
document.getElementById('message').textContent = text;
272+
}
273+
function clearMessage() {
274+
document.getElementById('message').textContent = '';
329275
}
330276

331277

@@ -344,19 +290,12 @@ const Main = (function() {
344290

345291
parser.loadFile(path)
346292
.then(data => {
347-
ymlToJson.convert('taxonomy.yml.txt', function(taxonomy) {
348-
clear();
349-
words = data.words;
350-
links = data.links;
351-
clusters = data.clusters;
352-
setSyntaxVisibility();
353-
draw();
354-
355-
tm.draw(taxonomy, words);
356-
});
293+
redrawVisualization();
294+
clearMessage();
357295
})
358296
.catch(err => {
359297
console.log('ERROR: ', err);
298+
printMessage(err);
360299
});
361300
};
362301

@@ -370,6 +309,20 @@ const Main = (function() {
370309
links.forEach(link => link.svg && link.svg.remove());
371310
}
372311

312+
function redrawVisualization() {
313+
let data = parser.parsedData;
314+
ymlToJson.convert('taxonomy.yml.txt', function(taxonomy) {
315+
clear();
316+
words = data.words;
317+
links = data.links;
318+
clusters = data.clusters;
319+
setSyntaxVisibility();
320+
draw();
321+
322+
tm.draw(taxonomy, words);
323+
});
324+
}
325+
373326
/**
374327
* draw: draw words, links, rows, etc. onto the visualization
375328
*/

js/parse/parse.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,20 @@ class Parser {
3737
})
3838
}
3939

40+
parseFiles(files) {
41+
console.log(files);
42+
if (files.length === 1) {
43+
const file = files[0];
44+
if (file.type === 'application/json') {
45+
this.parseJson(JSON.parse(file.content));
46+
}
47+
else if (file.type === '') {
48+
this.parseText(file.content);
49+
}
50+
return file.name;
51+
}
52+
}
53+
4054
parseJson(data) {
4155
this.reach.parse(data);
4256
this.parsedData = this.reach.data;

0 commit comments

Comments
 (0)