티스토리 수익 글 보기

티스토리 수익 글 보기

/*global Plotly*/ function doPlot() { // stats.js is used by stats.md to download graph data from the webserver, // and then display it using plotly.js. var numFormat = /\d+/; var dateFormat = /\d{4}-\d{2}-\d{2}/; var gHttpsData = { countryList: [], osList: [], dateToCountryOSPageloadData: {}, dateToHistoricalPageloadData: {} }; Plotly.setPlotConfig({locale: document.documentElement.lang}); const loc = document.getElementById(“plot-translations”).dataset; // Process a string `s` and, for each row (line), call `f()` with an // array of each tab-separated value within that row. function parse_delim(s, delim, f) { var ix_end = 0; for (var ix = 0; ix < s.length; ix = ix_end + 1) { ix_end = s.indexOf("\n", ix); if (ix_end == -1) { ix_end = s.length; } var row = s.substring(ix, ix_end).trim().split(delim); f(row); } } // Maintain a moving average function movingAvg(stack, value, window=14) { let total = 0; if (numFormat.test(value)) { stack.push(parseFloat(value)); while (stack.length > window) { stack.shift(0); } } for (let i = 0; i < stack.length; i++) { total = total + stack[i]; } if (stack.length == 0) { return "NULL"; } return total / stack.length; } // Add an (x,y) point to a Trace object if it is a real point. function insertPoint(trace, x, y) { if (numFormat.test(y)) { trace.x.push(x); trace.y.push(parseFloat(y)); } } function tsvListener(text) { var tIssued = { type: "scatter", name: loc.issued, x:[], y:[], fill: "tozeroy", line: { color: someGreen } }; var tActive = { type: "scatter", name: loc.certificates_active, x:[], y:[], line: { color: leOrange, dash: "dot" } }; var tFqdn = { type: "scatter", name: loc.fully_qualified_domains_active, x:[], y:[], line: { color: leBlue, dash: "line" } }; var tRegDom = { type: "scatter", name: loc.registered_domains_active, x:[], y:[], marker: { symbol: "diamond" }, line: { color: someGreen, dash: "dash" } }; parse_delim(text, "\t", function(row){ if (!dateFormat.test(row[0])) { return; } insertPoint(tIssued, row[0], row[1]); insertPoint(tActive, row[0], row[2]); insertPoint(tFqdn, row[0], row[3]); insertPoint(tRegDom, row[0], row[4]); }); const activeUsage = document.getElementById("activeUsage"); if (activeUsage) { activeUsage.innerHTML = ""; // remove waiting plotActiveUsage(activeUsage, tActive, tFqdn, tRegDom); } const issuancePerDay = document.getElementById("issuancePerDay"); if (issuancePerDay) { issuancePerDay.innerHTML = ""; // remove waiting plotIssuancePerDay(issuancePerDay, tIssued); } const combinedTimeline = document.getElementById("combinedTimeline"); if (combinedTimeline) { plotCombinedTimeline(combinedTimeline,tIssued, tActive, tFqdn, tRegDom); } } // Various running aggregates over time function plotActiveUsage(dom, tActive, tFqdn, tRegDom) { let traces = [ tActive, tFqdn, tRegDom ]; let layout = { margin: { t: 20 }, yaxis: { title: loc.active_count, }, legend: { xanchor: "left", yanchor: "top", x: 0, y: 1 } }; Plotly.plot(dom, traces, layout); } // Certificates issued over time function plotIssuancePerDay(dom, tIssued) { let traces = [ tIssued ]; let layout = { margin: { t: 20 }, yaxis: { title: loc.issued_per_day, }, legend: { xanchor: "left", yanchor: "top", x: 0, y: 1 } }; Plotly.plot(dom, traces, layout); } // Combined Graph: issuancePerDay + activeUsage function plotCombinedTimeline(dom, tIssued, tActive, tFqdn, tRegDom) { // Override the axis for the combined graph tIssued.yaxis = "y2"; let traces = [ tActive, tFqdn, tRegDom, tIssued ]; let layout = { margin: { t: 20 }, yaxis: { title: loc.active_count, side: "right" }, yaxis2: { title: loc.issued_per_day, titlefont: { color: "#2a7ae2" }, tickfont: { color: "#2a7ae2" }, overlaying: "y", side: "left", showgrid: false }, legend: { xanchor: "left", yanchor: "top", x: 0, y: 1 } }; Plotly.plot(dom, traces, layout); } function httpsCsvListener(responseText) { let countries = {}; let operatingSystems = {}; let dateToCountryOSPageloadData = {}; parse_delim(responseText, ",", function(row){ let datestamp = row[0].substring(0, 10); // Strip off the timestamp let os = row[1]; let country = row[2]; let reporting_ratio = row[3]; let normalized_pageloads = row[4]; let ratio = row[5]; if (!dateFormat.test(datestamp)) { return; } if (!(datestamp in dateToCountryOSPageloadData)) { dateToCountryOSPageloadData[datestamp] = {}; } let countryOSPageloadData = dateToCountryOSPageloadData[datestamp]; if (!(country in countryOSPageloadData)) { countryOSPageloadData[country] = {}; } let osPageloadData = countryOSPageloadData[country]; osPageloadData[os] = { normalized_pageloads, reporting_ratio, ratio }; // derive a master list for the UI countries[country] = 1; operatingSystems[os] = 1; }); gHttpsData.countryList = Object.keys(countries); gHttpsData.osList = Object.keys(operatingSystems); gHttpsData.dateToCountryOSPageloadData = dateToCountryOSPageloadData; } function httpsDerivePageloadsFromNormalizedData(traceObj, includeInHttpsAnalysis, stackMovingAverage = []) { let dateToCountryOSPageloadData = gHttpsData.dateToCountryOSPageloadData; // Input data is not sorted, and so neither is the dictionary, so we need // to sort it before we continue. let sortedDates = Object.keys(dateToCountryOSPageloadData); sortedDates.sort(); for (let datestamp of sortedDates) { // See https://bugzilla.mozilla.org/show_bug.cgi?id=1414839#c19 for // parameter details let totalNormalizedPageloads = 0.0; // Preprocess to sum all normalized_pageloads for included rows for (let country in dateToCountryOSPageloadData[datestamp]) { for (let os in dateToCountryOSPageloadData[datestamp][country]) { if (includeInHttpsAnalysis(os, country)) { let row = dateToCountryOSPageloadData[datestamp][country][os]; totalNormalizedPageloads += parseFloat(row.normalized_pageloads); } } } // Now derive the reporting ratio and secure pageload ratio let totalSecurePageloadRatio = 0.0; for (let country in dateToCountryOSPageloadData[datestamp]) { for (let os in dateToCountryOSPageloadData[datestamp][country]) { if (includeInHttpsAnalysis(os, country)) { let row = dateToCountryOSPageloadData[datestamp][country][os]; let dimensionNormalizedPageloads = parseFloat(row.normalized_pageloads) / totalNormalizedPageloads; totalSecurePageloadRatio += parseFloat(row.ratio) * dimensionNormalizedPageloads; } } } let securePageloadPercentage = movingAvg(stackMovingAverage, totalSecurePageloadRatio * 100); insertPoint(traceObj, datestamp, securePageloadPercentage); } } function historicalHttpsCsvListener(responseText) { parse_delim(responseText, ",", function(row){ let datestamp = row[0]; let pageloads = row[1]; if (!dateFormat.test(datestamp)) { return; } gHttpsData.dateToHistoricalPageloadData[datestamp] = pageloads; }); } function importHistoricalGlobalData(traceObj, stackMovingAvg) { let sortedDates = Object.keys(gHttpsData.dateToHistoricalPageloadData); sortedDates.sort(); for (let datestamp of sortedDates) { let pageloadPercent = gHttpsData.dateToHistoricalPageloadData[datestamp]; let securePageloadPercentage = movingAvg(stackMovingAvg, pageloadPercent); insertPoint(traceObj, datestamp, securePageloadPercentage); } } let leBlue = "#103a71"; let leOrange = "#ffa409"; let someGreen = "#2ca02c"; // Firefox telemetry (HTTP_PAGELOAD_IS_SSL) over time function httpsPlot(dom) { let traces = []; { let traceObj = { type: "scatter", x:[], y:[], name: loc.all_users, line: { color: leBlue } }; let stackMovingAvg = []; importHistoricalGlobalData(traceObj, stackMovingAvg); httpsDerivePageloadsFromNormalizedData(traceObj, () => { return true; }, stackMovingAvg); traces.push(traceObj); } { let traceObj = { type: “scatter”, x:[], y:[], name: loc.usa_users, line: { color: leOrange, dash: “dot” } }; httpsDerivePageloadsFromNormalizedData(traceObj, (os, country) => { return (country == “US”); }); traces.push(traceObj); } { let traceObj = { type: “scatter”, x:[], y:[], name: loc.japan_users, line: { color: someGreen, dash: “dash” } }; httpsDerivePageloadsFromNormalizedData(traceObj, (os, country) => { return (country == “JP”); }); traces.push(traceObj); } let layout = { margin: { t: 20 }, yaxis: { title: loc.percent_https, rangemode: “tozero”, ticksuffix: “%” }, legend: { xanchor: “left”, yanchor: “top”, x: 0, y: 1 } }; Plotly.plot(dom, traces, layout); } var path; if ( location.hostname === “letsencrypt.org” ) { path = “https://d4twhgtvn0ff5.cloudfront.net/”; } else { path = “/js/”; // in dev, will use old data. } fetch(path+”cert-timeline.tsv”) .then(response => { return response.text(); }).then(tsvListener); var waiting = “
“; const activeUsage = document.getElementById(“activeUsage”); if (activeUsage) { activeUsage.innerHTML = waiting; } const issuancePerDay = document.getElementById(“issuancePerDay”); if (issuancePerDay) { issuancePerDay.innerHTML = waiting; } const pageloadPercent = document.getElementById(“pageloadPercent”); if ( pageloadPercent ) { pageloadPercent.innerHTML = waiting; const currentHttpsReqPromise = fetch(path+”current-https-adoption.csv”) .then(response => { return response.text(); }).then(httpsCsvListener); const historicalHttpsReqPromise = fetch(path+”historical-https-adoption.csv”) .then(response => { return response.text(); }).then(historicalHttpsCsvListener); // We shouldn’t try to plot HTTPS until both the current and historical fetches // are completed Promise.all([currentHttpsReqPromise, historicalHttpsReqPromise]) .then(function(){ pageloadPercent.innerHTML = “”; // remove waiting httpsPlot(pageloadPercent); }); } } if (document.readyState === “complete”) { doPlot(); } else { window.addEventListener(“load”, doPlot); }