/
external.js
171 lines (143 loc) · 6.55 KB
/
external.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
/* eslint-env browser */
(function(window, hostname) {
if (!window) return
var nav = window.navigator
var loc = window.location
var host = loc.hostname
var doc = window.document
var con = window.console
var perf = window.performance
var uri = '//' + hostname
var targetOrigin = 'https://' + hostname
// A simple log function so the user knows why a request is not being send
var warn = function(message) {
if (con && con.warn) con.warn('Simple Analytics: ' + message)
}
try {
var userAgent = nav.userAgent
var dis = window.dispatchEvent
var lastSendUrl
var notSending = 'Not sending requests '
var attr = function(script, attribute) { return script && script.getAttribute('data-' + attribute) }
var script = doc.querySelector('script[src$="' + uri + '/app.js"]')
var mode = attr(script, 'mode')
var skipDNT = attr(script, 'skip-dnt') === 'true'
var functionName = attr(script, 'sa-global') || 'sa'
// Don't track when host is localhost
if (host === 'localhost') return warn(notSending + 'from localhost')
// We do advanced bot detection in our API, but this line filters already most bots
if (/(bot|spider|crawl)/i.test(userAgent)) return warn(notSending + 'because user agent is a robot')
var getParams = function(regex) {
// From the search we grab the utm_source and ref and save only that
var matches = loc.search.match(new RegExp('[?&]('+regex+')=([^?&]+)', 'gi'))
var match = matches ? matches.map(function(m) { return m.split('=')[1] }) : []
if (match && match[0]) return match[0]
}
var ref = getParams('utm_source|source|ref')
var campaign = getParams('utm_campaign|campaign')
var cleanRef = doc.referrer.replace(/^https?:\/\/((m|l|w{2,3}([0-9]+)?)\.)?([^?#]+)(.*)$/, '$4').replace(/^([^/]+)\/$/, '$1') || null
var post = function(isPushState) {
// Obfuscate personal data in URL by dropping the search and hash
var url = loc.protocol + '//' + host + loc.pathname
// Add hash to url when script is put in to hash mode
if (mode === 'hash' && loc.hash) url += loc.hash.split('?')[0]
// Don't send the last URL again (this could happen when pushState is used to change the URL hash or search)
if (lastSendUrl === url) return
lastSendUrl = url
// Don't track when Do Not Track is set to true
if (!skipDNT && 'doNotTrack' in nav && nav.doNotTrack === '1') return warn(notSending + 'when doNotTrack is enabled')
var data = { url: url }
if (userAgent) data.ua = userAgent
if (ref) data.urlReferrer = ref
if (cleanRef && !isPushState) data.referrer = cleanRef
if (window.innerWidth) data.width = window.innerWidth
// We put new code always in a try block to prevent huge issues
try {
// Check if back, forward or reload buttons are being use in modern browsers
var backModern = (perf && perf.getEntriesByType && perf.getEntriesByType('navigation')[0] && perf.getEntriesByType('navigation')[0].type)
? ['reload', 'back_forward'].indexOf(perf.getEntriesByType('navigation')[0].type) > -1
: null
// Check if back, forward or reload buttons are being use in older browsers
var back = typeof backModern === 'boolean'
? backModern
: perf && perf.navigation && perf.navigation.type && [perf.navigation.TYPE_RELOAD, perf.navigation.TYPE_BACK_FORWARD].indexOf(perf.navigation.type) > -1
// We set unique variable based on pushstate or back navigation, if no match we check the referrer
data.unique = isPushState || back ? false : doc.referrer && doc.referrer.split('/')[2] !== loc.hostname;
} catch (error) {
// nothing
}
try {
data.timezone = Intl.DateTimeFormat().resolvedOptions().timeZone
} catch (error) {
// nothing
}
var request = new XMLHttpRequest()
request.open('POST', uri + '/api', true)
// We use content type text/plain here because we don't want to send an
// pre-flight OPTIONS request
request.setRequestHeader('Content-Type', 'text/plain; charset=UTF-8')
request.send(JSON.stringify(data))
}
// Thanks to https://gist.github.com/rudiedirkx/fd568b08d7bffd6bd372
var his = window.history
var hisPushState = his ? his.pushState : null
if (hisPushState && Event && dis) {
var stateListener = function(type) {
var orig = his[type]
return function() {
var rv = orig.apply(this, arguments)
var event = new Event(type)
event.arguments = arguments
dis(event)
return rv
}
}
his.pushState = stateListener('pushState')
window.addEventListener('pushState', function() {
post(true)
})
}
// When in hash mode, we record a pageview based on the onhashchange function
if (mode === 'hash' && 'onhashchange' in window) {
window.onhashchange = post
}
// Post the page view
post()
// Stop when not running on subdomain
var hostWithoutSubdomain = /\.(.+)/.exec(hostname)[1]
if (!(host === hostWithoutSubdomain || new RegExp('.' + hostWithoutSubdomain + '$', 'i').test(host))) {
return warn('Events via this script only work on ' + hostWithoutSubdomain + ' domains')
}
// Build a simple queue
var queue = window[functionName] && window[functionName].q ? window[functionName].q : []
var loading = false
var loadIframe = function() {
loading = true
var iframe = doc.createElement('iframe')
iframe.setAttribute('src', uri + '/iframe.html')
iframe.style.display = 'none'
iframe.onload = function() {
var contentWindow = iframe.contentWindow
var refOrDocRef = ref || cleanRef
try {
if (queue) for (var index = 0; index < queue.length; index++) contentWindow.postMessage({ event: queue[index][0], ref: refOrDocRef, campaign: campaign }, targetOrigin)
} catch(e) { /* Nothing */ }
window[functionName] = function(event) {
contentWindow.postMessage({ event: event, ref: refOrDocRef, campaign: campaign }, targetOrigin)
}
}
doc.body.appendChild(iframe)
}
// Only load the iframe when events are pushed
window[functionName] = function() {
if (!loading) loadIframe()
queue.push([].slice.call(arguments))
}
if (!loading && queue.length) loadIframe()
} catch (e) {
warn(e.message)
var url = uri + '/image.gif'
if (e.message) url = url + '?error=' + encodeURIComponent(e.message)
new Image().src = url
}
})(window, 'simpleanalytics.example.com')