-
-
Notifications
You must be signed in to change notification settings - Fork 2
📖✨:add goatcounter analytics script #1511
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
OpenINFbot
merged 11 commits into
OpenINF:live
from
DerekNonGeneric:feat/add-goatcounter
Nov 27, 2024
Merged
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
f44e96a
📖✨:add goatcounter analytics script
DerekNonGeneric dcbbc38
add remaining bits to build js locally
DerekNonGeneric 5589bef
rm unused import
DerekNonGeneric 6a1f156
update biome to ignore vendor js
DerekNonGeneric d57b12f
add exclude pattern to deepsource config
DerekNonGeneric 4da32a2
Update .deepsource.toml
DerekNonGeneric a22909e
Update .deepsource.toml
DerekNonGeneric 4fa0701
Update .deepsource.toml
DerekNonGeneric 8b4821d
Update biome.jsonc
OpenINFbot eae7175
Update .deepsource.toml
OpenINFbot 8c1caf4
fix ci failures
DerekNonGeneric File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,6 @@ | ||
| version = 1 | ||
| exclude_patterns = [ | ||
| "_assets/js/vendor/**", | ||
| "pnpm-lock.yaml", | ||
| ] | ||
|
|
||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,271 @@ | ||
| // GoatCounter: https://www.goatcounter.com | ||
| // This file is released under the ISC license: https://opensource.org/licenses/ISC | ||
| ;(function() { | ||
| 'use strict'; | ||
|
|
||
| if (window.goatcounter && window.goatcounter.vars) // Compatibility with very old version; do not use. | ||
| window.goatcounter = window.goatcounter.vars | ||
| else | ||
| window.goatcounter = window.goatcounter || {} | ||
|
|
||
| // Load settings from data-goatcounter-settings. | ||
| var s = document.querySelector('script[data-goatcounter]') | ||
| if (s && s.dataset.goatcounterSettings) { | ||
| try { var set = JSON.parse(s.dataset.goatcounterSettings) } | ||
| catch (err) { console.error('invalid JSON in data-goatcounter-settings: ' + err) } | ||
| for (var k in set) | ||
| if (['no_onload', 'no_events', 'allow_local', 'allow_frame', 'path', 'title', 'referrer', 'event'].indexOf(k) > -1) | ||
| window.goatcounter[k] = set[k] | ||
| } | ||
|
|
||
| var enc = encodeURIComponent | ||
|
|
||
| // Get all data we're going to send off to the counter endpoint. | ||
| var get_data = function(vars) { | ||
| var data = { | ||
| p: (vars.path === undefined ? goatcounter.path : vars.path), | ||
| r: (vars.referrer === undefined ? goatcounter.referrer : vars.referrer), | ||
| t: (vars.title === undefined ? goatcounter.title : vars.title), | ||
| e: !!(vars.event || goatcounter.event), | ||
| s: [window.screen.width, window.screen.height, (window.devicePixelRatio || 1)], | ||
| b: is_bot(), | ||
| q: location.search, | ||
| } | ||
|
|
||
| var rcb, pcb, tcb // Save callbacks to apply later. | ||
| if (typeof(data.r) === 'function') rcb = data.r | ||
| if (typeof(data.t) === 'function') tcb = data.t | ||
| if (typeof(data.p) === 'function') pcb = data.p | ||
|
|
||
| if (is_empty(data.r)) data.r = document.referrer | ||
| if (is_empty(data.t)) data.t = document.title | ||
| if (is_empty(data.p)) data.p = get_path() | ||
|
|
||
| if (rcb) data.r = rcb(data.r) | ||
| if (tcb) data.t = tcb(data.t) | ||
| if (pcb) data.p = pcb(data.p) | ||
| return data | ||
| } | ||
|
|
||
| // Check if a value is "empty" for the purpose of get_data(). | ||
| var is_empty = function(v) { return v === null || v === undefined || typeof(v) === 'function' } | ||
|
|
||
| // See if this looks like a bot; there is some additional filtering on the | ||
| // backend, but these properties can't be fetched from there. | ||
| var is_bot = function() { | ||
| // Headless browsers are probably a bot. | ||
| var w = window, d = document | ||
| if (w.callPhantom || w._phantom || w.phantom) | ||
| return 150 | ||
| if (w.__nightmare) | ||
| return 151 | ||
| if (d.__selenium_unwrapped || d.__webdriver_evaluate || d.__driver_evaluate) | ||
| return 152 | ||
| if (navigator.webdriver) | ||
| return 153 | ||
| return 0 | ||
| } | ||
|
|
||
| // Object to urlencoded string, starting with a ?. | ||
| var urlencode = function(obj) { | ||
| var p = [] | ||
| for (var k in obj) | ||
| if (obj[k] !== '' && obj[k] !== null && obj[k] !== undefined && obj[k] !== false) | ||
| p.push(enc(k) + '=' + enc(obj[k])) | ||
| return '?' + p.join('&') | ||
| } | ||
|
|
||
| // Show a warning in the console. | ||
| var warn = function(msg) { | ||
| if (console && 'warn' in console) | ||
| console.warn('goatcounter: ' + msg) | ||
| } | ||
|
|
||
| // Get the endpoint to send requests to. | ||
| var get_endpoint = function() { | ||
| var s = document.querySelector('script[data-goatcounter]') | ||
| if (s && s.dataset.goatcounter) | ||
| return s.dataset.goatcounter | ||
| return (goatcounter.endpoint || window.counter) // counter is for compat; don't use. | ||
| } | ||
|
|
||
| // Get current path. | ||
| var get_path = function() { | ||
| var loc = location, | ||
| c = document.querySelector('link[rel="canonical"][href]') | ||
| if (c) { // May be relative or point to different domain. | ||
| var a = document.createElement('a') | ||
| a.href = c.href | ||
| if (a.hostname.replace(/^www\./, '') === location.hostname.replace(/^www\./, '')) | ||
| loc = a | ||
| } | ||
| return (loc.pathname + loc.search) || '/' | ||
| } | ||
|
|
||
| // Run function after DOM is loaded. | ||
| var on_load = function(f) { | ||
| if (document.body === null) | ||
| document.addEventListener('DOMContentLoaded', function() { f() }, false) | ||
| else | ||
| f() | ||
| } | ||
|
|
||
| // Filter some requests that we (probably) don't want to count. | ||
| goatcounter.filter = function() { | ||
| if ('visibilityState' in document && document.visibilityState === 'prerender') | ||
| return 'visibilityState' | ||
| if (!goatcounter.allow_frame && location !== parent.location) | ||
| return 'frame' | ||
| if (!goatcounter.allow_local && location.hostname.match(/(localhost$|^127\.|^10\.|^172\.(1[6-9]|2[0-9]|3[0-1])\.|^192\.168\.|^0\.0\.0\.0$)/)) | ||
| return 'localhost' | ||
| if (!goatcounter.allow_local && location.protocol === 'file:') | ||
| return 'localfile' | ||
| if (localStorage && localStorage.getItem('skipgc') === 't') | ||
| return 'disabled with #toggle-goatcounter' | ||
| return false | ||
| } | ||
|
|
||
| // Get URL to send to GoatCounter. | ||
| window.goatcounter.url = function(vars) { | ||
| var data = get_data(vars || {}) | ||
| if (data.p === null) // null from user callback. | ||
| return | ||
| data.rnd = Math.random().toString(36).substr(2, 5) // Browsers don't always listen to Cache-Control. | ||
|
|
||
| var endpoint = get_endpoint() | ||
| if (!endpoint) | ||
| return warn('no endpoint found') | ||
|
|
||
| return endpoint + urlencode(data) | ||
| } | ||
|
|
||
| // Count a hit. | ||
| window.goatcounter.count = function(vars) { | ||
| var f = goatcounter.filter() | ||
| if (f) | ||
| return warn('not counting because of: ' + f) | ||
| var url = goatcounter.url(vars) | ||
| if (!url) | ||
| return warn('not counting because path callback returned null') | ||
|
|
||
| if (!navigator.sendBeacon(url)) { | ||
| // This mostly fails due to being blocked by CSP; try again with an | ||
| // image-based fallback. | ||
| var img = document.createElement('img') | ||
| img.src = url | ||
| img.style.position = 'absolute' // Affect layout less. | ||
| img.style.bottom = '0px' | ||
| img.style.width = '1px' | ||
| img.style.height = '1px' | ||
| img.loading = 'eager' | ||
| img.setAttribute('alt', '') | ||
| img.setAttribute('aria-hidden', 'true') | ||
|
|
||
| var rm = function() { if (img && img.parentNode) img.parentNode.removeChild(img) } | ||
| img.addEventListener('load', rm, false) | ||
| document.body.appendChild(img) | ||
| } | ||
| } | ||
|
|
||
| // Get a query parameter. | ||
| window.goatcounter.get_query = function(name) { | ||
| var s = location.search.substr(1).split('&') | ||
| for (var i = 0; i < s.length; i++) | ||
| if (s[i].toLowerCase().indexOf(name.toLowerCase() + '=') === 0) | ||
| return s[i].substr(name.length + 1) | ||
| } | ||
|
|
||
| // Track click events. | ||
| window.goatcounter.bind_events = function() { | ||
| if (!document.querySelectorAll) // Just in case someone uses an ancient browser. | ||
| return | ||
|
|
||
| var send = function(elem) { | ||
| return function() { | ||
| goatcounter.count({ | ||
| event: true, | ||
| path: (elem.dataset.goatcounterClick || elem.name || elem.id || ''), | ||
| title: (elem.dataset.goatcounterTitle || elem.title || (elem.innerHTML || '').substr(0, 200) || ''), | ||
| referrer: (elem.dataset.goatcounterReferrer || elem.dataset.goatcounterReferral || ''), | ||
| }) | ||
| } | ||
| } | ||
|
|
||
| Array.prototype.slice.call(document.querySelectorAll("*[data-goatcounter-click]")).forEach(function(elem) { | ||
| if (elem.dataset.goatcounterBound) | ||
| return | ||
| var f = send(elem) | ||
| elem.addEventListener('click', f, false) | ||
| elem.addEventListener('auxclick', f, false) // Middle click. | ||
| elem.dataset.goatcounterBound = 'true' | ||
| }) | ||
| } | ||
|
|
||
| // Add a "visitor counter" frame or image. | ||
| window.goatcounter.visit_count = function(opt) { | ||
| on_load(function() { | ||
| opt = opt || {} | ||
| opt.type = opt.type || 'html' | ||
| opt.append = opt.append || 'body' | ||
| opt.path = opt.path || get_path() | ||
| opt.attr = opt.attr || {width: '200', height: (opt.no_branding ? '60' : '80')} | ||
|
|
||
| opt.attr['src'] = get_endpoint() + 'er/' + enc(opt.path) + '.' + enc(opt.type) + '?' | ||
| if (opt.no_branding) opt.attr['src'] += '&no_branding=1' | ||
| if (opt.style) opt.attr['src'] += '&style=' + enc(opt.style) | ||
| if (opt.start) opt.attr['src'] += '&start=' + enc(opt.start) | ||
| if (opt.end) opt.attr['src'] += '&end=' + enc(opt.end) | ||
|
|
||
| var tag = {png: 'img', svg: 'img', html: 'iframe'}[opt.type] | ||
| if (!tag) | ||
| return warn('visit_count: unknown type: ' + opt.type) | ||
|
|
||
| if (opt.type === 'html') { | ||
| opt.attr['frameborder'] = '0' | ||
| opt.attr['scrolling'] = 'no' | ||
| } | ||
|
|
||
| var d = document.createElement(tag) | ||
| for (var k in opt.attr) | ||
| d.setAttribute(k, opt.attr[k]) | ||
|
|
||
| var p = document.querySelector(opt.append) | ||
| if (!p) | ||
| return warn('visit_count: append not found: ' + opt.append) | ||
| p.appendChild(d) | ||
| }) | ||
| } | ||
|
|
||
| // Make it easy to skip your own views. | ||
| if (location.hash === '#toggle-goatcounter') { | ||
| if (localStorage.getItem('skipgc') === 't') { | ||
| localStorage.removeItem('skipgc', 't') | ||
| alert('GoatCounter tracking is now ENABLED in this browser.') | ||
| } | ||
| else { | ||
| localStorage.setItem('skipgc', 't') | ||
| alert('GoatCounter tracking is now DISABLED in this browser until ' + location + ' is loaded again.') | ||
| } | ||
| } | ||
|
|
||
| if (!goatcounter.no_onload) | ||
| on_load(function() { | ||
| // 1. Page is visible, count request. | ||
| // 2. Page is not yet visible; wait until it switches to 'visible' and count. | ||
| // See #487 | ||
| if (!('visibilityState' in document) || document.visibilityState === 'visible') | ||
| goatcounter.count() | ||
| else { | ||
| var f = function(e) { | ||
| if (document.visibilityState !== 'visible') | ||
| return | ||
| document.removeEventListener('visibilitychange', f) | ||
| goatcounter.count() | ||
| } | ||
| document.addEventListener('visibilitychange', f) | ||
| } | ||
|
|
||
| if (!goatcounter.no_events) | ||
| goatcounter.bind_events() | ||
| }) | ||
| })(); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| /** | ||
| * @file Build portal JS. | ||
| * @author The OpenINF Authors & Friends | ||
| * @license MIT OR Apache-2.0 OR BlueOak-1.0.0 | ||
| * @module {type ES6Module} build/tasks/compile/build-js | ||
| */ | ||
|
|
||
| import { jsify } from '@openinf/portal/build/tasks/jsify'; | ||
|
|
||
| jsify(); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.