225 строки
5.4 KiB
JavaScript
225 строки
5.4 KiB
JavaScript
let options = {
|
|
property: {
|
|
value: null,
|
|
type: "option",
|
|
},
|
|
value: {
|
|
value: null,
|
|
type: "option",
|
|
},
|
|
};
|
|
|
|
function getOption(name) {
|
|
return options[name].value;
|
|
}
|
|
|
|
function getOptionType(name) {
|
|
return options[name].type;
|
|
}
|
|
|
|
function setOption(name, value) {
|
|
return (options[name].value = value);
|
|
}
|
|
|
|
let onLoad = new Promise(function (resolve, reject) {
|
|
window.onload = resolve;
|
|
});
|
|
|
|
function setURL() {
|
|
let url = new URL(location.href);
|
|
|
|
let property = getOption("property");
|
|
let value = getOption("value");
|
|
if (!property) {
|
|
url.search = "";
|
|
} else if (!value) {
|
|
url.search = "?property=" + property;
|
|
} else {
|
|
url.search = "?property=" + property + "&value=" + value;
|
|
}
|
|
|
|
history.replaceState({}, document.title, url.href);
|
|
}
|
|
|
|
function populateTable() {
|
|
let property = getOption("property");
|
|
let value = getOption("value");
|
|
if (!property || !value) {
|
|
return;
|
|
}
|
|
|
|
let channels = ["nightly", "beta", "release"];
|
|
|
|
let results = {};
|
|
|
|
Promise.all(
|
|
channels.map((channel, index) =>
|
|
correlations
|
|
.getChannelsPercentage("Firefox", channel, property, value)
|
|
.then((result) => (results[channel] = result))
|
|
)
|
|
).then(() => {
|
|
let table = document.getElementById("table");
|
|
while (table.rows.length > 1) {
|
|
table.deleteRow(table.rows.length - 1);
|
|
}
|
|
|
|
let row = table.insertRow(table.rows.length);
|
|
|
|
channels.forEach((channel, index) => {
|
|
let cell = row.insertCell(index);
|
|
cell.appendChild(
|
|
document.createTextNode(
|
|
correlations.toPercentage(
|
|
(results[channel] && results[channel].p) || 0
|
|
) + " %"
|
|
)
|
|
);
|
|
});
|
|
|
|
let svgElem = document.getElementById("image");
|
|
|
|
d3.select(svgElem).selectAll("*").remove();
|
|
|
|
let margin = { top: 20, right: 300, bottom: 70, left: 300 };
|
|
let width = svgElem.getAttribute("width") - margin.left - margin.right;
|
|
let height = svgElem.getAttribute("height") - margin.top - margin.bottom;
|
|
|
|
let x = d3.scale.ordinal().rangeRoundBands([0, width], 0.05);
|
|
let y = d3.scale.linear().range([height, 0]);
|
|
|
|
let color = d3.scale.ordinal().range(["black", "blue", "red", "orange"]);
|
|
|
|
let xAxis = d3.svg.axis().scale(x).orient("bottom");
|
|
|
|
let yAxis = d3.svg.axis().scale(y).orient("left").ticks(10);
|
|
|
|
var svg = d3
|
|
.select(svgElem)
|
|
.attr("width", width + margin.left + margin.right)
|
|
.attr("height", height + margin.top + margin.bottom)
|
|
.append("g")
|
|
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
|
|
|
|
let data = channels.map((channel) => {
|
|
return {
|
|
channel: channel,
|
|
value: ((results[channel] && results[channel].p) || 0) * 100,
|
|
};
|
|
});
|
|
|
|
x.domain(channels);
|
|
y.domain([
|
|
0,
|
|
d3.max(data, function (d) {
|
|
return d.value;
|
|
}),
|
|
]);
|
|
|
|
svg
|
|
.append("g")
|
|
.attr("class", "x axis")
|
|
.attr("transform", "translate(27," + height + ")")
|
|
.call(xAxis)
|
|
.selectAll("text")
|
|
.style("text-anchor", "end")
|
|
.attr("dx", "-.8em")
|
|
.attr("dy", "-.55em")
|
|
.attr("transform", "rotate(-90)");
|
|
|
|
svg
|
|
.append("g")
|
|
.attr("class", "y axis")
|
|
.call(yAxis)
|
|
.append("text")
|
|
.attr("transform", "rotate(-90)")
|
|
.attr("y", 3)
|
|
.attr("dy", ".71em")
|
|
.style("text-anchor", "end")
|
|
.text("Percentage of users (%)");
|
|
|
|
svg
|
|
.selectAll("bar")
|
|
.data(data)
|
|
.enter()
|
|
.append("rect")
|
|
.style("fill", (d) => color(d.channel))
|
|
.attr("x", function (d) {
|
|
return 27 + x(d.channel);
|
|
})
|
|
.attr("width", x.rangeBand())
|
|
.attr("y", function (d) {
|
|
return y(d.value);
|
|
})
|
|
.attr("height", function (d) {
|
|
return height - y(d.value);
|
|
});
|
|
});
|
|
}
|
|
|
|
function populateValueSelect() {
|
|
let property = getOption("property");
|
|
if (!property) {
|
|
return;
|
|
}
|
|
|
|
correlations.getChannelsValues("Firefox", property).then((values) => {
|
|
if ($("#value")[0].selectize) {
|
|
$("#value")[0].selectize.destroy();
|
|
}
|
|
|
|
let $select = $("#value").selectize({
|
|
valueField: "name",
|
|
labelField: "name",
|
|
searchField: "name",
|
|
options: values.map((value) => {
|
|
return { name: value };
|
|
}),
|
|
onChange: (value) => {
|
|
setOption("value", value);
|
|
setURL();
|
|
populateTable();
|
|
},
|
|
});
|
|
|
|
$select[0].selectize.setValue(getOption("value"));
|
|
});
|
|
}
|
|
|
|
onLoad
|
|
.then(() => correlations.getChannelsProperties("Firefox"))
|
|
.then((props) => {
|
|
let queryVars = new URL(location.href).search.substring(1).split("&");
|
|
|
|
Object.keys(options).forEach(function (optionName) {
|
|
for (let queryVar of queryVars) {
|
|
if (queryVar.startsWith(optionName + "=")) {
|
|
let option = queryVar.substring((optionName + "=").length).trim();
|
|
setOption(optionName, decodeURIComponent(option));
|
|
}
|
|
}
|
|
});
|
|
|
|
let $select = $("#property").selectize({
|
|
valueField: "name",
|
|
labelField: "name",
|
|
searchField: "name",
|
|
options: props.map((prop) => {
|
|
return { name: prop };
|
|
}),
|
|
onChange: (prop) => {
|
|
if (prop != getOption("property")) {
|
|
setOption("value", "");
|
|
}
|
|
setOption("property", prop);
|
|
setURL();
|
|
populateValueSelect();
|
|
},
|
|
});
|
|
|
|
$select[0].selectize.setValue(getOption("property"));
|
|
})
|
|
.catch(function (err) {
|
|
console.error(err);
|
|
});
|