Source code

Revision control

Copy as Markdown

Other Tools

import { Suites, Tags } from "./tests.mjs";
import { params, defaultParams } from "./params.mjs";
export function createDeveloperModeContainer() {
const container = document.createElement("div");
container.className = "developer-mode";
const details = document.createElement("details");
const summary = document.createElement("summary");
summary.textContent = "Developer Mode";
details.append(summary);
const content = document.createElement("div");
content.className = "developer-mode-content";
content.append(createUIForSuites());
const settings = document.createElement("div");
settings.className = "settings";
settings.append(createUIForIterationCount());
settings.append(createUIForMeasurementMethod());
settings.append(createUIForWarmupSuite());
settings.append(createUIForWarmupBeforeSync());
settings.append(createUIForSyncStepDelay());
content.append(document.createElement("hr"));
content.append(settings);
content.append(document.createElement("hr"));
content.append(createUIForRun());
details.append(content);
container.append(details);
return container;
}
function span(text) {
const span = document.createElement("span");
span.textContent = text;
return span;
}
function createUIForMeasurementMethod() {
let check = document.createElement("input");
check.type = "checkbox";
check.id = "measurement-method";
check.checked = params.measurementMethod === "raf";
check.onchange = () => {
params.measurementMethod = check.checked ? "raf" : "timer";
updateURL();
};
let label = document.createElement("label");
label.append(check, " ", span("rAF timing"));
return label;
}
function createUIForWarmupSuite() {
let check = document.createElement("input");
check.type = "checkbox";
check.id = "warmup-suite";
check.checked = !!params.useWarmupSuite;
check.onchange = () => {
params.useWarmupSuite = check.checked;
updateURL();
};
let label = document.createElement("label");
label.append(check, " ", span("Use Warmup Suite"));
return label;
}
function createUIForIterationCount() {
const { range, label } = createTimeRangeUI("Iterations: ", params.iterationCount, "#", 1, 200);
range.onchange = () => {
params.iterationCount = parseInt(range.value);
updateURL();
};
return label;
}
function createUIForWarmupBeforeSync() {
const { range, label } = createTimeRangeUI("Warmup time: ", params.warmupBeforeSync);
range.onchange = () => {
params.warmupBeforeSync = parseInt(range.value);
updateURL();
};
return label;
}
function createUIForSyncStepDelay() {
const { range, label } = createTimeRangeUI("Sync step delay: ", params.waitBeforeSync);
range.onchange = () => {
params.waitBeforeSync = parseInt(range.value);
updateURL();
};
return label;
}
function createTimeRangeUI(labelText, initialValue, unit = "ms", min = 0, max = 1000) {
const range = document.createElement("input");
range.type = "range";
range.min = min;
range.max = max;
range.value = initialValue;
const rangeValueAndUnit = document.createElement("span");
rangeValueAndUnit.className = "range-label-data";
const rangeValue = document.createElement("span");
rangeValue.textContent = initialValue;
rangeValueAndUnit.append(rangeValue, " ", unit);
const label = document.createElement("label");
label.append(span(labelText), range, rangeValueAndUnit);
range.oninput = () => {
rangeValue.textContent = range.value;
};
return { range, label };
}
function createUIForSuites() {
const control = document.createElement("nav");
control.className = "suites";
const ol = document.createElement("ol");
const checkboxes = [];
const setSuiteEnabled = (suiteIndex, enabled) => {
Suites[suiteIndex].disabled = !enabled;
checkboxes[suiteIndex].checked = enabled;
};
for (const suite of Suites) {
const li = document.createElement("li");
const checkbox = document.createElement("input");
checkbox.id = suite.name;
checkbox.type = "checkbox";
checkbox.checked = !suite.disabled;
checkbox.onchange = () => {
suite.disabled = !checkbox.checked;
updateURL();
};
checkboxes.push(checkbox);
const label = document.createElement("label");
label.append(checkbox, " ", suite.name);
li.appendChild(label);
label.onclick = (event) => {
if (event?.ctrlKey || event?.metaKey) {
for (let suiteIndex = 0; suiteIndex < Suites.length; suiteIndex++) {
if (Suites[suiteIndex] !== suite)
setSuiteEnabled(suiteIndex, false);
else
setSuiteEnabled(suiteIndex, true);
}
}
};
ol.appendChild(li);
}
control.appendChild(ol);
let buttons = control.appendChild(document.createElement("div"));
buttons.className = "button-bar";
let button = document.createElement("button");
button.textContent = "Select all";
button.onclick = () => {
for (let suiteIndex = 0; suiteIndex < Suites.length; suiteIndex++)
setSuiteEnabled(suiteIndex, true);
updateURL();
};
buttons.appendChild(button);
button = document.createElement("button");
button.textContent = "Unselect all";
button.onclick = () => {
for (let suiteIndex = 0; suiteIndex < Suites.length; suiteIndex++)
setSuiteEnabled(suiteIndex, false);
updateURL();
};
buttons.appendChild(button);
let i = 0;
const kTagsPerLine = 3;
for (const tag of Tags) {
if (tag === "all")
continue;
if (!(i % kTagsPerLine)) {
buttons = control.appendChild(document.createElement("div"));
buttons.className = "button-bar";
}
i++;
button = document.createElement("button");
button.className = "tag";
button.textContent = `#${tag}`;
button.dataTag = tag;
button.onclick = (event) => {
const extendSelection = event?.shiftKey;
const invertSelection = event?.ctrlKey || event?.metaKey;
const selectedTag = event.target.dataTag;
for (let suiteIndex = 0; suiteIndex < Suites.length; suiteIndex++) {
let enabled = Suites[suiteIndex].tags.includes(selectedTag);
if (invertSelection)
enabled = !enabled;
if (extendSelection && !enabled)
continue;
setSuiteEnabled(suiteIndex, enabled);
}
updateURL();
};
buttons.appendChild(button);
}
return control;
}
function createUIForRun() {
let button = document.createElement("button");
button.textContent = "Start Test";
button.onclick = (event) => {
globalThis.benchmarkClient.start();
};
let buttons = document.createElement("div");
buttons.className = "button-bar";
buttons.appendChild(button);
return buttons;
}
function updateURL() {
const url = new URL(window.location.href);
// If less than all suites are selected then change the URL "Suites" GET parameter
// to comma separate only the selected
const selectedSuites = Suites.filter((suite) => !suite.disabled);
if (!selectedSuites.length) {
url.searchParams.delete("tags");
url.searchParams.delete("suites");
url.searchParams.delete("suite");
} else {
url.searchParams.delete("tags");
url.searchParams.delete("suite");
// Try finding common tags that would result in the current suite selection.
let commonTags = new Set(selectedSuites[0].tags);
for (const suite of Suites) {
if (suite.disabled)
suite.tags.forEach((tag) => commonTags.delete(tag));
else
commonTags = new Set(suite.tags.filter((tag) => commonTags.has(tag)));
}
if (commonTags.size) {
const tags = [...commonTags][0];
if (tags === "default")
url.searchParams.delete("tags");
else
url.searchParams.set("tags", tags);
url.searchParams.delete("suites");
} else {
url.searchParams.delete("tags");
url.searchParams.set("suites", selectedSuites.map((suite) => suite.name).join(","));
}
}
if (params.measurementMethod !== "raf")
url.searchParams.set("measurementMethod", "timer");
else
url.searchParams.delete("measurementMethod");
const boolParamKeys = ["iterationCount", "useWarmupSuite", "warmupBeforeSync", "waitBeforeSync"];
for (const paramKey of boolParamKeys) {
if (params[paramKey] !== defaultParams[paramKey])
url.searchParams.set(paramKey, params[paramKey]);
else
url.searchParams.delete(paramKey);
}
// Only push state if changed
url.search = decodeURIComponent(url.search);
if (url.href !== window.location.href)
window.history.pushState({}, "", url);
}