1<!DOCTYPE html> 2<html> 3<head> 4 <title>Debug</title> 5 <link rel="stylesheet" href="codemirror.css"> 6 <link rel="stylesheet" href="codemirror-additions.css"> 7 <script src="codemirror.js"></script> 8 <script src="javascript.js"></script> 9 <script src="css.js"></script> 10 <script src="Utilities.js"></script> 11 <script src="Formatter.js"></script> 12 <script src="FormatterDebug.js"></script> 13 <script src="FormatterContentBuilder.js"></script> 14 <script src="CodeMirrorFormatters.js"></script> 15</head> 16<body> 17 18 <h1>Debug Pretty Printing</h1> 19 20 <!-- Controls --> 21 <select id="mode"> 22 <option selected value="text/javascript">JavaScript</option> 23 <option value="text/css">CSS</option> 24 </select> 25 <button id="populate">Populate</button> 26 <button id="run-tests">Run Tests</button> 27 <button id="clear">Clear</button> 28 <button id="select-output">Select Output</button> 29 <button id="run-again">Run Again</button> 30 <button id="save-as-url">Save URL</button> 31 <button id="save-local-storage">Save to Storage</button> 32 <button id="clear-local-storage">Clear Storage</button> 33 <small id="time"></small> 34 35 <br><br> 36 37 <!-- Editor --> 38 <textarea id="code" name="code"></textarea> 39 40 <!-- Output --> 41 <pre id="pretty"></pre> 42 <pre id="debug"></pre> 43 44 <script> 45 // Editor. 46 var cm = CodeMirror.fromTextArea(document.getElementById("code"), { 47 lineNumbers: true, 48 }); 49 50 // Initial values from URL. 51 var queryParams = {}; 52 if (window.location.search.length > 0) { 53 var searchString = window.location.search.substring(1); 54 var groups = searchString.split("&"); 55 for (var i = 0; i < groups.length; ++i) { 56 var pair = groups[i].split("="); 57 queryParams[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1]); 58 } 59 } 60 61 // Initial mode and string. 62 var mode = "text/javascript"; 63 var content = "(function() { var a=1; return a+1; })();"; 64 var updatePicker = false; 65 if (queryParams.content || queryParams.mode) { 66 content = queryParams.content || ""; 67 mode = queryParams.mode || "text/javascript"; 68 updatePicker = true; 69 } else if (localStorage.content || localStorage.mode) { 70 content = localStorage.content || ""; 71 mode = localStorage.mode || "text/javascript"; 72 updatePicker = true; 73 } 74 75 // Initial mode picker value. 76 var modePicker = document.getElementById("mode"); 77 if (updatePicker) { 78 for (var i = 0; i < modePicker.options.length; ++i) { 79 if (modePicker.options[i].value === mode) { 80 modePicker.options[i].selected = true; 81 break; 82 } 83 } 84 } 85 86 // Set on CodeMirror. 87 cm.setValue(content); 88 cm.setOption("mode", mode); 89 90 // Changing the mode. 91 modePicker.addEventListener("change", function(event) { 92 cm.setValue(""); 93 cm.setOption("mode", modePicker.value); 94 refresh(); 95 }); 96 97 // Populate button to populate with some canned content. 98 document.getElementById("populate").addEventListener("click", function(event) { 99 switch (modePicker.value) { 100 case "text/javascript": 101 var url = "populate/jquery.min.js"; 102 break; 103 case "text/css": 104 var url = "populate/apple.css"; 105 break; 106 } 107 108 var xhr = new XMLHttpRequest; 109 xhr.open("GET", url, true); 110 xhr.onload = function() { 111 cm.setValue(xhr.responseText); 112 setTimeout(refresh); 113 } 114 xhr.send(); 115 }); 116 117 // Run Tests button. 118 document.getElementById("run-tests").addEventListener("click", function(event) { 119 cm.setValue("Running Tests..."); 120 refresh(); 121 runTests(); 122 }); 123 124 // Clear button. 125 document.getElementById("clear").addEventListener("click", function(event) { 126 cm.setValue(""); 127 refresh(); 128 }); 129 130 // Select output button. 131 document.getElementById("select-output").addEventListener("click", function(event) { 132 var range = document.createRange(); 133 range.selectNodeContents(document.getElementById("pretty")); 134 var selection = window.getSelection(); 135 selection.removeAllRanges(); 136 selection.addRange(range); 137 }); 138 139 // Run again button. 140 document.getElementById("run-again").addEventListener("click", function(event) { 141 refresh(); 142 }); 143 144 // Save as URL button. 145 document.getElementById("save-as-url").addEventListener("click", function(event) { 146 var mode = modePicker.value; 147 var content = cm.getValue(); 148 window.location.search = "?mode=" + window.encodeURIComponent(mode) + "&content=" + window.encodeURIComponent(content); 149 }); 150 151 // Save to localStorage. 152 document.getElementById("save-local-storage").addEventListener("click", function(event) { 153 localStorage.mode = modePicker.value; 154 localStorage.content = cm.getValue(); 155 }); 156 157 // Clear localStorage. 158 document.getElementById("clear-local-storage").addEventListener("click", function(event) { 159 localStorage.removeItem("mode"); 160 localStorage.removeItem("content"); 161 }); 162 163 // Button helpers. 164 var buttons = ["mode", "populate", "run-tests", "clear", "select-output", "run-again"]; 165 function disableButtons() { 166 console.log("disableButtons"); 167 buttons.forEach(function(id) { 168 document.getElementById(id).disabled = true; 169 }); 170 } 171 function enableButtons() { 172 console.log("enableButtons"); 173 buttons.forEach(function(id) { 174 document.getElementById(id).disabled = false; 175 }); 176 } 177 178 // Refresh after changes after a short delay. 179 var timer = null; 180 cm.on("change", function(codeMirror, change) { 181 if (timer) 182 clearTimeout(timer) 183 timer = setTimeout(function() { 184 clearTimeout(timer); 185 timer = null; 186 refresh(); 187 }, 500); 188 }); 189 190 // Output elements. 191 var timeOutput = document.getElementById("time"); 192 var prettyPre = document.getElementById("pretty"); 193 var debugPre = document.getElementById("debug"); 194 195 function refresh() { 196 if (timer) 197 clearTimeout(timer); 198 199 const start = {line: 0, ch: 0}; 200 const end = {line: cm.lineCount() - 1}; 201 202 // Setup. 203 const indentString = " "; 204 var originalLineEndings = []; 205 var formattedLineEndings = []; 206 var mapping = {original: [0], formatted: [0]}; 207 var builder = new FormatterContentBuilder(mapping, originalLineEndings, formattedLineEndings, 0, 0, indentString); 208 var formatter = new Formatter(cm, builder); 209 210 // Time the formatter. 211 var startTime = Date.now(); 212 formatter.format(start, end); 213 var endTime = Date.now(); 214 215 // Gather debug information. 216 var debug = formatter.debug(start, end); 217 218 // Output the results. 219 timeOutput.innerText = (endTime - startTime) + "ms"; 220 prettyPre.innerText = builder.formattedContent; 221 debugPre.innerText = debug; 222 } 223 224 setTimeout(refresh); 225 226 // Tests. 227 function runTests() { 228 disableButtons(); 229 function completedCallback() { 230 enableButtons(); 231 } 232 233 if (modePicker.value === "text/javascript") 234 runJavaScriptTests(completedCallback); 235 else 236 runCSSTests(completedCallback); 237 } 238 function runJavaScriptTests(callback) { 239 _runTests(callback, [ 240 "js-tests/block-comment.js", 241 "js-tests/single-statement-blocks.js", 242 "js-tests/switch-case-default.js", 243 ]); 244 } 245 function runCSSTests(callback) { 246 _runTests(callback, []); 247 } 248 function _runTests(callback, manifest) { 249 var index = -1; 250 var results = []; 251 setTimeout(runNextTest, 0); 252 253 function runNextTest() { 254 // Next test. 255 index++; 256 257 // Done. 258 if (index >= manifest.length) { 259 if (!index) 260 results.push("/* No tests for mode: " + modePicker.value); 261 printResults(); 262 return; 263 } 264 265 // Load test and expected results. 266 var test = manifest[index]; 267 var expected = test.replace(/\.js$/, "-expected.js"); 268 var xhr1 = new XMLHttpRequest; 269 xhr1.open("GET", test, false); 270 xhr1.send(); 271 var testData = xhr1.responseText; 272 var xhr2 = new XMLHttpRequest; 273 xhr2.open("GET", expected, false); 274 xhr2.send(); 275 var expectedData = xhr2.responseText; 276 277 // Run the test. 278 var editor = CodeMirror(document.createElement("div")); 279 editor.setOption("mode", modePicker.value); 280 editor.setValue(testData); 281 const start = {line: 0, ch: 0}; 282 const end = {line: editor.lineCount() - 1}; 283 const indentString = " "; 284 var originalLineEndings = []; 285 var formattedLineEndings = []; 286 var mapping = {original: [0], formatted: [0]}; 287 var builder = new FormatterContentBuilder(mapping, originalLineEndings, formattedLineEndings, 0, 0, indentString); 288 var formatter = new Formatter(editor, builder); 289 formatter.format(start, end); 290 291 // Compare results. 292 var pass = builder.formattedContent === expectedData; 293 results.push("/* " + (pass ? "PASS" : "FAIL") + ": " + test + " */"); 294 runNextTest(); 295 } 296 297 function printResults() { 298 cm.setValue(results.join("\n")); 299 cm.refresh(); 300 callback(); 301 } 302 } 303 304 </script> 305</body> 306</html> 307