blob: ddafa40e9d0408881d9907eaa37856821d5ac594 [file] [log] [blame]
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>SourceMaps Tool</title>
<style>:root { color-scheme: light dark; }</style>
<link rel="stylesheet" href="../../UserInterface/External/CodeMirror/codemirror.css">
<link rel="stylesheet" href="../../UserInterface/Views/Variables.css">
<link rel="stylesheet" href="../../UserInterface/Views/CodeMirrorOverrides.css">
<link rel="stylesheet" href="../../UserInterface/Views/SyntaxHighlightingDefaultTheme.css">
<link rel="stylesheet" href="styles.css">
<script src="../../UserInterface/External/CodeMirror/codemirror.js"></script>
<script src="../../UserInterface/External/CodeMirror/css.js"></script>
<script src="../../UserInterface/External/CodeMirror/htmlmixed.js"></script>
<script src="../../UserInterface/External/CodeMirror/javascript.js"></script>
<script src="../../UserInterface/External/CodeMirror/xml.js"></script>
<script src="../../UserInterface/Base/WebInspector.js"></script>
<script src="../../UserInterface/Base/LinkedList.js"></script>
<script src="../../UserInterface/Base/ListMultimap.js"></script>
<script src="../../UserInterface/Base/Object.js"></script>
<script src="../../UserInterface/Base/Utilities.js"></script>
<script src="../../UserInterface/Controllers/FormatterSourceMap.js"></script>
<script src="../../UserInterface/Proxies/FormatterWorkerProxy.js"></script>
</head>
<body>
<h1>Debug SourceMaps</h1>
<!-- Controls -->
<select id="mode">
<option>html</option>
<option>javascript</option>
<option>css</option>
<option>xml</option>
</select>
<button id="format">Format</button>
<button id="save-as-url">Save URL</button>
<br><br>
<!-- Editor -->
<div class="editors-container">
<textarea id="input" name="code"></textarea>
<textarea id="output" name="code"></textarea>
</div>
<!-- Output -->
<h3>Debug</h3>
<pre id="debug"></pre>
<h3>Source Mapping</h3>
<pre id="debug-mapping"></pre>
<script>
// Elements.
const modePicker = document.getElementById("mode");
const debugPre = document.getElementById("debug");
const debugMappingPre = document.getElementById("debug-mapping");
// Input Editor.
let inputCM = CodeMirror.fromTextArea(document.getElementById("input"), {lineNumbers: true});
inputCM.setOption("mode", "text/html");
// Output Editor
let outputCM = CodeMirror.fromTextArea(document.getElementById("output"), {lineNumbers: true, readOnly: true});
outputCM.setOption("mode", "text/html");
// Global.
let formatterSourceMap = null;
// Markers.
let inputMarker = null;
let outputMarker = null;
function clearMarkers() {
if (inputMarker)
inputMarker.clear();
if (outputMarker)
outputMarker.clear();
}
// Refresh after changes after a short delay.
let timer = null;
inputCM.on("change", function(codeMirror, change) {
if (timer)
clearTimeout(timer)
timer = setTimeout(function() {
clearTimeout(timer);
timer = null;
refresh();
}, 100);
});
// Input has changed, update Output.
let originalLocation = {lineNumber: 0, columnNumber: 0};
let formattedLocation = {lineNumber: 0, columnNumber: 0};
inputCM.on("cursorActivity", () => {
updateFromInput();
});
function updateFromInput() {
updateFormattedLocationFromInput();
updateDebugTextFromInput();
updateOutputCursorFromInput();
}
function updateFormattedLocationFromInput() {
if (!formatterSourceMap)
return;
let codeMirrorPosition = inputCM.getCursor();
let codeMirrorIndex = inputCM.getDoc().indexFromPos(codeMirrorPosition);
originalLocation = {lineNumber: codeMirrorPosition.line || 0, columnNumber: codeMirrorPosition.ch || 0, position: codeMirrorIndex};
formattedLocation = formatterSourceMap.originalToFormatted(originalLocation.lineNumber, originalLocation.columnNumber);
formattedLocation.position = formatterSourceMap.originalPositionToFormattedPosition(codeMirrorIndex);
}
function updateDebugTextFromInput() {
let originalDisplay = `${originalLocation.position} (${originalLocation.lineNumber}, ${originalLocation.columnNumber})`;
let formattedDisplay = `${formattedLocation.position} (${formattedLocation.lineNumber}, ${formattedLocation.columnNumber})`;
let debugText = "";
debugText = "";
debugText += "Original Location:\n";
debugText += originalDisplay + "\n";
debugText += "\n";
debugText += "Formatted Location:\n";
debugText += formattedDisplay + "\n";
debugPre.textContent = debugText;
}
let outputCursorElem = document.createElement("div");
outputCursorElem.style.display = "inline-block";
outputCursorElem.style.backgroundColor = "red";
outputCursorElem.style.width = "2px";
outputCursorElem.textContent = " ";
function updateOutputCursorFromInput() {
clearMarkers();
let codeMirrorPosition = {line: formattedLocation.lineNumber, ch: formattedLocation.columnNumber};
outputMarker = outputCM.setBookmark(codeMirrorPosition, outputCursorElem);
}
// Output has changed, update Input.
let reverseOriginalLocation = {lineNumber: 0, columnNumber: 0};
let reverseFormattedLocation = {lineNumber: 0, columnNumber: 0};
outputCM.on("cursorActivity", () => {
updateFromOutput();
});
function updateFromOutput() {
updateFormattedLocationFromOutput();
updateInputCursorFromOutput();
}
function updateFormattedLocationFromOutput() {
if (!formatterSourceMap)
return;
let codeMirrorPosition = outputCM.getCursor();
let codeMirrorIndex = outputCM.getDoc().indexFromPos(codeMirrorPosition);
reverseFormattedLocation = {lineNumber: codeMirrorPosition.line || 0, columnNumber: codeMirrorPosition.ch || 0, position: codeMirrorIndex};
reverseOriginalLocation = formatterSourceMap.formattedToOriginal(reverseFormattedLocation.lineNumber, reverseFormattedLocation.columnNumber);
reverseOriginalLocation.position = formatterSourceMap.formattedPositionToOriginalPosition(codeMirrorIndex);
}
let inputCursorElem = document.createElement("div");
inputCursorElem.style.display = "inline-block";
inputCursorElem.style.backgroundColor = "blue";
inputCursorElem.style.width = "2px";
inputCursorElem.textContent = " ";
function updateInputCursorFromOutput() {
clearMarkers();
let codeMirrorPosition = {line: reverseOriginalLocation.lineNumber, ch: reverseOriginalLocation.columnNumber};
inputMarker = inputCM.setBookmark(codeMirrorPosition, inputCursorElem);
}
// --------
function refresh() {
if (timer)
clearTimeout(timer);
const indentString = " ";
const includeSourceMapData = true;
let workerProxy = WI.FormatterWorkerProxy.singleton();
switch (modePicker.value) {
case "html":
workerProxy.formatHTML(inputCM.getValue(), indentString, includeSourceMapData, formatResult);
break;
case "javascript":
workerProxy.formatJavaScript(inputCM.getValue(), false, indentString, includeSourceMapData, formatResult);
break;
case "css":
workerProxy.formatCSS(inputCM.getValue(), indentString, includeSourceMapData, formatResult);
break;
case "xml":
workerProxy.formatXML(inputCM.getValue(), indentString, includeSourceMapData, formatResult);
break;
}
function formatResult({formattedText, sourceMapData}) {
outputCM.setValue(formattedText || "");
formatterSourceMap = WI.FormatterSourceMap.fromSourceMapData(sourceMapData);
updateFromInput();
debugMappingPre.textContent = JSON.stringify(sourceMapData, (key, value) => {
if (Array.isArray(value))
return `[${value.join()}]`;
return value;
}, 2);
}
}
setTimeout(refresh);
// Format button.
document.getElementById("format").addEventListener("click", (event) => {
refresh();
});
// Save as URL button.
document.getElementById("save-as-url").addEventListener("click", (event) => {
let content = inputCM.getValue();
let mode = modePicker.value;
window.location.search = `?content=${encodeURIComponent(content)}&mode=${encodeURIComponent(mode)}`;
});
const simpleHTML = `<!DOCTYPE html>
<html><head><title>Test</title>
<script src="js/script.js"></`+`script></head>
<body><!-- Comment --><div class="foo">
<style>body,div,.foo{color:red}p{color:blue}</style>
<script>(function(a,b,c){let sum=a;sum+=b;sum+=c;return sum;})()</`+`script>
<input type=text><br><p>Test</p></div><p><![CDATA[ Test ]]></p></body></html>`;
const simpleJS = `(function(){let a=1;return a+1;})();`;
const simpleCSS = `body{color:red;background:blue}*{color:green}`;
const simpleXML = `<?xml version="1.0" encoding="iso8859-5"?><outer><inner attr="value">1</inner></outer>`;
// Populate picker
function updateContentFromPicker() {
let mode, content;
switch (modePicker.value) {
case "html":
mode = "text/html";
content = simpleHTML;
break;
case "javascript":
mode = "text/javascript";
content = simpleJS;
break;
case "css":
mode = "text/css";
content = simpleCSS;
break;
case "xml":
mode = "text/xml";
content = simpleXML;
break;
default:
console.assert();
break;
}
inputCM.setOption("mode", mode);
outputCM.setOption("mode", mode);
inputCM.setValue(content);
refresh();
}
modePicker.addEventListener("change", (event) => {
updateContentFromPicker();
});
// Restore better initial value from query string.
(function() {
let queryParams = {};
if (window.location.search.length > 0) {
let searchString = window.location.search.substring(1);
let groups = searchString.split("&");
for (let i = 0; i < groups.length; ++i) {
let pair = groups[i].split("=");
queryParams[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1]);
}
}
if (queryParams.mode) {
modePicker.value = queryParams.mode;
updateContentFromPicker();
}
if (queryParams.content)
inputCM.setValue(queryParams.content);
})();
if (!inputCM.getValue())
inputCM.setValue(simpleHTML);
</script>
</body>
</html>