OLD | NEW |
---|---|
(Empty) | |
1 import 'dart:html'; | |
ricow1
2014/05/14 12:28:08
missing copyright header
zarah
2014/05/14 20:51:03
Done.
| |
2 import 'dart:convert'; | |
3 import 'dart:async'; | |
4 import 'package:source_maps/source_maps.dart'; | |
5 | |
6 Element targetFileName = querySelector("#target_filename"); | |
7 Element sourceFileName = querySelector("#source_filename"); | |
8 DivElement generatedOutput = querySelector("#generated_output"); | |
9 DivElement selectedSource = querySelector("#selected_source"); | |
10 DivElement selectedOutputSpan = querySelector("#current_span"); | |
11 DivElement decodedMap = querySelector("#decoded_map"); | |
12 DivElement originalMap = querySelector("#original_map"); | |
13 | |
14 Map<TargetEntry, List<SpanElement>> targetEntryMap = {}; | |
15 List<SpanElement> highlightedMapEntry = null; | |
16 List<String> target; | |
17 SingleMapping sourceMap; | |
18 | |
19 void adjustDivHeightsToWindow() { | |
20 generatedOutput.style.height = "${window.innerHeight / 3 - 50}px"; | |
21 selectedSource.style.height = "${window.innerHeight / 3 - 50}px"; | |
22 decodedMap.style.height = "${window.innerHeight / 3 - 50}px"; | |
23 originalMap.style.height = "${window.innerHeight / 3 - 50}px"; | |
24 } | |
25 | |
26 Future getMap() { | |
27 Completer c = new Completer(); | |
28 HttpRequest httpRequest = new HttpRequest(); | |
29 httpRequest | |
30 ..open('GET', 'http://127.0.0.1:9223/map') | |
31 ..onLoadEnd.listen((_) => c.complete(httpRequest.responseText)) | |
32 ..send(''); | |
33 return c.future; | |
34 } | |
35 | |
36 Future fetchFile(String path) { | |
37 Completer c = new Completer(); | |
38 HttpRequest httpRequest = new HttpRequest(); | |
39 sourceFileName.text = path; | |
40 httpRequest | |
41 ..open('GET', path) | |
42 ..onLoadEnd.listen((_) => c.complete(httpRequest.responseText)) | |
43 ..send(''); | |
44 return c.future; | |
45 } | |
46 | |
47 displaySource(String filename, List<String> source, TargetEntry entry) { | |
48 int line = entry.sourceLine; | |
49 int column = entry.sourceColumn; | |
50 int nameId = entry.sourceNameId; | |
51 String id = nameId == null ? null : sourceMap.names[nameId]; | |
52 selectedSource.children.clear(); | |
53 SpanElement marker = new SpanElement() | |
54 ..className = "marker" | |
55 ..appendText("*"); | |
56 for (int pos = 0; pos < source.length; pos++) { | |
ricow1
2014/05/14 12:28:08
for (var l in source)
zarah
2014/05/14 20:51:03
The variable pos is needed below so this would not
| |
57 String l = source[pos]; | |
58 if (pos != line) { | |
59 selectedSource.children.add(l.isEmpty ? new BRElement() : new DivElement() | |
60 ..appendText(l)); | |
61 } else { | |
62 selectedSource.children.add(new DivElement() | |
63 ..appendText(l.substring(0, column)) | |
64 ..children.add(marker) | |
65 ..appendText(l.substring(column))); | |
66 } | |
67 } | |
68 sourceFileName.text = filename; | |
69 marker.scrollIntoView(); | |
70 } | |
71 | |
72 void highlightSelectedSpan(TargetEntry entry, TargetLineEntry lineEntry) { | |
73 selectedOutputSpan.children.clear(); | |
74 String spanEndCol; | |
75 TargetEntry spanEnd; | |
76 bool nextEntryIsSpanEnd = false; | |
77 for (TargetEntry e in lineEntry.entries) { | |
78 if (nextEntryIsSpanEnd) { | |
79 spanEnd = e; | |
80 break; | |
81 } | |
82 if (e == entry) { | |
83 nextEntryIsSpanEnd = true; | |
84 } | |
85 } | |
86 if (spanEnd == null) { | |
87 spanEndCol = '${target[lineEntry.line].length} (EOL).'; | |
88 } else { | |
89 spanEndCol = '${spanEnd.column}.'; | |
90 } | |
91 | |
92 String targetSpan = | |
93 'Target: Line ${lineEntry.line} Col. ${entry.column} - $spanEndCol'; | |
94 | |
95 if (entry.sourceUrlId == null) { | |
96 targetSpan += ' Source: unknown'; | |
97 selectedOutputSpan.children.add(getTextElement(targetSpan)); | |
98 return; | |
99 } | |
100 | |
101 String source = sourceMap.urls[entry.sourceUrlId]; | |
102 String sourceName = source.substring(source.lastIndexOf('/') + 1); | |
103 String sourcePoint = | |
104 'Source: Line ${entry.sourceLine} Col. ${entry.sourceColumn}'; | |
105 sourcePoint += | |
106 entry.sourceNameId == null ? '' | |
107 : ' (${sourceMap.names[entry.sourceNameId]})'; | |
108 sourcePoint += ' in $sourceName'; | |
109 selectedOutputSpan.children.add(getTextElement(targetSpan)); | |
110 selectedOutputSpan.children.add(new BRElement()); | |
111 selectedOutputSpan.children.add(getTextElement(sourcePoint)); | |
112 | |
113 if (highlightedMapEntry != null) { | |
114 highlightedMapEntry[0].style.background = 'white'; | |
115 highlightedMapEntry[1].style.background = 'white'; | |
116 } | |
117 | |
118 String highlightColor = "#99ff99"; | |
119 highlightedMapEntry = targetEntryMap[entry]; | |
120 highlightedMapEntry[0] | |
121 ..scrollIntoView() | |
122 ..style.backgroundColor = highlightColor; | |
123 highlightedMapEntry[1] | |
124 ..scrollIntoView() | |
125 ..style.backgroundColor = highlightColor; | |
126 highlightedMapEntry[1].onMouseOver.listen((e) { | |
127 selectedOutputSpan.style.zIndex = "2"; | |
128 selectedOutputSpan.style.visibility = "visible"; | |
129 selectedOutputSpan.style.top = "${decodedMap.offsetTo(document.body).y + | |
130 decodedMap.clientHeight - 20}px"; | |
131 selectedOutputSpan.style.left = "${decodedMap.offsetTo(document.body).x}px"; | |
132 selectedOutputSpan.style.width= "${decodedMap.clientWidth}px"; | |
133 }); | |
134 | |
135 highlightedMapEntry[1].onMouseOut.listen( (e) { | |
136 selectedOutputSpan.style.visibility = "hidden"; | |
137 }); | |
138 | |
139 adjustDivHeightsToWindow(); | |
140 } | |
141 | |
142 void loadSource(TargetEntry entry) { | |
143 if (entry.sourceUrlId == null) { | |
144 return; | |
145 } | |
146 | |
147 String source = sourceMap.urls[entry.sourceUrlId]; | |
148 fetchFile(new Uri(path: "/file", | |
149 queryParameters: {"path": source}).toString()).then((text) | |
150 => displaySource(source, text.split("\n"), entry)); | |
151 selectedSource.text = "loading"; | |
152 } | |
153 | |
154 SpanElement createSpan(String content, TargetEntry entry, | |
155 TargetLineEntry lineEntry) { | |
156 return new SpanElement() | |
157 ..addEventListener('click', (e) { | |
158 loadSource(entry); | |
159 highlightSelectedSpan(entry, lineEntry); | |
160 }, false) | |
161 ..className = "range${entry.sourceUrlId % 4}" | |
162 ..appendText(content); | |
163 } | |
164 | |
165 Element getLineNumberElement(int line) { | |
166 SpanElement result = new SpanElement(); | |
167 result.style.fontFamily = "Courier"; | |
168 result.style.fontSize = "10pt"; | |
169 result.appendText("${line} "); | |
170 return result; | |
171 } | |
172 | |
173 Element getTextElement(String text) { | |
174 SpanElement result = new SpanElement(); | |
175 result.text = text; | |
176 return result; | |
177 } | |
178 | |
179 addTargetLine(int lineNumber, String content, TargetLineEntry lineEntry) { | |
180 if (content.isEmpty) { | |
181 generatedOutput.children.add(new BRElement()); | |
Johnni Winther
2014/05/14 12:00:38
Replace `new BRElement()` by `new DivElement()..ch
zarah
2014/05/14 20:51:03
Done.
| |
182 return; | |
183 } | |
184 if (lineEntry == null) { | |
185 generatedOutput.children.add(new DivElement() | |
186 ..children.add(getLineNumberElement(lineNumber)) | |
187 ..children.add(getTextElement(content))); | |
188 return; | |
189 } | |
190 DivElement div = new DivElement(); | |
191 div.children.add(getLineNumberElement(lineNumber)); | |
192 | |
193 int pos = 0; | |
194 TargetEntry previous = null; | |
195 for (TargetEntry next in lineEntry.entries) { | |
196 if (previous == null) { | |
197 if (pos < next.column) { | |
198 div.appendText(content.substring(pos, next.column)); | |
199 } | |
200 if (content.length == next.column) { | |
201 div.children.add(createSpan(" ", next, lineEntry)); | |
202 } | |
203 } else { | |
204 if (next.column <= content.length) { | |
205 String token = content.substring(pos, next.column); | |
206 div.children.add(createSpan(token, previous, lineEntry)); | |
207 } | |
208 if (content.length == next.column) { | |
209 div.children.add(createSpan(" ", next, lineEntry)); | |
210 } | |
211 } | |
212 pos = next.column; | |
213 previous = next; | |
214 } | |
215 String token = content.substring(pos); | |
216 if (previous == null) { | |
217 div.appendText(token); | |
218 } else { | |
219 div..appendText(" ")..children.add(createSpan(token, previous, lineEntry)); | |
Johnni Winther
2014/05/14 12:00:38
Remove `..appendText(" ")`.
zarah
2014/05/14 20:51:03
Done.
| |
220 } | |
221 generatedOutput.children.add(div); | |
222 } | |
223 | |
224 // Display the target source in the HTML. | |
225 void displayTargetSource() { | |
226 List<TargetLineEntry> targetLines = sourceMap.lines; | |
227 int linesIndex = 0; | |
228 for (int line = 0; line < target.length; line++) { | |
229 TargetLineEntry entry = null; | |
230 if (linesIndex < targetLines.length | |
231 && targetLines[linesIndex].line == line) { | |
232 entry = targetLines[linesIndex]; | |
233 linesIndex++; | |
234 } | |
235 if (entry != null) addTargetLine(line, target[line], entry); else | |
Johnni Winther
2014/05/14 12:00:38
Add {} to the branches.
zarah
2014/05/14 20:51:03
Done.
| |
236 addTargetLine(line, target[line], null); | |
237 } | |
238 } | |
239 | |
240 String getMappedData(String mapFileContent) { | |
241 // Source map contains mapping information in this format: | |
242 // "mappings": "A;A,yC;" | |
243 List<String> mapEntry = mapFileContent.split('mappings'); | |
244 return mapEntry[mapEntry.length-1].split('"')[2]; | |
245 } | |
246 | |
247 SpanElement createMapSpan(String segment) { | |
248 return new SpanElement()..text = segment; | |
249 } | |
250 | |
251 SpanElement createDecodedMapSpan(TargetEntry entry) { | |
252 return new SpanElement()..text = '(${entry.column}, ${entry.sourceUrlId},' | |
253 ' ${entry.sourceLine},' | |
254 ' ${entry.sourceColumn})'; | |
255 } | |
256 | |
257 displayMap(String mapFileContent) { | |
258 String mappedData = getMappedData(mapFileContent); | |
259 int sourceMapLine = 0; | |
260 for (String group in mappedData.split(';')) { | |
261 if (group.length == 0) continue; | |
262 | |
263 List<String> segments = []; | |
264 if (!group.contains(',')) { | |
265 segments.add(group); | |
266 } else { | |
267 segments = group.split(','); | |
268 } | |
269 | |
270 TargetLineEntry targetLineEntry = sourceMap.lines[sourceMapLine]; | |
271 decodedMap.children.add(getLineNumberElement(targetLineEntry.line)); | |
272 originalMap.children.add(getLineNumberElement(targetLineEntry.line)); | |
273 bool first = true; | |
274 int entryNumber = 0; | |
275 for (String segment in segments) { | |
276 TargetEntry entry = targetLineEntry.entries[entryNumber]; | |
277 SpanElement orignalMapSpan = createMapSpan(segment); | |
278 SpanElement decodedMapSpan = createDecodedMapSpan(entry); | |
279 if (first) { | |
280 first = false; | |
281 } else { | |
282 originalMap.children.add(getTextElement(', ')); | |
283 decodedMap.children.add(getTextElement(', ')); | |
284 } | |
285 originalMap.children.add(orignalMapSpan); | |
286 decodedMap.children.add(decodedMapSpan); | |
287 ++entryNumber; | |
288 targetEntryMap.putIfAbsent(entry, () => [orignalMapSpan, decodedMapSpan]); | |
289 } | |
290 originalMap.children.add(new BRElement()); | |
291 decodedMap.children.add(new BRElement()); | |
292 ++sourceMapLine; | |
293 } | |
294 } | |
295 | |
296 void main() { | |
297 Future load(String q) => fetchFile(new Uri(path: "/file", queryParameters: { | |
298 "path": q | |
299 }).toString()); | |
300 | |
301 getMap().then((mapFileName) { | |
302 load(mapFileName).then((mapFileContent) { | |
303 sourceMap = new SingleMapping.fromJson(JSON.decode(mapFileContent)); | |
304 displayMap(mapFileContent); | |
305 targetFileName.text = sourceMap.targetUrl; | |
306 load(targetFileName.text).then((targetFileContent) { | |
307 target = targetFileContent.split('\n'); | |
308 displayTargetSource(); | |
309 adjustDivHeightsToWindow(); | |
310 }); | |
311 }); | |
312 }); | |
313 | |
314 sourceFileName.text = "<source not selected>"; | |
315 } | |
OLD | NEW |