We do not collect personal information through the app.
-
We use Google Analytics, Gauges and Sentry to collect anonymous traffic information and improve the app.
+
We use Google Analytics and Gauges to collect anonymous traffic information if you have given consent to this. You can change your decision in the settings.
+
We use Sentry to collect crash data and improve the app.
The app uses cookies to store user preferences.
By using the app, you signify your acceptance of this policy. If you do not agree to this policy, please do not use the app.
If you have any questions regarding privacy, please email privacy@freecodecamp.org.
diff --git a/assets/javascripts/templates/pages/settings_tmpl.coffee b/assets/javascripts/templates/pages/settings_tmpl.coffee
index 1d439edb..e03a7744 100644
--- a/assets/javascripts/templates/pages/settings_tmpl.coffee
+++ b/assets/javascripts/templates/pages/settings_tmpl.coffee
@@ -15,6 +15,10 @@ app.templates.settingsPage = (settings) -> """
Automatically hide and show the sidebar
Tip: drag the edge of the sidebar to resize it.
+
diff --git a/assets/javascripts/tracking.js b/assets/javascripts/tracking.js
index ca05b218..a68ca493 100644
--- a/assets/javascripts/tracking.js
+++ b/assets/javascripts/tracking.js
@@ -1,28 +1,32 @@
try {
- if (app.config.env == 'production') {
- (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
- (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
- m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
- })(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
- ga('create', 'UA-5544833-12', 'devdocs.io');
- page.track(function() {
- ga('send', 'pageview', {
- page: location.pathname + location.search + location.hash,
- dimension1: app.router.context && app.router.context.doc && app.router.context.doc.slug_without_version
+ if (app.config.env === 'production') {
+ if (Cookies.get('analyticsConsent') === '1') {
+ (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
+ (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
+ m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
+ })(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
+ ga('create', 'UA-5544833-12', 'devdocs.io');
+ page.track(function() {
+ ga('send', 'pageview', {
+ page: location.pathname + location.search + location.hash,
+ dimension1: app.router.context && app.router.context.doc && app.router.context.doc.slug_without_version
+ });
});
- });
- page.track(function() {
- if (window._gauges)
- _gauges.push(['track']);
- else
- (function() {
- var _gauges=_gauges||[];!function(){var a=document.createElement("script");
- a.type="text/javascript",a.async=!0,a.id="gauges-tracker",
- a.setAttribute("data-site-id","51c15f82613f5d7819000067"),
- a.src="https://secure.gaug.es/track.js";var b=document.getElementsByTagName("script")[0];
- b.parentNode.insertBefore(a,b)}();
- })();
- });
+ page.track(function() {
+ if (window._gauges)
+ _gauges.push(['track']);
+ else
+ (function() {
+ var _gauges=_gauges||[];!function(){var a=document.createElement("script");
+ a.type="text/javascript",a.async=!0,a.id="gauges-tracker",
+ a.setAttribute("data-site-id","51c15f82613f5d7819000067"),
+ a.src="https://secure.gaug.es/track.js";var b=document.getElementsByTagName("script")[0];
+ b.parentNode.insertBefore(a,b)}();
+ })();
+ });
+ } else {
+ resetAnalytics();
+ }
}
} catch(e) { }
diff --git a/assets/javascripts/views/content/settings_page.coffee b/assets/javascripts/views/content/settings_page.coffee
index af2e9a9d..21de111a 100644
--- a/assets/javascripts/views/content/settings_page.coffee
+++ b/assets/javascripts/views/content/settings_page.coffee
@@ -14,6 +14,7 @@ class app.views.SettingsPage extends app.View
settings.dark = app.settings.get('dark')
settings.smoothScroll = !app.settings.get('fastScroll')
settings.arrowScroll = app.settings.get('arrowScroll')
+ settings.analyticsConsent = app.settings.get('analyticsConsent')
settings[layout] = app.settings.hasLayout(layout) for layout in app.settings.LAYOUTS
settings
@@ -32,6 +33,11 @@ class app.views.SettingsPage extends app.View
app.settings.set('fastScroll', !enable)
return
+ toggleAnalyticsConsent: (enable) ->
+ app.settings.set('analyticsConsent', if enable then '1' else '0')
+ resetAnalytics() unless enable
+ return
+
toggle: (name, enable) ->
app.settings.set(name, enable)
return
@@ -75,6 +81,8 @@ class app.views.SettingsPage extends app.View
@toggleSmoothScroll input.checked
when 'import'
@import input.files[0], input
+ when 'analyticsConsent'
+ @toggleAnalyticsConsent input.checked
else
@toggle input.name, input.checked
return
diff --git a/assets/javascripts/views/layout/document.coffee b/assets/javascripts/views/layout/document.coffee
index 02b98c7a..597dfe37 100644
--- a/assets/javascripts/views/layout/document.coffee
+++ b/assets/javascripts/views/layout/document.coffee
@@ -75,9 +75,11 @@ class app.views.Document extends app.View
return unless target.hasAttribute('data-behavior')
$.stopEvent(event)
switch target.getAttribute('data-behavior')
- when 'back' then history.back()
- when 'reload' then window.location.reload()
- when 'reboot' then app.reboot()
- when 'hard-reload' then app.reload()
- when 'reset' then app.reset() if confirm('Are you sure you want to reset DevDocs?')
+ when 'back' then history.back()
+ when 'reload' then window.location.reload()
+ when 'reboot' then app.reboot()
+ when 'hard-reload' then app.reload()
+ when 'reset' then app.reset() if confirm('Are you sure you want to reset DevDocs?')
+ when 'accept-analytics' then Cookies.set('analyticsConsent', '1') && app.reboot()
+ when 'decline-analytics' then Cookies.set('analyticsConsent', '0') && app.reboot()
return
diff --git a/assets/stylesheets/components/_notif.scss b/assets/stylesheets/components/_notif.scss
index dd23c43a..f0880fdd 100644
--- a/assets/stylesheets/components/_notif.scss
+++ b/assets/stylesheets/components/_notif.scss
@@ -134,3 +134,7 @@
._notif-info { color: var(--textColorLight); }
}
+
+._notif-right {
+ float: right;
+}