254 if (!('visibilityState' in document) || document.visibilityState === 'visible')
255 goatcounter.count()
256 else {
257 var f = function(e) {258 if (document.visibilityState !== 'visible')259 return260 document.removeEventListener('visibilitychange', f)261 goatcounter.count()262 }263 document.addEventListener('visibilitychange', f)
264 }
265
227 for (var k in opt.attr)
228 d.setAttribute(k, opt.attr[k])
229
230 var p = document.querySelector(opt.append)231 if (!p)
232 return warn('visit_count: append not found: ' + opt.append)
233 p.appendChild(d)
224 }
225
226 var d = document.createElement(tag)
227 for (var k in opt.attr)228 d.setAttribute(k, opt.attr[k])
229
230 var p = document.querySelector(opt.append)
223 opt.attr['scrolling'] = 'no'
224 }
225
226 var d = document.createElement(tag)227 for (var k in opt.attr)
228 d.setAttribute(k, opt.attr[k])
229
214 if (opt.start) opt.attr['src'] += '&start=' + enc(opt.start)
215 if (opt.end) opt.attr['src'] += '&end=' + enc(opt.end)
216
217 var tag = {png: 'img', svg: 'img', html: 'iframe'}[opt.type]218 if (!tag)
219 return warn('visit_count: unknown type: ' + opt.type)
220
192 Array.prototype.slice.call(document.querySelectorAll("*[data-goatcounter-click]")).forEach(function(elem) {
193 if (elem.dataset.goatcounterBound)
194 return
195 var f = send(elem)196 elem.addEventListener('click', f, false)
197 elem.addEventListener('auxclick', f, false) // Middle click.
198 elem.dataset.goatcounterBound = 'true'
178 if (!document.querySelectorAll) // Just in case someone uses an ancient browser.
179 return
180
181 var send = function(elem) {182 return function() {183 goatcounter.count({184 event: true,185 path: (elem.dataset.goatcounterClick || elem.name || elem.id || ''),186 title: (elem.dataset.goatcounterTitle || elem.title || (elem.innerHTML || '').substr(0, 200) || ''),187 referrer: (elem.dataset.goatcounterReferrer || elem.dataset.goatcounterReferral || ''),188 })189 }190 }191
192 Array.prototype.slice.call(document.querySelectorAll("*[data-goatcounter-click]")).forEach(function(elem) {
193 if (elem.dataset.goatcounterBound)
168 // Get a query parameter.
169 window.goatcounter.get_query = function(name) {
170 var s = location.search.substr(1).split('&')
171 for (var i = 0; i < s.length; i++)172 if (s[i].toLowerCase().indexOf(name.toLowerCase() + '=') === 0)
173 return s[i].substr(name.length + 1)
174 }
167
168 // Get a query parameter.
169 window.goatcounter.get_query = function(name) {
170 var s = location.search.substr(1).split('&')171 for (var i = 0; i < s.length; i++)
172 if (s[i].toLowerCase().indexOf(name.toLowerCase() + '=') === 0)
173 return s[i].substr(name.length + 1)
160 img.setAttribute('alt', '')
161 img.setAttribute('aria-hidden', 'true')
162
163 var rm = function() { if (img && img.parentNode) img.parentNode.removeChild(img) }164 img.addEventListener('load', rm, false)
165 document.body.appendChild(img)
166 }
150 if (!url)
151 return warn('not counting because path callback returned null')
152
153 var img = document.createElement('img')154 img.src = url
155 img.style.position = 'absolute' // Affect layout less.
156 img.style.bottom = '0px'
146 if (f)
147 return warn('not counting because of: ' + f)
148
149 var url = goatcounter.url(vars)150 if (!url)
151 return warn('not counting because path callback returned null')
152
142
143 // Count a hit.
144 window.goatcounter.count = function(vars) {
145 var f = goatcounter.filter()146 if (f)
147 return warn('not counting because of: ' + f)
148
133 return
134 data.rnd = Math.random().toString(36).substr(2, 5) // Browsers don't always listen to Cache-Control.
135
136 var endpoint = get_endpoint()137 if (!endpoint)
138 return warn('no endpoint found')
139
128
129 // Get URL to send to GoatCounter.
130 window.goatcounter.url = function(vars) {
131 var data = get_data(vars || {})132 if (data.p === null) // null from user callback.
133 return
134 data.rnd = Math.random().toString(36).substr(2, 5) // Browsers don't always listen to Cache-Control.
104 }
105
106 // Run function after DOM is loaded.
107 var on_load = function(f) {108 if (document.body === null)109 document.addEventListener('DOMContentLoaded', function() { f() }, false)110 else111 f()112 }113
114 // Filter some requests that we (probably) don't want to count.
115 goatcounter.filter = function() {
91 }
92
93 // Get current path.
94 var get_path = function() { 95 var loc = location, 96 c = document.querySelector('link[rel="canonical"][href]') 97 if (c) { // May be relative or point to different domain. 98 var a = document.createElement('a') 99 a.href = c.href100 if (a.hostname.replace(/^www\./, '') === location.hostname.replace(/^www\./, ''))101 loc = a102 }103 return (loc.pathname + loc.search) || '/'104 }105
106 // Run function after DOM is loaded.
107 var on_load = function(f) {
95 var loc = location,
96 c = document.querySelector('link[rel="canonical"][href]')
97 if (c) { // May be relative or point to different domain.
98 var a = document.createElement('a') 99 a.href = c.href
100 if (a.hostname.replace(/^www\./, '') === location.hostname.replace(/^www\./, ''))
101 loc = a
92
93 // Get current path.
94 var get_path = function() {
95 var loc = location, 96 c = document.querySelector('link[rel="canonical"][href]') 97 if (c) { // May be relative or point to different domain.
98 var a = document.createElement('a')
99 a.href = c.href
83 }
84
85 // Get the endpoint to send requests to.
86 var get_endpoint = function() { 87 var s = document.querySelector('script[data-goatcounter]') 88 if (s && s.dataset.goatcounter) 89 return s.dataset.goatcounter 90 return (goatcounter.endpoint || window.counter) // counter is for compat; don't use. 91 } 92
93 // Get current path.
94 var get_path = function() {
84
85 // Get the endpoint to send requests to.
86 var get_endpoint = function() {
87 var s = document.querySelector('script[data-goatcounter]') 88 if (s && s.dataset.goatcounter)
89 return s.dataset.goatcounter
90 return (goatcounter.endpoint || window.counter) // counter is for compat; don't use.
77 }
78
79 // Show a warning in the console.
80 var warn = function(msg) { 81 if (console && 'warn' in console) 82 console.warn('goatcounter: ' + msg) 83 } 84
85 // Get the endpoint to send requests to.
86 var get_endpoint = function() {
68 }
69
70 // Object to urlencoded string, starting with a ?.
71 var urlencode = function(obj) { 72 var p = [] 73 for (var k in obj) 74 if (obj[k] !== '' && obj[k] !== null && obj[k] !== undefined && obj[k] !== false) 75 p.push(enc(k) + '=' + enc(obj[k])) 76 return '?' + p.join('&') 77 } 78
79 // Show a warning in the console.
80 var warn = function(msg) {
70 // Object to urlencoded string, starting with a ?.
71 var urlencode = function(obj) {
72 var p = []
73 for (var k in obj) 74 if (obj[k] !== '' && obj[k] !== null && obj[k] !== undefined && obj[k] !== false)
75 p.push(enc(k) + '=' + enc(obj[k]))
76 return '?' + p.join('&')
69
70 // Object to urlencoded string, starting with a ?.
71 var urlencode = function(obj) {
72 var p = [] 73 for (var k in obj)
74 if (obj[k] !== '' && obj[k] !== null && obj[k] !== undefined && obj[k] !== false)
75 p.push(enc(k) + '=' + enc(obj[k]))
It is recommended to use let
or const
over var
.
This will help prevent re-declaration of variables that are in the global scope when using var
.
ES6 allows programmers to create variables with block scope instead of function scope using the let
and const
keywords.
Block scope is common in many other programming languages and helps programmers avoid mistakes such as this one:
var count = people.length;
var enoughFood = count > sandwiches.length;
if (enoughFood) {
var count = sandwiches.length; // accidentally overriding the count variable
console.log("We have " + count + " sandwiches for everyone. Plenty for all!");
}
// our count variable is no longer accurate
console.log("We have " + count + " people and " + sandwiches.length + " sandwiches!");
Block scoped variables shadow outer declarations instead of writing to them.
NOTE: There are certain edge cases where users might want to consider var. Consider this example:
var lib = lib || { run: () => {} }
Here, lib
might be a library that is exposed to an HTML file using a <script>
tag.
The var
keyword helps avoid re-writing lib
if it has already been declared via an injected script that was executed before this one.
Ideally, you should let bundlers worry about cases like this.
But if you want to use var
anyway, consider using a skipcq comment, or disabling the issue altogether.
Bad Practice
var x = "y";
var CONFIG = {};
Recommended
let x = "y";
const CONFIG = {};