Web Inspector: CSS Pretty Printing fails to put space between value functions around...
[WebKit-https.git] / Source / WebInspectorUI / Tools / PrettyPrinting / index.html
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         buttons.forEach(function(id) {
167             document.getElementById(id).disabled = true;
168         });
169     }
170     function enableButtons() {
171         buttons.forEach(function(id) {
172             document.getElementById(id).disabled = false;
173         });
174     }
175
176     // Refresh after changes after a short delay.
177     var timer = null;
178     cm.on("change", function(codeMirror, change) {
179         if (timer)
180             clearTimeout(timer)
181         timer = setTimeout(function() {
182             clearTimeout(timer);
183             timer = null;
184             refresh();
185         }, 500);
186     });
187
188     // Output elements.
189     var timeOutput = document.getElementById("time");
190     var prettyPre = document.getElementById("pretty");
191     var debugPre = document.getElementById("debug");
192
193     function refresh() {
194         if (timer)
195             clearTimeout(timer);
196
197         const start = {line: 0, ch: 0};
198         const end = {line: cm.lineCount() - 1};
199
200         // Setup.
201         const indentString = "    ";
202         var originalLineEndings = [];
203         var formattedLineEndings = [];
204         var mapping = {original: [0], formatted: [0]};
205         var builder = new FormatterContentBuilder(mapping, originalLineEndings, formattedLineEndings, 0, 0, indentString);
206         var formatter = new Formatter(cm, builder);
207
208         // Time the formatter.
209         var startTime = Date.now();
210         formatter.format(start, end);
211         var endTime = Date.now();
212
213         // Gather debug information.
214         var debug = formatter.debug(start, end);
215
216         // Output the results.
217         timeOutput.innerText = (endTime - startTime) + "ms";
218         prettyPre.innerText = builder.formattedContent;
219         debugPre.innerText = debug;
220     }
221
222     setTimeout(refresh);
223
224     // Tests.
225     function runTests() {
226         disableButtons();
227         function completedCallback() {
228             enableButtons();
229         }
230
231         if (modePicker.value === "text/javascript")
232             runJavaScriptTests(completedCallback);
233         else
234             runCSSTests(completedCallback);
235     }
236     function runJavaScriptTests(callback) {
237         _runTests(callback, [
238             "js-tests/block-comment.js",
239             "js-tests/single-statement-blocks.js",
240             "js-tests/switch-case-default.js",
241         ]);
242     }
243     function runCSSTests(callback) {
244         _runTests(callback, [
245             "css-tests/basic.css",
246             "css-tests/calc.css",
247             "css-tests/gradient.css",
248             "css-tests/keyframes.css",
249             "css-tests/media-query.css",
250             "css-tests/selectors.css",
251             "css-tests/wrapping.css",
252         ]);
253     }
254     function _runTests(callback, manifest) {
255         var index = -1;
256         var results = [];
257         setTimeout(runNextTest, 0);
258
259         function runNextTest() {
260             // Next test.
261             index++;
262
263             // Done.
264             if (index >= manifest.length) {
265                 if (!index)
266                     results.push("/* No tests for mode: " + modePicker.value);
267                 printResults();
268                 return;
269             }
270
271             // Load test and expected results.
272             var test = manifest[index];
273             var expected = test.replace(/\.([^\.]+)$/, "-expected.$1");
274             var xhr1 = new XMLHttpRequest;
275             xhr1.open("GET", test, false);
276             xhr1.send();
277             var testData = xhr1.responseText;
278             var xhr2 = new XMLHttpRequest;
279             xhr2.open("GET", expected, false);
280             xhr2.send();
281             var expectedData = xhr2.responseText;
282
283             // Run the test.
284             var editor = CodeMirror(document.createElement("div"));
285             editor.setOption("mode", modePicker.value);
286             editor.setValue(testData);
287             const start = {line: 0, ch: 0};
288             const end = {line: editor.lineCount() - 1};
289             const indentString = "    ";
290             var originalLineEndings = [];
291             var formattedLineEndings = [];
292             var mapping = {original: [0], formatted: [0]};
293             var builder = new FormatterContentBuilder(mapping, originalLineEndings, formattedLineEndings, 0, 0, indentString);
294             var formatter = new Formatter(editor, builder);
295             formatter.format(start, end);
296
297             // Compare results.
298             var pass = builder.formattedContent === expectedData;
299             results.push("/* " + (pass ? "PASS" : "FAIL") + ": " + test + " */");
300
301             // Output failures to console.
302             if (!pass) {
303                 console.log("Test", test, "Expected", expected);
304                 console.log("Formatted Output", builder.formattedContent.length);
305                 console.log(builder.formattedContent);
306                 console.log("Expected Output", expectedData.length);
307                 console.log(expectedData);
308             }
309
310             runNextTest();
311         }
312
313         function printResults() {
314             cm.setValue(results.join("\n"));
315             cm.refresh();
316             callback();
317         }
318     }
319
320     </script>
321 </body>
322 </html>