Web Inspector: Formatter: Pretty Print HTML resources (including inline <script>...
[WebKit-https.git] / Source / WebInspectorUI / Tools / JSFormatter / index.html
1 <!DOCTYPE html>
2 <html>
3 <head>
4     <title>JSFormatter Tool</title>
5     <link rel="stylesheet" href="../../UserInterface/External/CodeMirror/codemirror.css">
6     <link rel="stylesheet" href="codemirror-additions.css">
7     <script src="../../UserInterface/External/CodeMirror/codemirror.js"></script>
8     <script src="../../UserInterface/External/CodeMirror/javascript.js"></script>
9
10     <script src="../../UserInterface/External/Esprima/esprima.js"></script>
11     <script src="../../UserInterface/Workers/Formatter/FormatterContentBuilder.js"></script>
12     <script src="../../UserInterface/Workers/Formatter/FormatterUtilities.js"></script>
13     <script src="../../UserInterface/Workers/Formatter/ESTreeWalker.js"></script>
14     <script src="../../UserInterface/Workers/Formatter/JSFormatter.js"></script>
15     <script src="JSFormatterDebug.js"></script>
16 </head>
17 <body>
18     <h1>Debug JSFormatter</h1>
19
20     <!-- Controls -->
21     <button id="populate">Populate</button>
22     <select id="load-individual-test"><option>-- Load Test --</option></select>
23     <small>
24         <label><input id="program" type="radio" name="program-or-module"> Program</label>
25         <label><input id="module" type="radio" name="program-or-module" checked> Module</label>
26     </small>
27     <button id="run-tests">Run All Tests</button>
28     <button id="clear">Clear</button>
29     <button id="select-output">Select Output</button>
30     <button id="run-again">Run Again</button>
31     <button id="save-as-url">Save URL</button>
32     <small id="time"></small>
33     <br><br>
34
35     <!-- Editor -->
36     <textarea id="code" name="code"></textarea>
37
38     <!-- Output -->
39     <pre id="pretty"></pre>
40     <pre id="debug"></pre>
41
42     <script>
43     const testURLPrefix = "../../../../LayoutTests/inspector/formatting/resources/javascript-tests/";
44
45     let tests = [
46         "if-statement.js",
47         "import.js",
48         "for-statements.js",
49         "while-statement.js",
50         "do-while-statement.js",
51         "try-catch-finally-statements.js",
52         "switch-case-default.js",
53         "object-array-literal.js",
54         "unary-binary-expressions.js",
55         "logic-expressions.js",
56         "ternary-expressions.js",
57         "new-expression.js",
58         "numbers.js",
59         "label-break-continue-block.js",
60         "with-statement.js",
61         "return-statement.js",
62         "other-statements.js",
63         "variable-declaration.js",
64         "functions.js",
65         "classes.js",
66         "arrow-functions.js",
67         "generators.js",
68         "comments-and-preserve-newlines.js",
69         "comments-only.js",
70         "template-strings.js",
71         "sample-webinspector-object.js",
72         "sample-normal-utilities.js",
73         "sample-jquery.js",
74         "modules.js",
75     ];
76
77     // Initial values from URL.
78     let queryParams = {};
79     if (window.location.search.length > 0) {
80         let searchString = window.location.search.substring(1);
81         let groups = searchString.split("&");
82         for (let i = 0; i < groups.length; ++i) {
83             let pair = groups[i].split("=");
84             queryParams[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1]);
85         }
86     }
87
88     // Initial content.
89     let content = "(function(){let a=1;return a+1;})();";
90     if (queryParams.content)
91         content = queryParams.content || "";
92     if (queryParams.program)
93         document.getElementById("program").checked = true;
94
95     // Setup CodeMirror.
96     let cm = CodeMirror.fromTextArea(document.getElementById("code"), {lineNumbers: true});
97     cm.setValue(content);
98     cm.setOption("mode", "text/javascript");
99
100     // Populate button to populate with some canned content.
101     document.getElementById("populate").addEventListener("click", function(event) {
102         loadIndividualTest("sample-jquery.js");
103     });
104
105     // Fill the individual tests button.
106     let testSelectElement = document.getElementById("load-individual-test");
107     testSelectElement.addEventListener("change", function(event) {
108         let selectedOption = testSelectElement.selectedOptions[0];
109         let test = selectedOption.value;
110         loadIndividualTest(test);
111         testSelectElement.selectedIndex = 0;
112     });
113
114     // Load up the known tests.
115     for (let test of tests) {
116         let option = testSelectElement.appendChild(document.createElement("option"));
117         option.textContent = test;
118         option.value = test;
119     }
120
121     // Program / Module checkboxes.
122     let programCheckbox = document.getElementById("program");
123     let moduleCheckbox = document.getElementById("module");
124     programCheckbox.addEventListener("change", refresh);
125     moduleCheckbox.addEventListener("change", refresh);
126
127     // Run Tests button.
128     document.getElementById("run-tests").addEventListener("click", function(event) {
129         cm.setValue("/* Running Tests... */");
130         refresh();
131         runAllTests();
132     });
133
134     // Clear button.
135     document.getElementById("clear").addEventListener("click", function(event) {
136         cm.setValue("");
137         refresh();
138     });
139
140     // Select output button.
141     document.getElementById("select-output").addEventListener("click", function(event) {
142         let range = document.createRange();
143         range.selectNodeContents(document.getElementById("pretty"));
144         let selection = window.getSelection();
145         selection.removeAllRanges();
146         selection.addRange(range);
147     });
148
149     // Run again button.
150     document.getElementById("run-again").addEventListener("click", function(event) {
151         refresh();
152     });
153
154     // Save as URL button.
155     document.getElementById("save-as-url").addEventListener("click", function(event) {
156         let content = cm.getValue();
157         let queryString = "?content=" + window.encodeURIComponent(content);
158         if (programCheckbox.checked)
159             queryString += "&program";
160         window.location.search = queryString;
161     });
162
163     // Refresh after changes after a short delay.
164     let timer = null;
165     cm.on("change", function(codeMirror, change) {
166         if (timer)
167             clearTimeout(timer)
168         timer = setTimeout(function() {
169             clearTimeout(timer);
170             timer = null;
171             refresh();
172         }, 500);
173     });
174
175     // Output elements.
176     let timeOutput = document.getElementById("time");
177     let prettyPre = document.getElementById("pretty");
178     let debugPre = document.getElementById("debug");
179
180     // Current value of checkboxes.
181     function currentSourceType() {
182         if (programCheckbox.checked)
183             return JSFormatter.SourceType.Script;
184         if (moduleCheckbox.checked)
185             return JSFormatter.SourceType.Module;
186         console.assert(false, "Program or Module radio button should be checked");
187         return undefined;
188     }
189
190     function refresh() {
191         if (timer)
192             clearTimeout(timer);
193
194         // Time the formatter.
195         let startTime = Date.now();
196         let formatter = new JSFormatter(cm.getValue(), currentSourceType());
197         let endTime = Date.now();
198
199         // Show debug info.
200         let debugText;
201         try {
202             let debugFormatter = new JSFormatterDebug(cm.getValue(), currentSourceType());
203             debugText = debugFormatter.debugText;
204         } catch (error) {
205             debugText = "Parse error: " + JSON.stringify(error, null, 2);
206         }
207
208         // Output the results.
209         timeOutput.innerText = (endTime - startTime) + "ms";
210         prettyPre.innerText = formatter.formattedText;
211         debugPre.innerText = debugText;
212     }
213
214     setTimeout(refresh);
215
216     // Testing.
217
218     function isModuleTest(test) {
219         return test === "modules.js";
220     }
221
222     function loadIndividualTest(test) {
223         let testURL = testURLPrefix + test;
224         let xhr = new XMLHttpRequest;
225         xhr.open("GET", testURL, true);
226         xhr.onload = () => { cm.setValue(xhr.responseText); setTimeout(refresh); }
227         xhr.send();
228
229         if (isModuleTest(test))
230             moduleCheckbox.checked = true;
231         else
232             programCheckbox.checked = true;
233     }
234
235     function runAllTests() {
236         let index = -1;
237         let results = [];
238         setTimeout(runNextTest, 0);
239
240         function runNextTest() {
241             // Next test.
242             index++;
243
244             // Done.
245             if (index >= tests.length) {
246                 printResults();
247                 return;
248             }
249
250             // Load test and expected results.
251             let test = tests[index];
252             let testURL = testURLPrefix + test;
253             let expectedURL = testURL.replace(/\.([^\.]+)$/, "-expected.$1");
254             let xhr1 = new XMLHttpRequest;
255             xhr1.open("GET", testURL, false);
256             xhr1.send();
257             let xhr2 = new XMLHttpRequest;
258             xhr2.open("GET", expectedURL, false);
259             xhr2.send();
260             let testData = xhr1.responseText;
261             let expectedData = xhr2.responseText;
262
263             // Run the test.
264             let sourceType = isModuleTest(test) ? JSFormatter.SourceType.Module : JSFormatter.SourceType.Script;
265             let formatter = new JSFormatter(testData, sourceType);
266
267             // Compare results.
268             let pass = formatter.formattedText === expectedData;
269             results.push("/* " + (pass ? "PASS" : "!! FAIL") + ": " + test + " */");
270
271             // Output failures to console.
272             if (!pass) {
273                 console.log("Test", test);
274                 console.log("Formatted Output", formatter.formattedText ? formatter.formattedText.length : null);
275                 console.log(formatter.formattedText);
276                 console.log("Expected Output", expectedData.length);
277                 console.log(expectedData);
278             }
279
280             runNextTest();
281         }
282
283         function printResults() {
284             cm.setValue(results.join("\n"));
285             cm.refresh();
286         }
287     }
288     </script>
289 </body>
290 </html>