1import { Suites, Tags } from "./tests.mjs"; 2import { params, defaultParams } from "./params.mjs"; 3 4export function createDeveloperModeContainer() { 5 const container = document.createElement("div"); 6 container.className = "developer-mode"; 7 8 const details = document.createElement("details"); 9 const summary = document.createElement("summary"); 10 summary.textContent = "Developer Mode"; 11 details.append(summary); 12 13 const content = document.createElement("div"); 14 content.className = "developer-mode-content"; 15 16 content.append(createUIForSuites()); 17 18 const settings = document.createElement("div"); 19 settings.className = "settings"; 20 settings.append(createUIForIterationCount()); 21 settings.append(createUIForMeasurementMethod()); 22 settings.append(createUIForWarmupSuite()); 23 settings.append(createUIForWarmupBeforeSync()); 24 settings.append(createUIForSyncStepDelay()); 25 26 content.append(document.createElement("hr")); 27 content.append(settings); 28 29 content.append(document.createElement("hr")); 30 content.append(createUIForRun()); 31 32 details.append(content);
33 container.append(details); 34 return container; 35} 36 37function span(text) { 38 const span = document.createElement("span"); 39 span.textContent = text; 40 return span; 41} 42 43function createUIForMeasurementMethod() { 44 let check = document.createElement("input"); 45 check.type = "checkbox"; 46 check.id = "measurement-method"; 47 check.checked = params.measurementMethod === "raf"; 48 49 check.onchange = () => { 50 params.measurementMethod = check.checked ? "raf" : "timer"; 51 updateURL(); 52 }; 53 54 let label = document.createElement("label"); 55 label.append(check, " ", span("rAF timing")); 56 57 return label; 58} 59 60function createUIForWarmupSuite() { 61 let check = document.createElement("input"); 62 check.type = "checkbox"; 63 check.id = "warmup-suite"; 64 check.checked = !!params.useWarmupSuite; 65 66 check.onchange = () => { 67 params.useWarmupSuite = check.checked; 68 updateURL(); 69 }; 70 71 let label = document.createElement("label"); 72 label.append(check, " ", span("Use Warmup Suite")); 73 74 return label; 75} 76 77function createUIForIterationCount() { 78 const { range, label } = createTimeRangeUI("Iterations: ", params.iterationCount, "#", 1, 200); 79 range.onchange = () => { 80 params.iterationCount = parseInt(range.value); 81 updateURL(); 82 }; 83 return label; 84} 85 86function createUIForWarmupBeforeSync() { 87 const { range, label } = createTimeRangeUI("Warmup time: ", params.warmupBeforeSync); 88 range.onchange = () => { 89 params.warmupBeforeSync = parseInt(range.value); 90 updateURL(); 91 }; 92 return label; 93} 94 95function createUIForSyncStepDelay() { 96 const { range, label } = createTimeRangeUI("Sync step delay: ", params.waitBeforeSync); 97 range.onchange = () => { 98 params.waitBeforeSync = parseInt(range.value); 99 updateURL(); 100 }; 101 return label; 102} 103 104function createTimeRangeUI(labelText, initialValue, unit = "ms", min = 0, max = 1000) { 105 const range = document.createElement("input"); 106 range.type = "range"; 107 range.min = min; 108 range.max = max; 109 range.value = initialValue; 110 111 const rangeValueAndUnit = document.createElement("span"); 112 rangeValueAndUnit.className = "range-label-data"; 113 114 const rangeValue = document.createElement("span"); 115 rangeValue.textContent = initialValue; 116 rangeValueAndUnit.append(rangeValue, " ", unit); 117 118 const label = document.createElement("label"); 119 label.append(span(labelText), range, rangeValueAndUnit); 120 121 range.oninput = () => { 122 rangeValue.textContent = range.value; 123 }; 124 125 return { range, label }; 126} 127 128function createUIForSuites() { 129 const control = document.createElement("nav"); 130 control.className = "suites"; 131 const ol = document.createElement("ol"); 132 const checkboxes = []; 133 const setSuiteEnabled = (suiteIndex, enabled) => { 134 Suites[suiteIndex].disabled = !enabled; 135 checkboxes[suiteIndex].checked = enabled; 136 }; 137 138 for (const suite of Suites) { 139 const li = document.createElement("li"); 140 const checkbox = document.createElement("input"); 141 checkbox.id = suite.name; 142 checkbox.type = "checkbox"; 143 checkbox.checked = !suite.disabled; 144 checkbox.onchange = () => { 145 suite.disabled = !checkbox.checked; 146 updateURL(); 147 }; 148 checkboxes.push(checkbox); 149 150 const label = document.createElement("label"); 151 label.append(checkbox, " ", suite.name); 152 li.appendChild(label); 153 label.onclick = (event) => { 154 if (event?.ctrlKey || event?.metaKey) { 155 for (let suiteIndex = 0; suiteIndex < Suites.length; suiteIndex++) { 156 if (Suites[suiteIndex] !== suite) 157 setSuiteEnabled(suiteIndex, false); 158 else 159 setSuiteEnabled(suiteIndex, true); 160 } 161 } 162 }; 163 164 ol.appendChild(li); 165 } 166 control.appendChild(ol); 167 let buttons = control.appendChild(document.createElement("div")); 168 buttons.className = "button-bar"; 169 170 let button = document.createElement("button"); 171 button.textContent = "Select all"; 172 button.onclick = () => { 173 for (let suiteIndex = 0; suiteIndex < Suites.length; suiteIndex++) 174 setSuiteEnabled(suiteIndex, true); 175 176 updateURL(); 177 }; 178 buttons.appendChild(button); 179 180 button = document.createElement("button"); 181 button.textContent = "Unselect all"; 182 button.onclick = () => { 183 for (let suiteIndex = 0; suiteIndex < Suites.length; suiteIndex++) 184 setSuiteEnabled(suiteIndex, false); 185 186 updateURL(); 187 }; 188 buttons.appendChild(button); 189 190 let i = 0;
191 const kTagsPerLine = 3; 192 for (const tag of Tags) { 193 if (tag === "all") 194 continue; 195 if (!(i % kTagsPerLine)) { 196 buttons = control.appendChild(document.createElement("div")); 197 buttons.className = "button-bar"; 198 } 199 i++; 200 button = document.createElement("button"); 201 button.className = "tag"; 202 button.textContent = `#${tag}`; 203 button.dataTag = tag; 204 button.onclick = (event) => { 205 const extendSelection = event?.shiftKey; 206 const invertSelection = event?.ctrlKey || event?.metaKey; 207 const selectedTag = event.target.dataTag; 208 for (let suiteIndex = 0; suiteIndex < Suites.length; suiteIndex++) { 209 let enabled = Suites[suiteIndex].tags.includes(selectedTag); 210 if (invertSelection) 211 enabled = !enabled; 212 if (extendSelection && !enabled) 213 continue; 214 setSuiteEnabled(suiteIndex, enabled); 215 } 216 updateURL(); 217 }; 218 buttons.appendChild(button); 219 } 220 221 return control; 222} 223 224function createUIForRun() { 225 let button = document.createElement("button"); 226 button.textContent = "Start Test"; 227 button.onclick = (event) => { 228 globalThis.benchmarkClient.start(); 229 }; 230 let buttons = document.createElement("div"); 231 buttons.className = "button-bar"; 232 buttons.appendChild(button); 233 return buttons; 234} 235 236function updateURL() { 237 const url = new URL(window.location.href); 238 239 // If less than all suites are selected then change the URL "Suites" GET parameter 240 // to comma separate only the selected 241 const selectedSuites = Suites.filter((suite) => !suite.disabled); 242 243 if (!selectedSuites.length) { 244 url.searchParams.delete("tags"); 245 url.searchParams.delete("suites"); 246 url.searchParams.delete("suite"); 247 } else { 248 url.searchParams.delete("tags"); 249 url.searchParams.delete("suite"); 250 // Try finding common tags that would result in the current suite selection. 251 let commonTags = new Set(selectedSuites[0].tags); 252 for (const suite of Suites) { 253 if (suite.disabled) 254 suite.tags.forEach((tag) => commonTags.delete(tag)); 255 else 256 commonTags = new Set(suite.tags.filter((tag) => commonTags.has(tag))); 257 } 258 if (commonTags.size) { 259 const tags = [...commonTags][0]; 260 if (tags === "default") 261 url.searchParams.delete("tags"); 262 else 263 url.searchParams.set("tags", tags); 264 url.searchParams.delete("suites"); 265 } else { 266 url.searchParams.delete("tags"); 267 url.searchParams.set("suites", selectedSuites.map((suite) => suite.name).join(",")); 268 } 269 } 270 271 if (params.measurementMethod !== "raf") 272 url.searchParams.set("measurementMethod", "timer"); 273 else 274 url.searchParams.delete("measurementMethod"); 275 276 const boolParamKeys = ["iterationCount", "useWarmupSuite", "warmupBeforeSync", "waitBeforeSync"]; 277 for (const paramKey of boolParamKeys) { 278 if (params[paramKey] !== defaultParams[paramKey]) 279 url.searchParams.set(paramKey, params[paramKey]); 280 else 281 url.searchParams.delete(paramKey); 282 } 283 284 // Only push state if changed 285 url.search = decodeURIComponent(url.search); 286 if (url.href !== window.location.href) 287 window.history.pushState({}, "", url); 288}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.