diff --git a/articles.html b/articles.html index d5e0680..87f798f 100644 --- a/articles.html +++ b/articles.html @@ -11,6 +11,7 @@ + diff --git a/articles/api-with-handy-httpd.html b/articles/api-with-handy-httpd.html index 5355797..f34d10b 100644 --- a/articles/api-with-handy-httpd.html +++ b/articles/api-with-handy-httpd.html @@ -11,6 +11,7 @@ + diff --git a/articles/beginner-guide-to-apache-lucene.html b/articles/beginner-guide-to-apache-lucene.html index 506b364..30892d1 100644 --- a/articles/beginner-guide-to-apache-lucene.html +++ b/articles/beginner-guide-to-apache-lucene.html @@ -11,6 +11,7 @@ + diff --git a/articles/d-prospects-for-future.html b/articles/d-prospects-for-future.html index c876dac..1bea2d4 100644 --- a/articles/d-prospects-for-future.html +++ b/articles/d-prospects-for-future.html @@ -11,6 +11,7 @@ + diff --git a/articles/dsh-easier-scripting-in-d.html b/articles/dsh-easier-scripting-in-d.html index 25e4d98..bc40f1b 100644 --- a/articles/dsh-easier-scripting-in-d.html +++ b/articles/dsh-easier-scripting-in-d.html @@ -11,6 +11,7 @@ + diff --git a/articles/java-swing-2d-game.html b/articles/java-swing-2d-game.html index 625a163..1c3cd32 100644 --- a/articles/java-swing-2d-game.html +++ b/articles/java-swing-2d-game.html @@ -11,6 +11,7 @@ + diff --git a/articles/spring-osiv-transactions.html b/articles/spring-osiv-transactions.html index 213c054..f331624 100644 --- a/articles/spring-osiv-transactions.html +++ b/articles/spring-osiv-transactions.html @@ -11,6 +11,7 @@ + diff --git a/articles/template.html b/articles/template.html index ee3df8d..3474f8b 100644 --- a/articles/template.html +++ b/articles/template.html @@ -11,6 +11,7 @@ + diff --git a/contact.html b/contact.html index eb1b94e..049ace5 100644 --- a/contact.html +++ b/contact.html @@ -11,6 +11,7 @@ + diff --git a/index.html b/index.html index ba62ebf..3e3f75b 100644 --- a/index.html +++ b/index.html @@ -11,6 +11,7 @@ + diff --git a/logbook.html b/logbook.html index ea909b6..a7ca734 100644 --- a/logbook.html +++ b/logbook.html @@ -11,6 +11,7 @@ + diff --git a/projects.html b/projects.html index 8b82914..c8ceee4 100644 --- a/projects.html +++ b/projects.html @@ -11,6 +11,7 @@ + diff --git a/scripts/sitestat.js b/scripts/sitestat.js new file mode 100644 index 0000000..3d36f61 --- /dev/null +++ b/scripts/sitestat.js @@ -0,0 +1,104 @@ +/* +sitestat.js is meant to be included in the like so: + + + +See test-site/index.html for an example. +*/ + +/** + * The global websocket singleton. It's initialized at the bottom of this script. + * @type {WebSocket | null} + */ +let WS = null; +let REMOTE_URL = null; +const RETRY_TIMEOUT = 1000; +let RETRY_COUNT = 0; + +/** + * Sends some JSON object to sitestat. + * @param {object} data The data to send. + */ +function sendStat(data) { + if (WS !== null && WS.readyState === WebSocket.OPEN) { + WS.send(JSON.stringify(data)); + } else { + console.warn("Couldn't send sitestat data because websocket is not open: ", data); + } +} + +/** + * Handles any event encountered, and sends a small message to sitestat about it. + * @param {Event} event The event that occurred. + */ +function handleEvent(event) { + sendStat({ + type: "event", + event: event.type + }); +} + +/** + * Gets the remote URL that sitestat is running at, from the query params of + * the script's `src` attribute. Throws an error if such a URL could not be + * found. + * @returns {string} The remote URL to connect to. + */ +function getRemoteUrl() { + const scriptUrl = document.currentScript.src; + const paramsIdx = scriptUrl.indexOf("?"); + if (paramsIdx !== -1) { + const paramsStr = scriptUrl.substring(paramsIdx); + const params = new URLSearchParams(paramsStr); + const remoteUrl = params.get("remote-url"); + if (remoteUrl !== null) { + return remoteUrl; + } + } + throw new Error("Missing `remote-url=...` query parameter on script src attribute.") +} + +function initWS() { + console.info("Trying to open a connection to sitestat..."); + WS = new WebSocket(`wss://${REMOTE_URL}/ws`); + WS.onopen = () => { + console.info( + "📈 Established a connection to %csitestat%c for %cnon-intrusive, non-identifiable%csite analytics.\nLearn more here: https://github.com/andrewlalis/sitestat or check your browser's network tab to see what's being sent.", + "font-weight: bold; font-style: italic; font-size: large; color: #32a852; background-color: #2e2e2e; padding: 5px;", + "", + "font-style: italic;" + ); + sendStat({ + type: "ident", + href: window.location.href, + userAgent: window.navigator.userAgent, + viewport: { + width: window.innerWidth, + height: window.innerHeight + } + }); + } + // WS.onerror = console.error; + WS.onclose = (closeEvent) => { + if (closeEvent.code > 1001 && RETRY_COUNT < 10) { + RETRY_COUNT++; + console.warn("sitestat connection closed unexpectedly with code "+closeEvent.code+". Trying to reestablish the connection in " + RETRY_TIMEOUT + "ms."); + window.setTimeout(initWS, RETRY_TIMEOUT); + } + } +} + + + +// The main script starts below: +if (window.navigator.webdriver) { + throw new Error("sitestat disabled for automated user agents."); +} +REMOTE_URL = getRemoteUrl(); +initWS(); + +// Register various event listeners. +const events = ["click", "keyup", "keydown", "copy"]; +for (let i = 0; i < events.length; i++) { + document.addEventListener(events[i], handleEvent); +} diff --git a/training.html b/training.html index a5fc445..d326997 100644 --- a/training.html +++ b/training.html @@ -11,6 +11,7 @@ +