File size: 2,279 Bytes
6556bb5
 
 
 
 
 
 
 
 
 
 
 
ee12ee4
 
 
 
 
6556bb5
ee12ee4
 
 
 
 
 
 
 
 
 
 
 
 
001aa75
ee12ee4
 
 
 
6556bb5
 
 
 
 
ee12ee4
6556bb5
 
ee12ee4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6556bb5
 
 
 
 
 
 
 
ee12ee4
 
 
 
 
 
 
6556bb5
ee12ee4
6556bb5
ee12ee4
 
b784e3e
001aa75
 
 
 
ee12ee4
 
 
 
 
6556bb5
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96


/**
 * Indicates position of spans of text inside the string. 
 * (for visual applications only, no semantic sense here.)
 */
interface Span {
	type:  string;
	start: number;
	end:   number;
}

interface SpanTag {
	span: Span;
	tag: "start" | "end";
}

class Displacy {
	static sortSpans(spans: Span[]) {
		spans.sort((a, b) => {  /// `a` should come first when the result is < 0
			if (a.start === b.start) {
				return b.end - a.end;   /// CAUTION.
			}
			return a.start - b.start;
		});
		
		// Check existence of **strict overlapping**
		spans.forEach((s, i) => {
			if (i < spans.length - 1) {
				const sNext = spans[i+1];
				if (s.start < sNext.start && s.end > sNext.start) {
					console.log("ERROR", "Spans: strict overlapping");
				}
			}
		});
	}
	
	/**
	 * Render a text string and its entity spans
	 * 
	 * *see displacy-ent.js*
	 * see https://github.com/explosion/displacy-ent/issues/2
	 */
	static render(text: string, spans: Span[]): string {
		this.sortSpans(spans);
		
		const tags: { [index: number]: SpanTag[] } = {};
		const __addTag = (i: number, s: Span, tag: "start" | "end") => {
			if (Array.isArray(tags[i])) {
				tags[i].push({ span: s, tag: tag });
			} else {
				tags[i] = [{ span: s, tag: tag }];
			}
		};
		for (const s of spans) {
			__addTag(s.start, s, "start");
			__addTag(s.end, s, "end");
		}
		// console.log(JSON.stringify(tags));  // todo remove
		
		let out = {
			__content: "",
			append(s: string) {
				this.__content += s;
			}
		};
		let offset = 0;
		
		const indexes = Object.keys(tags).map(k => parseInt(k, 10)).sort((a, b) => a - b); /// CAUTION
		for (const i of indexes) {
			const spanTags = tags[i];
			// console.log(i, spanTags);  // todo remove
			if (i > offset) {
				out.append(text.slice(offset, i));
			}
			
			offset = i;
			
			for (const sT of spanTags) {
				if (sT.tag === "start") {
					out.append(`<mark data-entity="${ sT.span.type.toLowerCase() }" data-index="${ (<any>sT.span).index }">`);
					const singleScore = (<any>sT.span).singleScore;
					if (singleScore) {
						out.append(`<span class="single-score">${ singleScore.toFixed(3) }</span>`);
					}
				} else {
					out.append(`</mark>`);
				}
			}
		}
		
		out.append(text.slice(offset, text.length));
		return out.__content;
	}
}