Add google analytics support

Add code to track performance metrics and events (password calculation).
This commit is contained in:
Slava Bacherikov committed 2018-02-08 02:42:06 +02:00
1 parent 6c6c02da4e
commit d9bf30765f
12 files changed
+139 -11

No files matched your search

+9
View File
@@ -1,6 +1,8 @@
#!/bin/sh
set -e
CLOSURE_COMPILER_URL="https://dl.google.com/closure-compiler/compiler-latest.zip"
PROD_GOOGLE_ANALYTICS="UA-112154345-1"
STAGE_GOOGLE_ANALYTCIS="UA-112154345-2"
install_closure() {
wget $CLOSURE_COMPILER_URL -O /tmp/closure.zip
@@ -11,6 +13,13 @@ install_closure() {
}
build_package() {
if [ -n "$TRAVIS_TAG" ]; then
export GOOGLE_ANALYTICS_TAG="$PROD_GOOGLE_ANALYTICS"
echo "Using prod google analytics tag"
else
export GOOGLE_ANALYTICS_TAG="$STAGE_GOOGLE_ANALYTCIS"
echo "Using stage google analytics tag"
fi
PRODUCTION=1 npm run webpack
}
+2
View File
@@ -0,0 +1,2 @@
var dataLayer;
+17
View File
@@ -0,0 +1,17 @@
/**
* @return {number}
* @nosideeffects
*/
Performance.prototype.mozNow = function() {};
/**
* @return {number}
* @nosideeffects
*/
Performance.prototype.oNow = function() {};
/**
* @return {number}
* @nosideeffects
*/
Performance.prototype.msNow = function() {};
+12
View File
@@ -0,0 +1,12 @@
declare const GOOGLE_ANALYTICS_TAG: string | undefined;
interface Window {
dataLayer: any[];
}
interface Performance {
webkitNow(): number;
mozNow(): number;
oNow(): number;
msNow(): number;
}
+20
View File
@@ -0,0 +1,20 @@
/* tslint:disable:no-empty */
/* tslint:disable:only-arrow-functions */
export let gtag: (...args: any[]) => void = () => {};
if (GOOGLE_ANALYTICS_TAG) {
let gaElem = document.createElement("script");
gaElem.async = true;
let tag = GOOGLE_ANALYTICS_TAG;
gaElem.src = `https://www.googletagmanager.com/gtag/js?id=${tag}`;
let firstScript = document.getElementsByTagName("script")[0] as HTMLScriptElement;
(firstScript.parentNode as HTMLElement).insertBefore(gaElem, firstScript);
window.dataLayer = window.dataLayer || [];
gtag = function() { window.dataLayer.push(arguments); };
gtag("js", new Date());
gtag("config", GOOGLE_ANALYTICS_TAG, {
"custom_map": {"dimension1": "siteVersion"},
"transport_type": "beacon"
});
}
+8 -3
View File
@@ -2,6 +2,7 @@ import { dellHddSolver, dellSolver, hddOldSolver } from "./dell";
import { fsi20DecNewSolver, fsi20DecOldSolver, fsiHexSolver } from "./fsi";
import { hpMiniSolver } from "./hpmini";
import { insydeSolver } from "./insyde";
import { monotonicTime } from "./performancePolyfill";
import {
phoenixFsiLSolver, phoenixFsiPSolver, phoenixFsiSolver,
phoenixFsiSSolver, phoenixFsiXSolver, phoenixHPCompaqSolver, phoenixSolver
@@ -10,7 +11,7 @@ import { samsungSolver } from "./samsung";
import { sonySolver } from "./sony";
import { Solver } from "./utils";
export type KeygenResult = [Solver, string[]];
export type KeygenResult = [Solver, string[], number];
export const solvers: Solver[] = [
sonySolver,
@@ -35,6 +36,10 @@ export const solvers: Solver[] = [
// NOTE: In future this function can change or even be removed
export function keygen(serial: string): KeygenResult[] {
return solvers
.map((solver): KeygenResult => [solver, solver(serial)])
.filter(([_, passwords]) => passwords !== undefined && passwords.length >= 1);
.map((solver): KeygenResult => {
let startTime = monotonicTime();
let passwords = solver(serial);
let calcTime = monotonicTime() - startTime;
return [solver, passwords, calcTime];
}).filter(([_, passwords]) => passwords !== undefined && passwords.length >= 1);
}
+7
View File
@@ -0,0 +1,7 @@
import { monotonicTime } from "./performancePolyfill";
describe("Check performance.now polyfill", () => {
it("monotonicTime should return number", () => {
expect(typeof monotonicTime()).toEqual("number");
});
});
+20
View File
@@ -0,0 +1,20 @@
function makeMonotonicTime(): () => number {
if (typeof performance !== "undefined" && performance) {
let nowFun = performance.now ||
performance.webkitNow ||
performance.mozNow ||
performance.oNow ||
performance.msNow;
if (nowFun) {
return nowFun.bind(performance);
} else {
return Date.now;
}
} else {
return Date.now;
}
}
export const monotonicTime = makeMonotonicTime();
+30 -6
View File
@@ -1,6 +1,5 @@
import { keygen, KeygenResult } from "./keygen";
/// <reference types="dom" />
import { gtag } from "./googleAnalytics";
import { keygen, KeygenResult, solvers } from "./keygen";
let form = document.getElementById("form_id") as HTMLFormElement;
let serialInput = document.getElementById("serial_id") as HTMLInputElement;
@@ -11,10 +10,10 @@ let answerElement = document.getElementById("answer") as HTMLDivElement;
function renderResult(results: KeygenResult[]): void {
let table = document.createElement("table");
table.classList.add("answer_table");
results.forEach(([solver, passwords]) => {
results.forEach(([solver, passwords, _]) => {
let trElem = document.createElement("tr");
let tdNameElem = document.createElement("td");
tdNameElem.appendChild(document.createTextNode(solver.biosName));
tdNameElem.appendChild(document.createTextNode(solver.description));
trElem.appendChild(tdNameElem);
let tdElem = document.createElement("td");
passwords.forEach((pwd, index) => {
@@ -29,6 +28,31 @@ function renderResult(results: KeygenResult[]): void {
answerElement.appendChild(table);
}
function trackResult(serial: string): KeygenResult[] {
let result = keygen(serial);
if (GOOGLE_ANALYTICS_TAG) {
let eventParams: {[key: string]: boolean} = {};
solvers.forEach((solver) => {
eventParams[solver.biosName] = false;
});
result.forEach(([solver, _, time]) => {
eventParams[solver.biosName] = true;
// send calculation timing report
gtag("event", "timing_complete", {
"name": "bios_keygen",
"value": Math.round(time),
"event_category": solver.biosName
});
});
// send event about calculation
gtag("event", "bios_keygen", eventParams);
}
return result;
}
form.addEventListener("submit", (event) => {
if (event.preventDefault) {
event.preventDefault();
@@ -36,7 +60,7 @@ form.addEventListener("submit", (event) => {
event.returnValue = false;
}
answerElement.innerHTML = ""; // remove old table
let results = keygen(serialInput.value);
let results = trackResult(serialInput.value);
if (results.length > 0) {
tryThis.style.display = "";
dellNote.style.display = "";
+1 -1
View File
@@ -13,7 +13,7 @@ export interface Solver {
(code: string): string[];
readonly biosName: string;
readonly examples?: string[];
readonly description?: string;
readonly description: string;
validator(code: string): boolean;
cleaner(code: string): string;
keygen(code: string): string[];
+1
View File
@@ -1,6 +1,7 @@
{
"compilerOptions": {
"module": "commonjs",
"lib": ["dom", "es5"],
"target": "es3",
"noImplicitAny": true,
"noImplicitReturns": true,
+12 -1
View File
@@ -4,12 +4,20 @@ const ClosureCompilerPlugin = require('webpack-closure-compiler');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const webpack = require('webpack');
function makeClosureCompiler() {
const externDir = path.join(__dirname, "externs");
const externs = [
path.join(externDir, "googleAnalytics.js"),
path.join(externDir, "performance.js")
];
var pluginOptions = {
compiler: {
compilation_level: "ADVANCED",
create_source_map: true
create_source_map: true,
externs: externs
},
concurrency: os.cpus().length
};
@@ -29,6 +37,9 @@ var plugins = [
to: 'assets/'
}
]),
new webpack.DefinePlugin({
GOOGLE_ANALYTICS_TAG: JSON.stringify(process.env.GOOGLE_ANALYTICS_TAG)
}),
new HtmlWebpackPlugin({
minify: {
collapseWhitespace: true,