History

Merge branch 'master' into mariadb

Jasper van Merle committed 7 years ago

Commit facb0a42 parents 2933798 b34dbb2280 files · +3705 −678Browse files

Showing the changes to assets/javascripts (31 of 280 files). Show all

Unified · Split

31 files
  1. assets/javascripts/app/app.coffee +14 −9
  2. assets/javascripts/app/appcache.coffee +0 −42
  3. assets/javascripts/app/config.coffee.erb +3 −0
  4. assets/javascripts/app/serviceworker.coffee +49 −0
  5. assets/javascripts/app/settings.coffee +34 −1
  6. assets/javascripts/app/update_checker.coffee +3 −3
  7. assets/javascripts/lib/cookies_store.coffee +5 −1
  8. assets/javascripts/lib/favicon.coffee +64 −0
  9. assets/javascripts/lib/page.coffee +17 −1
  10. assets/javascripts/lib/util.coffee +4 −0
  11. assets/javascripts/models/doc.coffee +3 −1
  12. assets/javascripts/news.json +8 −0
  13. assets/javascripts/templates/error_tmpl.coffee +5 −5
  14. assets/javascripts/templates/notif_tmpl.coffee +6 −0
  15. assets/javascripts/templates/pages/about_tmpl.coffee +45 −10
  16. assets/javascripts/templates/pages/offline_tmpl.coffee +9 −4
  17. assets/javascripts/templates/pages/root_tmpl.coffee.erb +2 −2
  18. assets/javascripts/templates/pages/settings_tmpl.coffee +8 −0
  19. assets/javascripts/tracking.js +27 −23
  20. assets/javascripts/vendor/fastclick.js +2 −2
  21. assets/javascripts/vendor/raven.js +1 −1
  22. assets/javascripts/views/content/content.coffee +3 −0
  23. assets/javascripts/views/content/entry_page.coffee +2 −1
  24. assets/javascripts/views/content/settings_page.coffee +10 −11
  25. assets/javascripts/views/content/type_page.coffee +1 −0
  26. assets/javascripts/views/layout/document.coffee +7 −5
  27. assets/javascripts/views/layout/resizer.coffee +2 −9
  28. assets/javascripts/views/layout/settings.coffee +1 −9
  29. assets/javascripts/views/search/search.coffee +8 −0
  30. assets/javascripts/views/search/search_scope.coffee +23 −5
  31. assets/javascripts/views/sidebar/sidebar.coffee +1 −1
