5 <title>SourceMaps Tool</title>
7 <style>:root { color-scheme: light dark; }</style>
8 <link rel="stylesheet" href="../../UserInterface/External/CodeMirror/codemirror.css">
9 <link rel="stylesheet" href="../../UserInterface/Views/Variables.css">
10 <link rel="stylesheet" href="../../UserInterface/Views/CodeMirrorOverrides.css">
11 <link rel="stylesheet" href="../../UserInterface/Views/SyntaxHighlightingDefaultTheme.css">
12 <link rel="stylesheet" href="styles.css">
14 <script src="../../UserInterface/External/CodeMirror/codemirror.js"></script>
15 <script src="../../UserInterface/External/CodeMirror/css.js"></script>
16 <script src="../../UserInterface/External/CodeMirror/htmlmixed.js"></script>
17 <script src="../../UserInterface/External/CodeMirror/javascript.js"></script>
18 <script src="../../UserInterface/External/CodeMirror/xml.js"></script>
20 <script src="../../UserInterface/Base/WebInspector.js"></script>
21 <script src="../../UserInterface/Base/Multimap.js"></script>
22 <script src="../../UserInterface/Base/Object.js"></script>
23 <script src="../../UserInterface/Base/Utilities.js"></script>
24 <script src="../../UserInterface/Controllers/FormatterSourceMap.js"></script>
25 <script src="../../UserInterface/Proxies/FormatterWorkerProxy.js"></script>
28 <h1>Debug SourceMaps</h1>
33 <option>javascript</option>
37 <button id="format">Format</button>
38 <button id="save-as-url">Save URL</button>
42 <div class="editors-container">
43 <textarea id="input" name="code"></textarea>
44 <textarea id="output" name="code"></textarea>
49 <pre id="debug"></pre>
50 <h3>Source Mapping</h3>
51 <pre id="debug-mapping"></pre>
55 const modePicker = document.getElementById("mode");
56 const debugPre = document.getElementById("debug");
57 const debugMappingPre = document.getElementById("debug-mapping");
60 let inputCM = CodeMirror.fromTextArea(document.getElementById("input"), {lineNumbers: true});
61 inputCM.setOption("mode", "text/html");
64 let outputCM = CodeMirror.fromTextArea(document.getElementById("output"), {lineNumbers: true, readOnly: true});
65 outputCM.setOption("mode", "text/html");
68 let formatterSourceMap = null;
71 let inputMarker = null;
72 let outputMarker = null;
73 function clearMarkers() {
80 // Refresh after changes after a short delay.
82 inputCM.on("change", function(codeMirror, change) {
85 timer = setTimeout(function() {
92 // Input has changed, update Output.
94 let originalLocation = {lineNumber: 0, columnNumber: 0};
95 let formattedLocation = {lineNumber: 0, columnNumber: 0};
97 inputCM.on("cursorActivity", () => {
101 function updateFromInput() {
102 updateFormattedLocationFromInput();
103 updateDebugTextFromInput();
104 updateOutputCursorFromInput();
107 function updateFormattedLocationFromInput() {
108 if (!formatterSourceMap)
111 let codeMirrorPosition = inputCM.getCursor();
112 let codeMirrorIndex = inputCM.getDoc().indexFromPos(codeMirrorPosition);
113 originalLocation = {lineNumber: codeMirrorPosition.line || 0, columnNumber: codeMirrorPosition.ch || 0, position: codeMirrorIndex};
114 formattedLocation = formatterSourceMap.originalToFormatted(originalLocation.lineNumber, originalLocation.columnNumber);
115 formattedLocation.position = formatterSourceMap.originalPositionToFormattedPosition(codeMirrorIndex);
118 function updateDebugTextFromInput() {
119 let originalDisplay = `${originalLocation.position} (${originalLocation.lineNumber}, ${originalLocation.columnNumber})`;
120 let formattedDisplay = `${formattedLocation.position} (${formattedLocation.lineNumber}, ${formattedLocation.columnNumber})`;
123 debugText += "Original Location:\n";
124 debugText += originalDisplay + "\n";
126 debugText += "Formatted Location:\n";
127 debugText += formattedDisplay + "\n";
128 debugPre.textContent = debugText;
131 let outputCursorElem = document.createElement("div");
132 outputCursorElem.style.display = "inline-block";
133 outputCursorElem.style.backgroundColor = "red";
134 outputCursorElem.style.width = "2px";
135 outputCursorElem.textContent = " ";
137 function updateOutputCursorFromInput() {
139 let codeMirrorPosition = {line: formattedLocation.lineNumber, ch: formattedLocation.columnNumber};
140 outputMarker = outputCM.setBookmark(codeMirrorPosition, outputCursorElem);
143 // Output has changed, update Input.
145 let reverseOriginalLocation = {lineNumber: 0, columnNumber: 0};
146 let reverseFormattedLocation = {lineNumber: 0, columnNumber: 0};
148 outputCM.on("cursorActivity", () => {
152 function updateFromOutput() {
153 updateFormattedLocationFromOutput();
154 updateInputCursorFromOutput();
157 function updateFormattedLocationFromOutput() {
158 if (!formatterSourceMap)
161 let codeMirrorPosition = outputCM.getCursor();
162 let codeMirrorIndex = outputCM.getDoc().indexFromPos(codeMirrorPosition);
163 reverseFormattedLocation = {lineNumber: codeMirrorPosition.line || 0, columnNumber: codeMirrorPosition.ch || 0, position: codeMirrorIndex};
164 reverseOriginalLocation = formatterSourceMap.formattedToOriginal(reverseFormattedLocation.lineNumber, reverseFormattedLocation.columnNumber);
165 reverseOriginalLocation.position = formatterSourceMap.formattedPositionToOriginalPosition(codeMirrorIndex);
168 let inputCursorElem = document.createElement("div");
169 inputCursorElem.style.display = "inline-block";
170 inputCursorElem.style.backgroundColor = "blue";
171 inputCursorElem.style.width = "2px";
172 inputCursorElem.textContent = " ";
174 function updateInputCursorFromOutput() {
176 let codeMirrorPosition = {line: reverseOriginalLocation.lineNumber, ch: reverseOriginalLocation.columnNumber};
177 inputMarker = inputCM.setBookmark(codeMirrorPosition, inputCursorElem);
186 const indentString = " ";
187 const includeSourceMapData = true;
188 let workerProxy = WI.FormatterWorkerProxy.singleton();
190 switch (modePicker.value) {
192 workerProxy.formatHTML(inputCM.getValue(), indentString, includeSourceMapData, formatResult);
195 workerProxy.formatJavaScript(inputCM.getValue(), false, indentString, includeSourceMapData, formatResult);
198 workerProxy.formatCSS(inputCM.getValue(), indentString, includeSourceMapData, formatResult);
201 workerProxy.formatXML(inputCM.getValue(), indentString, includeSourceMapData, formatResult);
205 function formatResult({formattedText, sourceMapData}) {
206 outputCM.setValue(formattedText || "");
207 formatterSourceMap = WI.FormatterSourceMap.fromSourceMapData(sourceMapData);
210 debugMappingPre.textContent = JSON.stringify(sourceMapData, (key, value) => {
211 if (Array.isArray(value))
212 return `[${value.join()}]`;
221 document.getElementById("format").addEventListener("click", (event) => {
225 // Save as URL button.
226 document.getElementById("save-as-url").addEventListener("click", (event) => {
227 let content = inputCM.getValue();
228 let mode = modePicker.value;
229 window.location.search = `?content=${encodeURIComponent(content)}&mode=${encodeURIComponent(mode)}`;
232 const simpleHTML = `<!DOCTYPE html>
233 <html><head><title>Test</title>
234 <script src="js/script.js"></`+`script></head>
235 <body><!-- Comment --><div class="foo">
236 <style>body,div,.foo{color:red}p{color:blue}</style>
237 <script>(function(a,b,c){let sum=a;sum+=b;sum+=c;return sum;})()</`+`script>
238 <input type=text><br><p>Test</p></div><p><![CDATA[ Test ]]></p></body></html>`;
239 const simpleJS = `(function(){let a=1;return a+1;})();`;
240 const simpleCSS = `body{color:red;background:blue}*{color:green}`;
241 const simpleXML = `<?xml version="1.0" encoding="iso8859-5"?><outer><inner attr="value">1</inner></outer>`;
244 function updateContentFromPicker() {
246 switch (modePicker.value) {
249 content = simpleHTML;
252 mode = "text/javascript";
267 inputCM.setOption("mode", mode);
268 outputCM.setOption("mode", mode);
269 inputCM.setValue(content);
273 modePicker.addEventListener("change", (event) => {
274 updateContentFromPicker();
277 // Restore better initial value from query string.
279 let queryParams = {};
280 if (window.location.search.length > 0) {
281 let searchString = window.location.search.substring(1);
282 let groups = searchString.split("&");
283 for (let i = 0; i < groups.length; ++i) {
284 let pair = groups[i].split("=");
285 queryParams[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1]);
288 if (queryParams.mode) {
289 modePicker.value = queryParams.mode;
290 updateContentFromPicker();
292 if (queryParams.content)
293 inputCM.setValue(queryParams.content);
296 if (!inputCM.getValue())
297 inputCM.setValue(simpleHTML);