assets/javascripts/app/app.coffee+14−9
@@ -13,10 +13,12 @@ · 12 unmodified lines
13 13
14 @el = $('._app')14 @el = $('._app')
15 @localStorage = new LocalStorageStore15 @localStorage = new LocalStorageStore
16 @appCache = new app.AppCache if app.AppCache.isEnabled()16 @serviceWorker = new app.ServiceWorker if app.ServiceWorker.isEnabled()
17 @settings = new app.Settings17 @settings = new app.Settings
18 @db = new app.DB()18 @db = new app.DB()
19 19
20 @settings.initLayout()
21
20 @docs = new app.collections.Docs22 @docs = new app.collections.Docs
21 @disabledDocs = new app.collections.Docs23 @disabledDocs = new app.collections.Docs
22 @entries = new app.collections.Entries24 @entries = new app.collections.Entries
@@ -76,7 +78,7 @@ · 53 unmodified lines
76 .install()78 .install()
77 @previousErrorHandler = onerror79 @previousErrorHandler = onerror
78 window.onerror = @onWindowError.bind(@)80 window.onerror = @onWindowError.bind(@)
79 CookieStore.onBlocked = @onCookieBlocked81 CookiesStore.onBlocked = @onCookieBlocked
80 return82 return
81 83
82 bootOne: ->84 bootOne: ->
@@ -138,7 +140,10 @@ · 55 unmodified lines
138 @docs.sort()140 @docs.sort()
139 @initDoc(doc)141 @initDoc(doc)
140 @saveDocs()142 @saveDocs()
141 _onSuccess()143 if app.settings.get('autoInstall')
144 doc.install(_onSuccess, onError)
145 else
146 _onSuccess()
142 return147 return
143 148
144 doc.load onSuccess, onError, writeCache: true149 doc.load onSuccess, onError, writeCache: true
@@ -147,7 +152,7 @@ · 2 unmodified lines
147 saveDocs: ->152 saveDocs: ->
148 @settings.setDocs(doc.slug for doc in @docs.all())153 @settings.setDocs(doc.slug for doc in @docs.all())
149 @db.migrate()154 @db.migrate()
150 @appCache?.updateInBackground()155 @serviceWorker?.updateInBackground()
151 156
152 welcomeBack: ->157 welcomeBack: ->
153 visitCount = @settings.get('count')158 visitCount = @settings.get('count')
@@ -167,14 +172,14 @@ · 13 unmodified lines
167 reload: ->172 reload: ->
168 @docs.clearCache()173 @docs.clearCache()
169 @disabledDocs.clearCache()174 @disabledDocs.clearCache()
170 if @appCache then @appCache.reload() else @reboot()175 if @serviceWorker then @serviceWorker.reload() else @reboot()
171 return176 return
172 177
173 reset: ->178 reset: ->
174 @localStorage.reset()179 @localStorage.reset()
175 @settings.reset()180 @settings.reset()
176 @db?.reset()181 @db?.reset()
177 @appCache?.update()182 @serviceWorker?.update()
178 window.location = '/'183 window.location = '/'
179 return184 return
180 185
@@ -193,9 +198,9 @@ · 12 unmodified lines
193 return198 return
194 199
195 indexHost: ->200 indexHost: ->
196 # Can't load the index files from the host/CDN when applicationCache is201 # Can't load the index files from the host/CDN when service worker is
197 # enabled because it doesn't support caching URLs that use CORS.202 # enabled because it doesn't support caching URLs that use CORS.
198 @config[if @appCache and @settings.hasDocs() then 'index_path' else 'docs_origin']203 @config[if @serviceWorker and @settings.hasDocs() then 'index_path' else 'docs_origin']
199 204
200 onBootError: (args...) ->205 onBootError: (args...) ->
201 @trigger 'bootError'206 @trigger 'bootError'
@@ -252,7 +257,7 @@ · 50 unmodified lines
252 matchMedia: !!window.matchMedia257 matchMedia: !!window.matchMedia
253 insertAdjacentHTML: !!document.body.insertAdjacentHTML258 insertAdjacentHTML: !!document.body.insertAdjacentHTML
254 defaultPrevented: document.createEvent('CustomEvent').defaultPrevented is false259 defaultPrevented: document.createEvent('CustomEvent').defaultPrevented is false
255 cssVariables: CSS.supports and CSS.supports('(--t: 0)')260 cssVariables: !!CSS?.supports?('(--t: 0)')
256 261
257 for key, value of features when not value262 for key, value of features when not value
258 Raven.captureMessage "unsupported/#{key}", level: 'info'263 Raven.captureMessage "unsupported/#{key}", level: 'info'
assets/javascripts/app/appcache.coffee+0−42
@@ -1,42 +0,0 @@
1class app.AppCache
2 $.extend @prototype, Events
3
4 @isEnabled: ->
5 try
6 applicationCache and applicationCache.status isnt applicationCache.UNCACHED
7 catch
8
9 constructor: ->
10 @cache = applicationCache
11 @notifyUpdate = true
12 @onUpdateReady() if @cache.status is @cache.UPDATEREADY
13
14 $.on @cache, 'progress', @onProgress
15 $.on @cache, 'updateready', @onUpdateReady
16
17 update: ->
18 @notifyUpdate = true
19 @notifyProgress = true
20 try @cache.update() catch
21 return
22
23 updateInBackground: ->
24 @notifyUpdate = false
25 @notifyProgress = false
26 try @cache.update() catch
27 return
28
29 reload: ->
30 $.on @cache, 'updateready noupdate error', -> app.reboot()
31 @notifyUpdate = false
32 @notifyProgress = true
33 try @cache.update() catch
34 return
35
36 onProgress: (event) =>
37 @trigger 'progress', event if @notifyProgress
38 return
39
40 onUpdateReady: =>
41 @trigger 'updateready' if @notifyUpdate
42 return
assets/javascripts/app/config.coffee.erb+3−0
@@ -13,3 +13,6 @@ app.config = · 12 unmodified lines
13 version: <%= Time.now.to_i %>13 version: <%= Time.now.to_i %>
14 release: <%= Time.now.utc.httpdate.to_json %>14 release: <%= Time.now.utc.httpdate.to_json %>
15 mathml_stylesheet: '<%= App.cdn_origin %>/mathml.css'15 mathml_stylesheet: '<%= App.cdn_origin %>/mathml.css'
16 favicon_spritesheet: '<%= image_path('sprites/docs.png') %>'
17 service_worker_path: '/service-worker.js'
18 service_worker_enabled: <%= App.environment == :production || ENV['ENABLE_SERVICE_WORKER'] == 'true' %>
assets/javascripts/app/serviceworker.coffee+49−0
@@ -0,0 +1,49 @@
1class app.ServiceWorker
2 $.extend @prototype, Events
3
4 @isEnabled: ->
5 !!navigator.serviceWorker and app.config.service_worker_enabled
6
7 constructor: ->
8 @registration = null
9 @notifyUpdate = true
10
11 navigator.serviceWorker.register(app.config.service_worker_path, {scope: '/'})
12 .then(
13 (registration) => @updateRegistration(registration),
14 (error) -> console.error('Could not register service worker:', error)
15 )
16
17 update: ->
18 return unless @registration
19 @notifyUpdate = true
20 return @registration.update().catch(->)
21
22 updateInBackground: ->
23 return unless @registration
24 @notifyUpdate = false
25 return @registration.update().catch(->)
26
27 reload: ->
28 return @updateInBackground().then(() -> app.reboot())
29
30 updateRegistration: (registration) ->
31 @registration = registration
32 $.on @registration, 'updatefound', @onUpdateFound
33 return
34
35 onUpdateFound: =>
36 $.off @installingRegistration, 'statechange', @onStateChange() if @installingRegistration
37 @installingRegistration = @registration.installing
38 $.on @installingRegistration, 'statechange', @onStateChange
39 return
40
41 onStateChange: =>
42 if @installingRegistration and @installingRegistration.state == 'installed' and navigator.serviceWorker.controller
43 @installingRegistration = null
44 @onUpdateReady()
45 return
46
47 onUpdateReady: ->
48 @trigger 'updateready' if @notifyUpdate
49 return
assets/javascripts/app/settings.coffee+34−1
@@ -5,11 +5,13 @@ class app.Settings · 4 unmodified lines
5 'manualUpdate'5 'manualUpdate'
6 'fastScroll'6 'fastScroll'
7 'arrowScroll'7 'arrowScroll'
8 'analyticsConsent'
8 'docs'9 'docs'
9 'dark'10 'dark'
10 'layout'11 'layout'
11 'size'12 'size'
12 'tips'13 'tips'
14 'autoInstall'
13 ]15 ]
14 16
15 INTERNAL_KEYS = [17 INTERNAL_KEYS = [
@@ -19,6 +21,8 @@ class app.Settings · 3 unmodified lines
19 'news'21 'news'
20 ]22 ]
21 23
24 LAYOUTS: ['_max-width', '_sidebar-hidden', '_native-scrollbars']
25
22 @defaults:26 @defaults:
23 count: 027 count: 0
24 hideDisabled: false28 hideDisabled: false
@@ -26,9 +30,10 @@ class app.Settings · 1 unmodified line
26 news: 030 news: 0
27 manualUpdate: false31 manualUpdate: false
28 schema: 132 schema: 1
33 analyticsConsent: false
29 34
30 constructor: ->35 constructor: ->
31 @store = new CookieStore36 @store = new CookiesStore
32 @cache = {}37 @cache = {}
33 38
34 get: (key) ->39 get: (key) ->
@@ -38,6 +43,7 @@ class app.Settings · 3 unmodified lines
38 set: (key, value) ->43 set: (key, value) ->
39 @store.set(key, value)44 @store.set(key, value)
40 delete @cache[key]45 delete @cache[key]
46 @toggleDark(value) if key == 'dark'
41 return47 return
42 48
43 del: (key) ->49 del: (key) ->
@@ -63,6 +69,8 @@ class app.Settings · 19 unmodified lines
63 return69 return
64 70
65 setLayout: (name, enable) ->71 setLayout: (name, enable) ->
72 @toggleLayout(name, enable)
73
66 layout = (@store.get('layout') || '').split(' ')74 layout = (@store.get('layout') || '').split(' ')
67 $.arrayDelete(layout, '')75 $.arrayDelete(layout, '')
68 76
@@ -104,3 +112,28 @@ class app.Settings · 35 unmodified lines
104 @store.reset()112 @store.reset()
105 @cache = {}113 @cache = {}
106 return114 return
115
116 initLayout: ->
117 @toggleDark(@get('dark') is 1)
118 @toggleLayout(layout, @hasLayout(layout)) for layout in @LAYOUTS
119 @initSidebarWidth()
120 return
121
122 toggleDark: (enable) ->
123 classList = document.documentElement.classList
124 classList.toggle('_theme-default', !enable)
125 classList.toggle('_theme-dark', enable)
126 color = getComputedStyle(document.documentElement).getPropertyValue('--headerBackground').trim()
127 $('meta[name=theme-color]').setAttribute('content', color)
128 return
129
130 toggleLayout: (layout, enable) ->
131 classList = document.body.classList
132 classList.toggle(layout, enable) unless layout is '_sidebar-hidden'
133 classList.toggle('_overlay-scrollbars', $.overlayScrollbarsEnabled())
134 return
135
136 initSidebarWidth: ->
137 size = @get('size')
138 document.documentElement.style.setProperty('--sidebarWidth', size + 'px') if size
139 return
assets/javascripts/app/update_checker.coffee+3−3
@@ -3,13 +3,13 @@ class app.UpdateChecker · 2 unmodified lines
3 @lastCheck = Date.now()3 @lastCheck = Date.now()
4 4
5 $.on window, 'focus', @onFocus5 $.on window, 'focus', @onFocus
6 app.appCache.on 'updateready', @onUpdateReady if app.appCache6 app.serviceWorker?.on 'updateready', @onUpdateReady
7 7
8 setTimeout @checkDocs, 08 setTimeout @checkDocs, 0
9 9
10 check: ->10 check: ->
11 if app.appCache11 if app.serviceWorker
12 app.appCache.update()12 app.serviceWorker.update()
13 else13 else
14 ajax14 ajax
15 url: $('script[src*="application"]').getAttribute('src')15 url: $('script[src*="application"]').getAttribute('src')
assets/javascripts/lib/cookies_store.coffeerenamed from assets/javascripts/lib/cookie_store.coffee+5−1
@@ -1,4 +1,8 @@
1class @CookieStore1class @CookiesStore
2 # Intentionally called CookiesStore instead of CookieStore
3 # Calling it CookieStore causes issues when the Experimental Web Platform features flag is enabled in Chrome
4 # Related issue: https://github.com/freeCodeCamp/devdocs/issues/932
5
2 INT = /^\d+$/6 INT = /^\d+$/
3 7
4 @onBlocked: ->8 @onBlocked: ->
assets/javascripts/lib/favicon.coffee+64−0
@@ -0,0 +1,64 @@
1defaultUrl = null
2currentSlug = null
3
4imageCache = {}
5urlCache = {}
6
7withImage = (url, action) ->
8 if imageCache[url]
9 action(imageCache[url])
10 else
11 img = new Image()
12 img.crossOrigin = 'anonymous'
13 img.src = url
14 img.onload = () =>
15 imageCache[url] = img
16 action(img)
17
18@setFaviconForDoc = (doc) ->
19 return if currentSlug == doc.slug
20
21 favicon = $('link[rel="icon"]')
22
23 if defaultUrl == null
24 defaultUrl = favicon.href
25
26 if urlCache[doc.slug]
27 favicon.href = urlCache[doc.slug]
28 currentSlug = doc.slug
29 return
30
31 styles = window.getComputedStyle($("._icon-#{doc.slug.split('~')[0]}"), ':before')
32
33 bgUrl = app.config.favicon_spritesheet
34 sourceSize = 16
35 sourceX = Math.abs(parseInt(styles['background-position-x'].slice(0, -2)))
36 sourceY = Math.abs(parseInt(styles['background-position-y'].slice(0, -2)))
37
38 withImage(bgUrl, (docImg) ->
39 withImage(defaultUrl, (defaultImg) ->
40 size = defaultImg.width
41
42 canvas = document.createElement('canvas')
43 ctx = canvas.getContext('2d')
44
45 canvas.width = size
46 canvas.height = size
47 ctx.drawImage(defaultImg, 0, 0)
48
49 docIconPercentage = 65
50 destinationCoords = size / 100 * (100 - docIconPercentage)
51 destinationSize = size / 100 * docIconPercentage
52 ctx.drawImage(docImg, sourceX, sourceY, sourceSize, sourceSize, destinationCoords, destinationCoords, destinationSize, destinationSize)
53
54 urlCache[doc.slug] = canvas.toDataURL()
55 favicon.href = urlCache[doc.slug]
56
57 currentSlug = doc.slug
58 )
59 )
60
61@resetFavicon = () ->
62 if defaultUrl != null and currentSlug != null
63 $('link[rel="icon"]').href = defaultUrl
64 currentSlug = null
assets/javascripts/lib/page.coffee+17−1
@@ -199,5 +199,21 @@ page.track = (fn) -> · 198 unmodified lines
199 return199 return
200 200
201track = ->201track = ->
202 tracker.call() for tracker in trackers202 consentGiven = Cookies.get('analyticsConsent')
203 consentAsked = Cookies.get('analyticsConsentAsked')
204
205 if consentGiven == '1'
206 tracker.call() for tracker in trackers
207 else if consentGiven == undefined and consentAsked == undefined
208 # Only ask for consent once per browser session
209 Cookies.set('analyticsConsentAsked', '1')
210
211 new app.views.Notif 'AnalyticsConsent', autoHide: null
212 return
213
214@resetAnalytics = ->
215 for cookie in document.cookie.split(/;\s?/)
216 name = cookie.split('=')[0]
217 if name[0] == '_' && name[1] != '_'
218 Cookies.expire(name)
203 return219 return
assets/javascripts/lib/util.coffee+4−0
@@ -352,6 +352,10 @@ isIE = null · 351 unmodified lines
352$.isIE = ->352$.isIE = ->
353 isIE ?= navigator.userAgent?.indexOf('MSIE') >= 0 || navigator.userAgent?.indexOf('rv:11.0') >= 0353 isIE ?= navigator.userAgent?.indexOf('MSIE') >= 0 || navigator.userAgent?.indexOf('rv:11.0') >= 0
354 354
355isChromeForAndroid = null
356$.isChromeForAndroid = ->
357 isChromeForAndroid ?= navigator.userAgent?.indexOf('Android') >= 0 && /Chrome\/([.0-9])+ Mobile/.test(navigator.userAgent)
358
355isAndroid = null359isAndroid = null
356$.isAndroid = ->360$.isAndroid = ->
357 isAndroid ?= navigator.userAgent?.indexOf('Android') >= 0361 isAndroid ?= navigator.userAgent?.indexOf('Android') >= 0
assets/javascripts/models/doc.coffee+3−1
@@ -142,4 +142,6 @@ class app.models.Doc extends app.Model · 141 unmodified lines
142 return142 return
143 143
144 isOutdated: (status) ->144 isOutdated: (status) ->
145 status and status.installed and @mtime isnt status.mtime145 return false if not status
146 isInstalled = status.installed or app.settings.get('autoInstall')
147 isInstalled and @mtime isnt status.mtime
assets/javascripts/news.json+8−0
@@ -1,4 +1,12 @@
1[1[
2 [
3 "2019-07-21",
4 "Fixed several bugs, added an option to automatically download documentation and <a href=\"https://github.com/freeCodeCamp/devdocs/pulls?utf8=%E2%9C%93&q=is%3Apr+is%3Aclosed+sort%3Aupdated-desc+closed%3A%3E2019-07-18+977+988+986+870+886+1024+979+975+941+831+1005+848+942\" target=\"_blank\">more</a>."
5 ],
6 [
7 "2019-07-19",
8 "Replaced the AppCache with a Service Worker (which makes DevDocs an installable PWA) and fixed layout preferences on Firefox."
9 ],
2 [10 [
3 "2018-09-23",11 "2018-09-23",
4 "New documentations: <a href=\"/puppeteer/\">Puppeteer</a> and <a href=\"/handlebars/\">Handlebars.js</a>"12 "New documentations: <a href=\"/puppeteer/\">Puppeteer</a> and <a href=\"/handlebars/\">Handlebars.js</a>"
assets/javascripts/templates/error_tmpl.coffee+5−5
@@ -12,8 +12,8 @@ app.templates.notFoundPage = -> · 11 unmodified lines
12 12
13app.templates.pageLoadError = ->13app.templates.pageLoadError = ->
14 error """ The page failed to load. """,14 error """ The page failed to load. """,
15 """ It may be missing from the server (try reloading the app) or you could be offline.<br>15 """ It may be missing from the server (try reloading the app) or you could be offline (try <a href="/offline">installing the documentation for offline usage</a> when online again).<br>
16 If you keep seeing this, you're likely behind a proxy or firewall that blocks cross-domain requests. """,16 If you're online and you keep seeing this, you're likely behind a proxy or firewall that blocks cross-domain requests. """,
17 """ #{back} &middot; <a href="/##{location.pathname}" target="_top" class="_error-link">Reload</a>17 """ #{back} &middot; <a href="/##{location.pathname}" target="_top" class="_error-link">Reload</a>
18 &middot; <a href="#" class="_error-link" data-retry>Retry</a> """18 &middot; <a href="#" class="_error-link" data-retry>Retry</a> """
19 19
@@ -57,9 +57,9 @@ app.templates.unsupportedBrowser = """ · 37 unmodified lines
57 <p class="_fail-text">DevDocs is an API documentation browser which supports the following browsers:57 <p class="_fail-text">DevDocs is an API documentation browser which supports the following browsers:
58 <ul class="_fail-list">58 <ul class="_fail-list">
59 <li>Recent versions of Firefox, Chrome, or Opera59 <li>Recent versions of Firefox, Chrome, or Opera
60 <li>Safari 9.1+60 <li>Safari 11.1+
61 <li>Edge 16+61 <li>Edge 17+
62 <li>iOS 10+62 <li>iOS 11.3+
63 </ul>63 </ul>
64 <p class="_fail-text">64 <p class="_fail-text">
65 If you're unable to upgrade, we apologize.65 If you're unable to upgrade, we apologize.
assets/javascripts/templates/notif_tmpl.coffee+6−0
@@ -68,3 +68,9 @@ app.templates.notifShare = -> · 67 unmodified lines
68app.templates.notifUpdateDocs = ->68app.templates.notifUpdateDocs = ->
69 textNotif """ Documentation updates available. """,69 textNotif """ Documentation updates available. """,
70 """ <a href="/offline">Install them</a> as soon as possible to avoid broken pages. """70 """ <a href="/offline">Install them</a> as soon as possible to avoid broken pages. """
71
72app.templates.notifAnalyticsConsent = ->
73 textNotif """ Tracking cookies """,
74 """ We would like to gather usage data about how DevDocs is used through Google Analytics and Gauges. We only collect anonymous traffic information.
75 Please confirm if you accept our tracking cookies. You can always change your decision in the settings.
76 <br><span class="_notif-right"><a href="#" data-behavior="accept-analytics">Accept</a> or <a href="#" data-behavior="decline-analytics">Decline</a></span> """
assets/javascripts/templates/pages/about_tmpl.coffee+45−10
@@ -73,7 +73,8 @@ app.templates.aboutPage = -> """ · 72 unmodified lines
73 <ul>73 <ul>
74 <li><a href="https://devdocs.io">devdocs.io</a> ("App") is operated by <a href="https://www.freecodecamp.org/">freeCodeCamp</a> ("We").74 <li><a href="https://devdocs.io">devdocs.io</a> ("App") is operated by <a href="https://www.freecodecamp.org/">freeCodeCamp</a> ("We").
75 <li>We do not collect personal information through the app.75 <li>We do not collect personal information through the app.
76 <li>We use Google Analytics, Gauges and Sentry to collect anonymous traffic information and improve the app.76 <li>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 <a href="/settings">settings</a>.
77 <li>We use Sentry to collect crash data and improve the app.
77 <li>The app uses cookies to store user preferences.78 <li>The app uses cookies to store user preferences.
78 <li>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.79 <li>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.
79 <li>If you have any questions regarding privacy, please email <a href="mailto:privacy@freecodecamp.org">privacy@freecodecamp.org</a>.80 <li>If you have any questions regarding privacy, please email <a href="mailto:privacy@freecodecamp.org">privacy@freecodecamp.org</a>.
@@ -87,7 +88,7 @@ credits = [ · 7 unmodified lines
87 'https://creativecommons.org/licenses/by/4.0/'88 'https://creativecommons.org/licenses/by/4.0/'
88 ], [89 ], [
89 'Ansible',90 'Ansible',
90 '2012-2018 Michael DeHaan<br>&copy; 2018 Red Hat, Inc.',91 '2012-2018 Michael DeHaan<br>&copy; 2018–2019 Red Hat, Inc.',
91 'GPLv3',92 'GPLv3',
92 'https://raw.githubusercontent.com/ansible/ansible/devel/COPYING'93 'https://raw.githubusercontent.com/ansible/ansible/devel/COPYING'
93 ], [94 ], [
@@ -122,7 +123,7 @@ credits = [ · 28 unmodified lines
122 'https://raw.githubusercontent.com/petkaantonov/bluebird/master/LICENSE'123 'https://raw.githubusercontent.com/petkaantonov/bluebird/master/LICENSE'
123 ], [124 ], [
124 'Bootstrap',125 'Bootstrap',
125 '2011-2018 Twitter, Inc.<br>2011-2018 The Bootstrap Authors',126 '2011-2019 Twitter, Inc.<br>2011-2019 The Bootstrap Authors',
126 'CC BY',127 'CC BY',
127 'https://creativecommons.org/licenses/by/3.0/'128 'https://creativecommons.org/licenses/by/3.0/'
128 ], [129 ], [
@@ -162,7 +163,7 @@ credits = [ · 33 unmodified lines
162 'https://github.com/clojure/clojure/blob/master/epl-v10.html'163 'https://github.com/clojure/clojure/blob/master/epl-v10.html'
163 ], [164 ], [
164 'CMake',165 'CMake',
165 '2000-2018 Kitware, Inc. and Contributors',166 '2000-2019 Kitware, Inc. and Contributors',
166 'BSD',167 'BSD',
167 'https://cmake.org/licensing/'168 'https://cmake.org/licensing/'
168 ], [169 ], [
@@ -185,6 +186,11 @@ credits = [ · 16 unmodified lines
185 '2009-2018 Jeremy Ashkenas',186 '2009-2018 Jeremy Ashkenas',
186 'MIT',187 'MIT',
187 'https://raw.githubusercontent.com/jashkenas/coffeescript/master/LICENSE'188 'https://raw.githubusercontent.com/jashkenas/coffeescript/master/LICENSE'
189 ], [
190 'Composer',
191 'Nils Adermann, Jordi Boggiano',
192 'MIT',
193 'https://raw.githubusercontent.com/composer/composer/master/LICENSE'
188 ], [194 ], [
189 'Cordova',195 'Cordova',
190 '2012-2018 The Apache Software Foundation',196 '2012-2018 The Apache Software Foundation',
@@ -201,6 +207,12 @@ credits = [ · 10 unmodified lines
201 'Apache',207 'Apache',
202 'https://raw.githubusercontent.com/crystal-lang/crystal/master/LICENSE'208 'https://raw.githubusercontent.com/crystal-lang/crystal/master/LICENSE'
203 ], [209 ], [
210 'Cypress',
211 '2017 Cypress.io',
212 'MIT',
213 'https://raw.githubusercontent.com/cypress-io/cypress-documentation/develop/LICENSE.md'
214 ],
215 [
204 'D',216 'D',
205 '1999-2018 The D Language Foundation',217 '1999-2018 The D Language Foundation',
206 'Boost',218 'Boost',
@@ -222,7 +234,7 @@ credits = [ · 15 unmodified lines
222 'https://raw.githubusercontent.com/django/django/master/LICENSE'234 'https://raw.githubusercontent.com/django/django/master/LICENSE'
223 ], [235 ], [
224 'Docker',236 'Docker',
225 '2017 Docker, Inc.<br>Docker and the Docker logo are trademarks of Docker, Inc.',237 '2019 Docker, Inc.<br>Docker and the Docker logo are trademarks of Docker, Inc.',
226 'Apache',238 'Apache',
227 'https://raw.githubusercontent.com/docker/docker.github.io/master/LICENSE'239 'https://raw.githubusercontent.com/docker/docker.github.io/master/LICENSE'
228 ], [240 ], [
@@ -387,7 +399,7 @@ credits = [ · 158 unmodified lines
387 'https://raw.githubusercontent.com/koajs/koa/master/LICENSE'399 'https://raw.githubusercontent.com/koajs/koa/master/LICENSE'
388 ], [400 ], [
389 'Kotlin',401 'Kotlin',
390 '2010-2018 JetBrains s.r.o.',402 '2010-2019 JetBrains s.r.o.',
391 'Apache',403 'Apache',
392 'https://raw.githubusercontent.com/JetBrains/kotlin/master/license/LICENSE.txt'404 'https://raw.githubusercontent.com/JetBrains/kotlin/master/license/LICENSE.txt'
393 ], [405 ], [
@@ -397,7 +409,7 @@ credits = [ · 3 unmodified lines
397 'https://raw.githubusercontent.com/laravel/framework/master/LICENSE.txt'409 'https://raw.githubusercontent.com/laravel/framework/master/LICENSE.txt'
398 ], [410 ], [
399 'Leaflet',411 'Leaflet',
400 '2010-2018 Vladimir Agafonkin<br>&copy; 2010-2011, CloudMade<br>Maps &copy; OpenStreetMap contributors.',412 '2010-2019 Vladimir Agafonkin<br>&copy; 2010-2011, CloudMade<br>Maps &copy; OpenStreetMap contributors.',
401 'BSD',413 'BSD',
402 'https://raw.githubusercontent.com/Leaflet/Leaflet/master/LICENSE'414 'https://raw.githubusercontent.com/Leaflet/Leaflet/master/LICENSE'
403 ], [415 ], [
@@ -477,7 +489,7 @@ credits = [ · 73 unmodified lines
477 'https://github.com/LearnBoost/mongoose/blob/master/README.md#license'489 'https://github.com/LearnBoost/mongoose/blob/master/README.md#license'
478 ], [490 ], [
479 'nginx',491 'nginx',
480 '2002-2018 Igor Sysoev<br>&copy; 2011-2018 Nginx, Inc.',492 '2002-2019 Igor Sysoev<br>&copy; 2011-2019 Nginx, Inc.',
481 'BSD',493 'BSD',
482 'http://nginx.org/LICENSE'494 'http://nginx.org/LICENSE'
483 ], [495 ], [
@@ -522,7 +534,7 @@ credits = [ · 38 unmodified lines
522 'https://raw.githubusercontent.com/OpenTSDB/opentsdb.net/gh-pages/COPYING.LESSER'534 'https://raw.githubusercontent.com/OpenTSDB/opentsdb.net/gh-pages/COPYING.LESSER'
523 ], [535 ], [
524 'Padrino',536 'Padrino',
525 '2010-2016 Padrino',537 '2010-2019 Padrino',
526 'MIT',538 'MIT',
527 'https://raw.githubusercontent.com/padrino/padrino-framework/master/padrino/LICENSE.txt'539 'https://raw.githubusercontent.com/padrino/padrino-framework/master/padrino/LICENSE.txt'
528 ], [540 ], [
@@ -572,7 +584,7 @@ credits = [ · 43 unmodified lines
572 'https://raw.githubusercontent.com/GoogleChrome/puppeteer/master/LICENSE'584 'https://raw.githubusercontent.com/GoogleChrome/puppeteer/master/LICENSE'
573 ], [585 ], [
574 'Pygame',586 'Pygame',
575 'Pygame Developpers',587 'Pygame Developers',
576 'LGPLv2.1',588 'LGPLv2.1',
577 'https://raw.githubusercontent.com/pygame/pygame/master/LICENSE'589 'https://raw.githubusercontent.com/pygame/pygame/master/LICENSE'
578 ], [590 ], [
@@ -636,11 +648,23 @@ credits = [ · 57 unmodified lines
636 'MIT',648 'MIT',
637 'https://raw.githubusercontent.com/rust-lang/rust/master/LICENSE-MIT'649 'https://raw.githubusercontent.com/rust-lang/rust/master/LICENSE-MIT'
638 ], [650 ], [
651 'Salt Stack',
652 '2019 SaltStack',
653 'Apache',
654 'https://raw.githubusercontent.com/saltstack/salt/develop/LICENSE'
655 ],
656 [
639 'Sass',657 'Sass',
640 '2006-2016 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein',658 '2006-2016 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein',
641 'MIT',659 'MIT',
642 'https://raw.githubusercontent.com/sass/sass/stable/MIT-LICENSE'660 'https://raw.githubusercontent.com/sass/sass/stable/MIT-LICENSE'
643 ], [661 ], [
662 'Scala',
663 '2002-2019 EPFL, with contributions from Lightbend',
664 'Apache',
665 'https://raw.githubusercontent.com/scala/scala-lang/master/license.md'
666 ],
667 [
644 'scikit-image',668 'scikit-image',
645 '2011 the scikit-image team',669 '2011 the scikit-image team',
646 'BSD',670 'BSD',
@@ -715,6 +739,11 @@ credits = [ · 68 unmodified lines
715 '2013-2018 Evan You, Vue.js contributors',739 '2013-2018 Evan You, Vue.js contributors',
716 'MIT',740 'MIT',
717 'https://raw.githubusercontent.com/vuejs/vue/master/LICENSE'741 'https://raw.githubusercontent.com/vuejs/vue/master/LICENSE'
742 ], [
743 'Vue Router',
744 '2013-present Evan You',
745 'MIT',
746 'https://raw.githubusercontent.com/vuejs/vue-router/dev/LICENSE'
718 ], [747 ], [
719 'Vulkan',748 'Vulkan',
720 '2014-2017 Khronos Group Inc.<br>Vulkan and the Vulkan logo are registered trademarks of the Khronos Group Inc.',749 '2014-2017 Khronos Group Inc.<br>Vulkan and the Vulkan logo are registered trademarks of the Khronos Group Inc.',
@@ -726,6 +755,12 @@ credits = [ · 5 unmodified lines
726 'CC BY',755 'CC BY',
727 'https://creativecommons.org/licenses/by/4.0/'756 'https://creativecommons.org/licenses/by/4.0/'
728 ], [757 ], [
758 'Wordpress',
759 '2003-2019 WordPress Foundation',
760 'GPLv2+',
761 'https://wordpress.org/about/license/'
762 ],
763 [
729 'Yarn',764 'Yarn',
730 '2016-present Yarn Contributors',765 '2016-present Yarn Contributors',
731 'BSD',766 'BSD',
assets/javascripts/templates/pages/offline_tmpl.coffee+9−4
@@ -25,8 +25,8 @@ app.templates.offlinePage = (docs) -> """ · 24 unmodified lines
25 <h2 class="_block-heading">Questions & Answers</h2>25 <h2 class="_block-heading">Questions & Answers</h2>
26 <dl>26 <dl>
27 <dt>How does this work?27 <dt>How does this work?
28 <dd>Each page is cached as a key-value pair in <a href="https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API">IndexedDB</a> (downloaded from a single file).<br>28 <dd>Each page is cached as a key-value pair in <a href="https://devdocs.io/dom/indexeddb_api">IndexedDB</a> (downloaded from a single file).<br>
29 The app also uses <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Using_the_application_cache">AppCache</a> and <a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API">localStorage</a> to cache the assets and index files.29 The app also uses <a href="https://devdocs.io/dom/service_worker_api/using_service_workers">Service Workers</a> and <a href="https://devdocs.io/dom/web_storage_api">localStorage</a> to cache the assets and index files.
30 <dt>Can I close the tab/browser?30 <dt>Can I close the tab/browser?
31 <dd>#{canICloseTheTab()}31 <dd>#{canICloseTheTab()}
32 <dt>What if I don't update a documentation?32 <dt>What if I don't update a documentation?
@@ -41,10 +41,15 @@ app.templates.offlinePage = (docs) -> """ · 8 unmodified lines
41"""41"""
42 42
43canICloseTheTab = ->43canICloseTheTab = ->
44 if app.AppCache.isEnabled()44 if app.ServiceWorker.isEnabled()
45 """ Yes! Even offline, you can open a new tab, go to <a href="//devdocs.io">devdocs.io</a>, and everything will work as if you were online (provided you installed all the documentations you want to use beforehand). """45 """ Yes! Even offline, you can open a new tab, go to <a href="//devdocs.io">devdocs.io</a>, and everything will work as if you were online (provided you installed all the documentations you want to use beforehand). """
46 else46 else
47 """ No. AppCache isn't available in your browser (or is disabled), so loading <a href="//devdocs.io">devdocs.io</a> offline won't work.<br>47 reason = "aren't available in your browser (or are disabled)"
48
49 if app.config.env != 'production'
50 reason = "are disabled in your development instance of DevDocs (enable them by setting the <code>ENABLE_SERVICE_WORKER</code> environment variable to <code>true</code>)"
51
52 """ No. Service Workers #{reason}, so loading <a href="//devdocs.io">devdocs.io</a> offline won't work.<br>
48 The current tab will continue to function even when you go offline (provided you installed all the documentations beforehand). """53 The current tab will continue to function even when you go offline (provided you installed all the documentations beforehand). """
49 54
50app.templates.offlineDoc = (doc, status) ->55app.templates.offlineDoc = (doc, status) ->
assets/javascripts/templates/pages/root_tmpl.coffee.erb+2−2
@@ -8,7 +8,7 @@ app.templates.intro = """ · 7 unmodified lines
8 <p>Thanks for downloading DevDocs. Here are a few things you should know:8 <p>Thanks for downloading DevDocs. Here are a few things you should know:
9 <ol class="_intro-list">9 <ol class="_intro-list">
10 <li>Your local version of DevDocs won't self-update. Unless you're modifying the code,10 <li>Your local version of DevDocs won't self-update. Unless you're modifying the code,
11 I&nbsp;recommend using the hosted version at <a href="https://devdocs.io">devdocs.io</a>.11 we&nbsp;recommend using the hosted version at <a href="https://devdocs.io">devdocs.io</a>.
12 <li>Run <code>thor docs:list</code> to see all available documentations.12 <li>Run <code>thor docs:list</code> to see all available documentations.
13 <li>Run <code>thor docs:download &lt;name&gt;</code> to download documentations.13 <li>Run <code>thor docs:download &lt;name&gt;</code> to download documentations.
14 <li>Run <code>thor docs:download --installed</code> to update all downloaded documentations.14 <li>Run <code>thor docs:download --installed</code> to update all downloaded documentations.
@@ -39,7 +39,7 @@ app.templates.intro = """ · 24 unmodified lines
39 <li>DevDocs works <a href="/offline">offline</a>, on mobile, and can be installed on <a href="https://chrome.google.com/webstore/detail/devdocs/mnfehgbmkapmjnhcnbodoamcioleeooe">Chrome</a>.39 <li>DevDocs works <a href="/offline">offline</a>, on mobile, and can be installed on <a href="https://chrome.google.com/webstore/detail/devdocs/mnfehgbmkapmjnhcnbodoamcioleeooe">Chrome</a>.
40 <li>For the latest news, follow <a href="https://twitter.com/DevDocs">@DevDocs</a>.40 <li>For the latest news, follow <a href="https://twitter.com/DevDocs">@DevDocs</a>.
41 <li>DevDocs is free and <a href="https://github.com/freeCodeCamp/devdocs">open source</a>.41 <li>DevDocs is free and <a href="https://github.com/freeCodeCamp/devdocs">open source</a>.
42 <iframe class="_github-btn" src="//ghbtns.com/github-btn.html?user=freeCodeCamp&repo=devdocs&type=watch&count=true" allowtransparency="true" frameborder="0" scrolling="0" width="100" height="20"></iframe>42 <object data="https://img.shields.io/github/stars/freeCodeCamp/devdocs.svg?style=social" type="image/svg+xml" aria-hidden="true"></object>
43 <li>And if you're new to coding, check out <a href="https://www.freecodecamp.org/">freeCodeCamp's open source curriculum</a>.43 <li>And if you're new to coding, check out <a href="https://www.freecodecamp.org/">freeCodeCamp's open source curriculum</a>.
44 </ol>44 </ol>
45 <p>Happy coding!45 <p>Happy coding!
assets/javascripts/templates/pages/settings_tmpl.coffee+8−0
@@ -15,6 +15,14 @@ app.templates.settingsPage = (settings) -> """ · 14 unmodified lines
15 <input type="checkbox" form="settings" name="layout" value="_sidebar-hidden"#{if settings['_sidebar-hidden'] then ' checked' else ''}>Automatically hide and show the sidebar15 <input type="checkbox" form="settings" name="layout" value="_sidebar-hidden"#{if settings['_sidebar-hidden'] then ' checked' else ''}>Automatically hide and show the sidebar
16 <small>Tip: drag the edge of the sidebar to resize it.</small>16 <small>Tip: drag the edge of the sidebar to resize it.</small>
17 </label>17 </label>
18 <label class="_settings-label">
19 <input type="checkbox" form="settings" name="autoInstall" value="_auto-install"#{if settings.autoInstall then ' checked' else ''}>Automatically download documentation for offline use
20 <small>Only enable this when bandwidth isn't a concern to you.</small>
21 </label>
22 <label class="_settings-label">
23 <input type="checkbox" form="settings" name="analyticsConsent"#{if settings.analyticsConsent then ' checked' else ''}>Enable tracking cookies
24 <small>With this checked, we enable Google Analytics and Gauges to collect anonymous traffic information.</small>
25 </label>
18 </div>26 </div>
19 </div>27 </div>
20 28
assets/javascripts/tracking.js+27−23
@@ -1,28 +1,32 @@
1try {1try {
2 if (app.config.env == 'production') {2 if (app.config.env === 'production') {
3 (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){3 if (Cookies.get('analyticsConsent') === '1') {
4 (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),4 (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
5 m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)5 (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
6 })(window,document,'script','https://www.google-analytics.com/analytics.js','ga');6 m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
7 ga('create', 'UA-5544833-12', 'devdocs.io');7 })(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
8 page.track(function() {8 ga('create', 'UA-5544833-12', 'devdocs.io');
9 ga('send', 'pageview', {9 page.track(function() {
10 page: location.pathname + location.search + location.hash,10 ga('send', 'pageview', {
11 dimension1: app.router.context && app.router.context.doc && app.router.context.doc.slug_without_version11 page: location.pathname + location.search + location.hash,
12 dimension1: app.router.context && app.router.context.doc && app.router.context.doc.slug_without_version
13 });
12 });14 });
13 });
14 15
15 page.track(function() {16 page.track(function() {
16 if (window._gauges)17 if (window._gauges)
17 _gauges.push(['track']);18 _gauges.push(['track']);
18 else19 else
19 (function() {20 (function() {
20 var _gauges=_gauges||[];!function(){var a=document.createElement("script");21 var _gauges=_gauges||[];!function(){var a=document.createElement("script");
21 a.type="text/javascript",a.async=!0,a.id="gauges-tracker",22 a.type="text/javascript",a.async=!0,a.id="gauges-tracker",
22 a.setAttribute("data-site-id","51c15f82613f5d7819000067"),23 a.setAttribute("data-site-id","51c15f82613f5d7819000067"),
23 a.src="https://secure.gaug.es/track.js";var b=document.getElementsByTagName("script")[0];24 a.src="https://secure.gaug.es/track.js";var b=document.getElementsByTagName("script")[0];
24 b.parentNode.insertBefore(a,b)}();25 b.parentNode.insertBefore(a,b)}();
25 })();26 })();
26 });27 });
28 } else {
29 resetAnalytics();
30 }
27 }31 }
28} catch(e) { }32} catch(e) { }
assets/javascripts/vendor/fastclick.js+2−2
@@ -413,7 +413,7 @@ · 412 unmodified lines
413 // when the user next taps anywhere else on the page, new touchstart and touchend events are dispatched413 // when the user next taps anywhere else on the page, new touchstart and touchend events are dispatched
414 // with the same identifier as the touch event that previously triggered the click that triggered the alert.414 // with the same identifier as the touch event that previously triggered the click that triggered the alert.
415 // Sadly, there is an issue on iOS 4 that causes some normal touch events to have the same identifier as an415 // Sadly, there is an issue on iOS 4 that causes some normal touch events to have the same identifier as an
416 // immediately preceeding touch event (issue #52), so this fix is unavailable on that platform.416 // immediately preceding touch event (issue #52), so this fix is unavailable on that platform.
417 // Issue 120: touch.identifier is 0 when Chrome dev tools 'Emulate touch events' is set with an iOS device UA string,417 // Issue 120: touch.identifier is 0 when Chrome dev tools 'Emulate touch events' is set with an iOS device UA string,
418 // which causes all touch events to be ignored. As this block only applies to iOS, and iOS identifiers are always long,418 // which causes all touch events to be ignored. As this block only applies to iOS, and iOS identifiers are always long,
419 // random integers, it's safe to to continue if the identifier is 0 here.419 // random integers, it's safe to to continue if the identifier is 0 here.
@@ -805,7 +805,7 @@ · 385 unmodified lines
805 }805 }
806 }806 }
807 807
808 // IE11: prefixed -ms-touch-action is no longer supported and it's recomended to use non-prefixed version808 // IE11: prefixed -ms-touch-action is no longer supported and it's recommended to use non-prefixed version
809 // http://msdn.microsoft.com/en-us/library/windows/apps/Hh767313.aspx809 // http://msdn.microsoft.com/en-us/library/windows/apps/Hh767313.aspx
810 if (layer.style.touchAction === 'none' || layer.style.touchAction === 'manipulation') {810 if (layer.style.touchAction === 'none' || layer.style.touchAction === 'manipulation') {
811 return true;811 return true;
assets/javascripts/vendor/raven.js+1−1
@@ -856,7 +856,7 @@ Raven.prototype = { · 855 unmodified lines
856 },856 },
857 857
858 _triggerEvent: function(eventType, options) {858 _triggerEvent: function(eventType, options) {
859 // NOTE: `event` is a native browser thing, so let's avoid conflicting wiht it859 // NOTE: `event` is a native browser thing, so let's avoid conflicting with it
860 var evt, key;860 var evt, key;
861 861
862 if (!this._hasDocument) return;862 if (!this._hasDocument) return;
assets/javascripts/views/content/content.coffee+3−0
@@ -153,6 +153,9 @@ class app.views.Content extends app.View · 152 unmodified lines
153 return153 return
154 154
155 afterRoute: (route, context) =>155 afterRoute: (route, context) =>
156 if route != 'entry' and route != 'type'
157 resetFavicon()
158
156 switch route159 switch route
157 when 'root'160 when 'root'
158 @show @rootPage161 @show @rootPage
assets/javascripts/views/content/entry_page.coffee+2−1
@@ -40,6 +40,7 @@ class app.views.EntryPage extends app.View · 39 unmodified lines
40 if app.disabledDocs.findBy 'slug', @entry.doc.slug40 if app.disabledDocs.findBy 'slug', @entry.doc.slug
41 @hiddenView = new app.views.HiddenPage @el, @entry41 @hiddenView = new app.views.HiddenPage @el, @entry
42 42
43 setFaviconForDoc(@entry.doc)
43 @delay @polyfillMathML44 @delay @polyfillMathML
44 @trigger 'loaded'45 @trigger 'loaded'
45 return46 return
@@ -123,7 +124,7 @@ class app.views.EntryPage extends app.View · 77 unmodified lines
123 @render @tmpl('pageLoadError')124 @render @tmpl('pageLoadError')
124 @resetClass()125 @resetClass()
125 @addClass @constructor.errorClass126 @addClass @constructor.errorClass
126 app.appCache?.update()127 app.serviceWorker?.update()
127 return128 return
128 129
129 cache: ->130 cache: ->
assets/javascripts/views/content/settings_page.coffee+10−11
@@ -1,7 +1,4 @@
1class app.views.SettingsPage extends app.View1class app.views.SettingsPage extends app.View
2 LAYOUTS = ['_max-width', '_sidebar-hidden', '_native-scrollbars']
3 SIDEBAR_HIDDEN_LAYOUT = '_sidebar-hidden'
4
5 @className: '_static'2 @className: '_static'
6 3
7 @events:4 @events:
@@ -17,31 +14,31 @@ class app.views.SettingsPage extends app.View · 9 unmodified lines
17 settings.dark = app.settings.get('dark')14 settings.dark = app.settings.get('dark')
18 settings.smoothScroll = !app.settings.get('fastScroll')15 settings.smoothScroll = !app.settings.get('fastScroll')
19 settings.arrowScroll = app.settings.get('arrowScroll')16 settings.arrowScroll = app.settings.get('arrowScroll')
20 settings[layout] = app.settings.hasLayout(layout) for layout in LAYOUTS17 settings.autoInstall = app.settings.get('autoInstall')
18 settings.analyticsConsent = app.settings.get('analyticsConsent')
19 settings[layout] = app.settings.hasLayout(layout) for layout in app.settings.LAYOUTS
21 settings20 settings
22 21
23 getTitle: ->22 getTitle: ->
24 'Preferences'23 'Preferences'
25 24
26 toggleDark: (enable) ->25 toggleDark: (enable) ->
27 html = document.documentElement
28 html.classList.toggle('_theme-default')
29 html.classList.toggle('_theme-dark')
30 app.settings.set('dark', !!enable)26 app.settings.set('dark', !!enable)
31 app.appCache?.updateInBackground()
32 return27 return
33 28
34 toggleLayout: (layout, enable) ->29 toggleLayout: (layout, enable) ->
35 document.body.classList[if enable then 'add' else 'remove'](layout) unless layout is SIDEBAR_HIDDEN_LAYOUT
36 document.body.classList[if $.overlayScrollbarsEnabled() then 'add' else 'remove']('_overlay-scrollbars')
37 app.settings.setLayout(layout, enable)30 app.settings.setLayout(layout, enable)
38 app.appCache?.updateInBackground()
39 return31 return
40 32
41 toggleSmoothScroll: (enable) ->33 toggleSmoothScroll: (enable) ->
42 app.settings.set('fastScroll', !enable)34 app.settings.set('fastScroll', !enable)
43 return35 return
44 36
37 toggleAnalyticsConsent: (enable) ->
38 app.settings.set('analyticsConsent', if enable then '1' else '0')
39 resetAnalytics() unless enable
40 return
41
45 toggle: (name, enable) ->42 toggle: (name, enable) ->
46 app.settings.set(name, enable)43 app.settings.set(name, enable)
47 return44 return
@@ -85,6 +82,8 @@ class app.views.SettingsPage extends app.View · 37 unmodified lines
85 @toggleSmoothScroll input.checked82 @toggleSmoothScroll input.checked
86 when 'import'83 when 'import'
87 @import input.files[0], input84 @import input.files[0], input
85 when 'analyticsConsent'
86 @toggleAnalyticsConsent input.checked
88 else87 else
89 @toggle input.name, input.checked88 @toggle input.name, input.checked
90 return89 return
assets/javascripts/views/content/type_page.coffee+1−0
@@ -9,6 +9,7 @@ class app.views.TypePage extends app.View · 8 unmodified lines
9 9
10 render: (@type) ->10 render: (@type) ->
11 @html @tmpl('typePage', @type)11 @html @tmpl('typePage', @type)
12 setFaviconForDoc(@type.doc)
12 return13 return
13 14
14 getTitle: ->15 getTitle: ->
assets/javascripts/views/layout/document.coffee+7−5
@@ -75,9 +75,11 @@ class app.views.Document extends app.View · 74 unmodified lines
75 return unless target.hasAttribute('data-behavior')75 return unless target.hasAttribute('data-behavior')
76 $.stopEvent(event)76 $.stopEvent(event)
77 switch target.getAttribute('data-behavior')77 switch target.getAttribute('data-behavior')
78 when 'back' then history.back()78 when 'back' then history.back()
79 when 'reload' then window.location.reload()79 when 'reload' then window.location.reload()
80 when 'reboot' then app.reboot()80 when 'reboot' then app.reboot()
81 when 'hard-reload' then app.reload()81 when 'hard-reload' then app.reload()
82 when 'reset' then app.reset() if confirm('Are you sure you want to reset DevDocs?')82 when 'reset' then app.reset() if confirm('Are you sure you want to reset DevDocs?')
83 when 'accept-analytics' then Cookies.set('analyticsConsent', '1', expires: 1e8) && app.reboot()
84 when 'decline-analytics' then Cookies.set('analyticsConsent', '0', expires: 1e8) && app.reboot()
83 return85 return
assets/javascripts/views/layout/resizer.coffee+2−9
@@ -11,9 +11,6 @@ class app.views.Resizer extends app.View · 10 unmodified lines
11 init: ->11 init: ->
12 @el.setAttribute('draggable', 'true')12 @el.setAttribute('draggable', 'true')
13 @appendTo $('._app')13 @appendTo $('._app')
14
15 @style = $('style[data-resizer]')
16 @size = @style.getAttribute('data-size')
17 return14 return
18 15
19 MIN = 26016 MIN = 260
@@ -24,15 +21,11 @@ class app.views.Resizer extends app.View · 4 unmodified lines
24 return unless value > 021 return unless value > 0
25 value = Math.min(Math.max(Math.round(value), MIN), MAX)22 value = Math.min(Math.max(Math.round(value), MIN), MAX)
26 newSize = "#{value}px"23 newSize = "#{value}px"
27 @style.innerHTML = @style.innerHTML.replace(new RegExp(@size, 'g'), newSize)24 document.documentElement.style.setProperty('--sidebarWidth', newSize)
28 @size = newSize25 app.settings.setSize(value) if save
29 if save
30 app.settings.setSize(value)
31 app.appCache?.updateInBackground()
32 return26 return
33 27
34 onDragStart: (event) =>28 onDragStart: (event) =>
35 @style.removeAttribute('disabled')
36 event.dataTransfer.effectAllowed = 'link'29 event.dataTransfer.effectAllowed = 'link'
37 event.dataTransfer.setData('Text', '')30 event.dataTransfer.setData('Text', '')
38 $.on(window, 'dragover', @onDrag)31 $.on(window, 'dragover', @onDrag)
assets/javascripts/views/layout/settings.coffee+1−9
@@ -25,7 +25,6 @@ class app.views.Settings extends app.View · 24 unmodified lines
25 if super25 if super
26 @render()26 @render()
27 document.body.classList.remove(SIDEBAR_HIDDEN_LAYOUT)27 document.body.classList.remove(SIDEBAR_HIDDEN_LAYOUT)
28 app.appCache?.on 'progress', @onAppCacheProgress
29 return28 return
30 29
31 deactivate: ->30 deactivate: ->
@@ -33,7 +32,6 @@ class app.views.Settings extends app.View · 1 unmodified line
33 @resetClass()32 @resetClass()
34 @docPicker.detach()33 @docPicker.detach()
35 document.body.classList.add(SIDEBAR_HIDDEN_LAYOUT) if app.settings.hasLayout(SIDEBAR_HIDDEN_LAYOUT)34 document.body.classList.add(SIDEBAR_HIDDEN_LAYOUT) if app.settings.hasLayout(SIDEBAR_HIDDEN_LAYOUT)
36 app.appCache?.off 'progress', @onAppCacheProgress
37 return35 return
38 36
39 render: ->37 render: ->
@@ -52,7 +50,7 @@ class app.views.Settings extends app.View · 12 unmodified lines
52 docs = @docPicker.getSelectedDocs()50 docs = @docPicker.getSelectedDocs()
53 app.settings.setDocs(docs)51 app.settings.setDocs(docs)
54 52
55 @saveBtn.textContent = if app.appCache then 'Downloading\u2026' else 'Saving\u2026'53 @saveBtn.textContent = 'Saving\u2026'
56 disabledDocs = new app.collections.Docs(doc for doc in app.docs.all() when docs.indexOf(doc.slug) is -1)54 disabledDocs = new app.collections.Docs(doc for doc in app.docs.all() when docs.indexOf(doc.slug) is -1)
57 disabledDocs.uninstall ->55 disabledDocs.uninstall ->
58 app.db.migrate()56 app.db.migrate()
@@ -83,9 +81,3 @@ class app.views.Settings extends app.View · 24 unmodified lines
83 $.stopEvent(event)81 $.stopEvent(event)
84 app.router.show '/'82 app.router.show '/'
85 return83 return
86
87 onAppCacheProgress: (event) =>
88 if event.lengthComputable
89 percentage = Math.round event.loaded * 100 / event.total
90 @saveBtn.textContent = "Downloading\u2026 (#{percentage}%)"
91 return
assets/javascripts/views/search/search.coffee+8−0
@@ -30,6 +30,9 @@ class app.views.Search extends app.View · 29 unmodified lines
30 .on 'results', @onResults30 .on 'results', @onResults
31 .on 'end', @onEnd31 .on 'end', @onEnd
32 32
33 @scope
34 .on 'change', @onScopeChange
35
33 app.on 'ready', @onReady36 app.on 'ready', @onReady
34 $.on window, 'hashchange', @searchUrl37 $.on window, 'hashchange', @searchUrl
35 $.on window, 'focus', @onWindowFocus38 $.on window, 'focus', @onWindowFocus
@@ -138,6 +141,11 @@ class app.views.Search extends app.View · 102 unmodified lines
138 $.stopEvent(event)141 $.stopEvent(event)
139 return142 return
140 143
144 onScopeChange: =>
145 @value = ''
146 @onInput()
147 return
148
141 afterRoute: (name, context) =>149 afterRoute: (name, context) =>
142 return if app.shortcuts.eventInProgress?.name is 'escape'150 return if app.shortcuts.eventInProgress?.name is 'escape'
143 @reset(true) if not context.init and app.router.isIndex()151 @reset(true) if not context.init and app.router.isIndex()
assets/javascripts/views/search/search_scope.coffee+23−5
@@ -6,7 +6,9 @@ class app.views.SearchScope extends app.View · 5 unmodified lines
6 tag: '._search-tag'6 tag: '._search-tag'
7 7
8 @events:8 @events:
9 click: 'onClick'
9 keydown: 'onKeydown'10 keydown: 'onKeydown'
11 textInput: 'onTextInput'
10 12
11 @routes:13 @routes:
12 after: 'afterRoute'14 after: 'afterRoute'
@@ -87,17 +89,33 @@ class app.views.SearchScope extends app.View · 74 unmodified lines
87 @trigger 'change', null, previousDoc89 @trigger 'change', null, previousDoc
88 return90 return
89 91
92 doScopeSearch: (event) =>
93 @search @input.value[0...@input.selectionStart]
94 $.stopEvent(event) if @doc
95 return
96
97 onClick: (event) =>
98 if event.target is @tag
99 @reset()
100 $.stopEvent(event)
101 return
102
90 onKeydown: (event) =>103 onKeydown: (event) =>
91 if event.which is 8 # backspace104 if event.which is 8 # backspace
92 if @doc and not @input.value105 if @doc and @input.selectionEnd is 0
93 $.stopEvent(event)
94 @reset()106 @reset()
95 else if not @doc and @input.value107 $.stopEvent(event)
108 else if not @doc and @input.value and not $.isChromeForAndroid()
96 return if event.ctrlKey or event.metaKey or event.altKey or event.shiftKey109 return if event.ctrlKey or event.metaKey or event.altKey or event.shiftKey
97 if event.which is 9 or # tab110 if event.which is 9 or # tab
98 (event.which is 32 and app.isMobile()) # space111 (event.which is 32 and app.isMobile()) # space
99 @search @input.value[0...@input.selectionStart]112 @doScopeSearch(event)
100 $.stopEvent(event) if @doc113 return
114
115 onTextInput: (event) =>
116 return unless $.isChromeForAndroid()
117 if not @doc and @input.value and event.data == ' '
118 @doScopeSearch(event)
101 return119 return
102 120
103 extractHashValue: ->121 extractHashValue: ->
assets/javascripts/views/sidebar/sidebar.coffee+1−1
@@ -28,7 +28,7 @@ class app.views.Sidebar extends app.View · 27 unmodified lines
28 28
29 app.on 'ready', @onReady29 app.on 'ready', @onReady
30 30
31 $.on document.documentElement, 'mouseleave', (event) => @display() if event.clientX < 1031 $.on document.documentElement, 'mouseleave', (event) => @display() unless event.clientX <= 0
32 $.on document.documentElement, 'mouseenter', => @resetDisplay(forceNoHover: false)32 $.on document.documentElement, 'mouseenter', => @resetDisplay(forceNoHover: false)
33 return33 return
34 34