History

Merge branch 'master' into cobol

Jasper van Merle committed 7 years ago

Commit 3fb03102 parents 14aa61a b34dbb2352 files · +4303 −897Browse files

Showing the changes to assets/javascripts (32 of 352 files). Show all

Unified · Split

32 files
  1. assets/javascripts/app/app.coffee +15 −10
  2. assets/javascripts/app/appcache.coffee +0 −46
  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/license.coffee +1 −1
  10. assets/javascripts/lib/page.coffee +17 −1
  11. assets/javascripts/lib/util.coffee +4 −0
  12. assets/javascripts/models/doc.coffee +3 −1
  13. assets/javascripts/news.json +8 −0
  14. assets/javascripts/templates/error_tmpl.coffee +8 −8
  15. assets/javascripts/templates/notif_tmpl.coffee +6 −0
  16. assets/javascripts/templates/pages/about_tmpl.coffee +54 −24
  17. assets/javascripts/templates/pages/offline_tmpl.coffee +9 −4
  18. assets/javascripts/templates/pages/root_tmpl.coffee.erb +3 −3
  19. assets/javascripts/templates/pages/settings_tmpl.coffee +8 −0
  20. assets/javascripts/tracking.js +27 −23
  21. assets/javascripts/vendor/fastclick.js +2 −2
  22. assets/javascripts/vendor/raven.js +1 −1
  23. assets/javascripts/views/content/content.coffee +3 −0
  24. assets/javascripts/views/content/entry_page.coffee +2 −1
  25. assets/javascripts/views/content/settings_page.coffee +10 −11
  26. assets/javascripts/views/content/type_page.coffee +1 −0
  27. assets/javascripts/views/layout/document.coffee +7 −5
  28. assets/javascripts/views/layout/resizer.coffee +2 −9
  29. assets/javascripts/views/layout/settings.coffee +1 −9
  30. assets/javascripts/views/search/search.coffee +8 −0
  31. assets/javascripts/views/search/search_scope.coffee +23 −5
  32. assets/javascripts/views/sidebar/sidebar.coffee +1 −1
assets/javascripts/app/app.coffee+15−10
@@ -13,10 +13,12 @@ · 12 unmodified lines
1313
1414 @el = $('._app')
1515 @localStorage = new LocalStorageStore
16− @appCache = new app.AppCache if app.AppCache.isEnabled()
16+ @serviceWorker = new app.ServiceWorker if app.ServiceWorker.isEnabled()
1717 @settings = new app.Settings
1818 @db = new app.DB()
1919
20+ @settings.initLayout()
21+
2022 @docs = new app.collections.Docs
2123 @disabledDocs = new app.collections.Docs
2224 @entries = new app.collections.Entries
@@ -76,7 +78,7 @@ · 53 unmodified lines
7678 .install()
7779 @previousErrorHandler = onerror
7880 window.onerror = @onWindowError.bind(@)
79− CookieStore.onBlocked = @onCookieBlocked
81+ CookiesStore.onBlocked = @onCookieBlocked
8082 return
8183
8284 bootOne: ->
@@ -138,7 +140,10 @@ · 55 unmodified lines
138140 @docs.sort()
139141 @initDoc(doc)
140142 @saveDocs()
141− _onSuccess()
143+ if app.settings.get('autoInstall')
144+ doc.install(_onSuccess, onError)
145+ else
146+ _onSuccess()
142147 return
143148
144149 doc.load onSuccess, onError, writeCache: true
@@ -147,7 +152,7 @@ · 2 unmodified lines
147152 saveDocs: ->
148153 @settings.setDocs(doc.slug for doc in @docs.all())
149154 @db.migrate()
150− @appCache?.updateInBackground()
155+ @serviceWorker?.updateInBackground()
151156
152157 welcomeBack: ->
153158 visitCount = @settings.get('count')
@@ -158,7 +163,7 @@ · 4 unmodified lines
158163 @updateChecker = new app.UpdateChecker()
159164
160165 reboot: ->
161− if location.pathname isnt '/'
166+ if location.pathname isnt '/' and location.pathname isnt '/settings'
162167 window.location = "/##{location.pathname}"
163168 else
164169 window.location = '/'
@@ -167,14 +172,14 @@ · 2 unmodified lines
167172 reload: ->
168173 @docs.clearCache()
169174 @disabledDocs.clearCache()
170− if @appCache then @appCache.reload() else @reboot()
175+ if @serviceWorker then @serviceWorker.reload() else @reboot()
171176 return
172177
173178 reset: ->
174179 @localStorage.reset()
175180 @settings.reset()
176181 @db?.reset()
177− @appCache?.update()
182+ @serviceWorker?.update()
178183 window.location = '/'
179184 return
180185
@@ -193,9 +198,9 @@ · 12 unmodified lines
193198 return
194199
195200 indexHost: ->
196− # Can't load the index files from the host/CDN when applicationCache is
201+ # Can't load the index files from the host/CDN when service worker is
197202 # 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']
199204
200205 onBootError: (args...) ->
201206 @trigger 'bootError'
@@ -252,7 +257,7 @@ · 50 unmodified lines
252257 matchMedia: !!window.matchMedia
253258 insertAdjacentHTML: !!document.body.insertAdjacentHTML
254259 defaultPrevented: document.createEvent('CustomEvent').defaultPrevented is false
255− cssVariables: CSS.supports and CSS.supports('(--t: 0)')
260+ cssVariables: !!CSS?.supports?('(--t: 0)')
256261
257262 for key, value of features when not value
258263 Raven.captureMessage "unsupported/#{key}", level: 'info'
assets/javascripts/app/appcache.coffee+0−46
@@ -1,46 +0,0 @@
1−class 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', ->
31− if location.pathname isnt '/'
32− window.location = "/##{location.pathname}"
33− else
34− window.location = '/'
35− @notifyUpdate = false
36− @notifyProgress = true
37− try @cache.update() catch
38− return
39−
40− onProgress: (event) =>
41− @trigger 'progress', event if @notifyProgress
42− return
43−
44− onUpdateReady: =>
45− @trigger 'updateready' if @notifyUpdate
46− return
assets/javascripts/app/config.coffee.erb+3−0
@@ -13,3 +13,6 @@ app.config = · 12 unmodified lines
1313 version: <%= Time.now.to_i %>
1414 release: <%= Time.now.utc.httpdate.to_json %>
1515 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 @@
1+class 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
55 'manualUpdate'
66 'fastScroll'
77 'arrowScroll'
8+ 'analyticsConsent'
89 'docs'
910 'dark'
1011 'layout'
1112 'size'
1213 'tips'
14+ 'autoInstall'
1315 ]
1416
1517 INTERNAL_KEYS = [
@@ -19,6 +21,8 @@ class app.Settings · 3 unmodified lines
1921 'news'
2022 ]
2123
24+ LAYOUTS: ['_max-width', '_sidebar-hidden', '_native-scrollbars']
25+
2226 @defaults:
2327 count: 0
2428 hideDisabled: false
@@ -26,9 +30,10 @@ class app.Settings · 1 unmodified line
2630 news: 0
2731 manualUpdate: false
2832 schema: 1
33+ analyticsConsent: false
2934
3035 constructor: ->
31− @store = new CookieStore
36+ @store = new CookiesStore
3237 @cache = {}
3338
3439 get: (key) ->
@@ -38,6 +43,7 @@ class app.Settings · 3 unmodified lines
3843 set: (key, value) ->
3944 @store.set(key, value)
4045 delete @cache[key]
46+ @toggleDark(value) if key == 'dark'
4147 return
4248
4349 del: (key) ->
@@ -63,6 +69,8 @@ class app.Settings · 19 unmodified lines
6369 return
6470
6571 setLayout: (name, enable) ->
72+ @toggleLayout(name, enable)
73+
6674 layout = (@store.get('layout') || '').split(' ')
6775 $.arrayDelete(layout, '')
6876
@@ -104,3 +112,28 @@ class app.Settings · 35 unmodified lines
104112 @store.reset()
105113 @cache = {}
106114 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
33 @lastCheck = Date.now()
44
55 $.on window, 'focus', @onFocus
6− app.appCache.on 'updateready', @onUpdateReady if app.appCache
6+ app.serviceWorker?.on 'updateready', @onUpdateReady
77
88 setTimeout @checkDocs, 0
99
1010 check: ->
11− if app.appCache
12− app.appCache.update()
11+ if app.serviceWorker
12+ app.serviceWorker.update()
1313 else
1414 ajax
1515 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 @@
1−class @CookieStore
1+class @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+
26 INT = /^\d+$/
37
48 @onBlocked: ->
assets/javascripts/lib/favicon.coffee+64−0
@@ -0,0 +1,64 @@
1+defaultUrl = null
2+currentSlug = null
3+
4+imageCache = {}
5+urlCache = {}
6+
7+withImage = (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/license.coffee+1−1
@@ -1,5 +1,5 @@
11 ###
2− * Copyright 2013-2018 Thibaut Courouble and other contributors
2+ * Copyright 2013-2019 Thibaut Courouble and other contributors
33 *
44 * This source code is licensed under the terms of the Mozilla
55 * Public License, v. 2.0, a copy of which may be obtained at:
assets/javascripts/lib/page.coffee+17−1
@@ -199,5 +199,21 @@ page.track = (fn) -> · 198 unmodified lines
199199 return
200200
201201 track = ->
202− tracker.call() for tracker in trackers
202+ 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)
203219 return
assets/javascripts/lib/util.coffee+4−0
@@ -352,6 +352,10 @@ isIE = null · 351 unmodified lines
352352 $.isIE = ->
353353 isIE ?= navigator.userAgent?.indexOf('MSIE') >= 0 || navigator.userAgent?.indexOf('rv:11.0') >= 0
354354
355+isChromeForAndroid = null
356+$.isChromeForAndroid = ->
357+ isChromeForAndroid ?= navigator.userAgent?.indexOf('Android') >= 0 && /Chrome\/([.0-9])+ Mobile/.test(navigator.userAgent)
358+
355359 isAndroid = null
356360 $.isAndroid = ->
357361 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
142142 return
143143
144144 isOutdated: (status) ->
145− status and status.installed and @mtime isnt status.mtime
145+ 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 @@
11 [
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+ ],
210 [
311 "2018-09-23",
412 "New documentations: <a href=\"/puppeteer/\">Puppeteer</a> and <a href=\"/handlebars/\">Handlebars.js</a>"
assets/javascripts/templates/error_tmpl.coffee+8−8
@@ -12,8 +12,8 @@ app.templates.notFoundPage = -> · 11 unmodified lines
1212
1313 app.templates.pageLoadError = ->
1414 error """ The page failed to load. """,
15− """ It may be missing from the server (try reloading the app) or you could be offline.<br>
16− If you keep seeing this, you're likely behind a proxy or firewall that blocks cross-domain requests. """,
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're online and you keep seeing this, you're likely behind a proxy or firewall that blocks cross-domain requests. """,
1717 """ #{back} &middot; <a href="/##{location.pathname}" target="_top" class="_error-link">Reload</a>
1818 &middot; <a href="#" class="_error-link" data-retry>Retry</a> """
1919
@@ -57,17 +57,17 @@ app.templates.unsupportedBrowser = """ · 37 unmodified lines
5757 <p class="_fail-text">DevDocs is an API documentation browser which supports the following browsers:
5858 <ul class="_fail-list">
5959 <li>Recent versions of Firefox, Chrome, or Opera
60− <li>Safari 9.1+
61− <li>Edge 16+
62− <li>iOS 10+
60+ <li>Safari 11.1+
61+ <li>Edge 17+
62+ <li>iOS 11.3+
6363 </ul>
6464 <p class="_fail-text">
65− If you're unable to upgrade, I apologize.
66− I decided to prioritize speed and new features over support for older browsers.
65+ If you're unable to upgrade, we apologize.
66+ We decided to prioritize speed and new features over support for older browsers.
6767 <p class="_fail-text">
6868 Note: if you're already using one of the browsers above, check your settings and add-ons.
6969 The app uses feature detection, not user agent sniffing.
7070 <p class="_fail-text">
71− &mdash; Thibaut <a href="https://twitter.com/DevDocs" class="_fail-link">@DevDocs</a>
71+ &mdash; <a href="https://twitter.com/DevDocs">@DevDocs</a>
7272 </div>
7373 """
assets/javascripts/templates/notif_tmpl.coffee+6−0
@@ -68,3 +68,9 @@ app.templates.notifShare = -> · 67 unmodified lines
6868 app.templates.notifUpdateDocs = ->
6969 textNotif """ Documentation updates available. """,
7070 """ <a href="/offline">Install them</a> as soon as possible to avoid broken pages. """
71+
72+app.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+54−24
@@ -11,22 +11,18 @@ app.templates.aboutPage = -> """ · 10 unmodified lines
1111 </nav>
1212
1313 <h1 class="_lined-heading">DevDocs: API Documentation Browser</h1>
14− <p>DevDocs combines multiple API documentations in a clean and organized web UI with instant search, offline support, mobile version, dark theme, keyboard shortcuts, and more.
15− <ul>
16− <li>Created and maintained by <a href="https://thibaut.me">Thibaut Courouble</a>
17− <li>Free and <a href="https://github.com/freeCodeCamp/devdocs">open source</a>
18− <iframe class="_github-btn" src="https://ghbtns.com/github-btn.html?user=freeCodeCamp&repo=devdocs&type=watch&count=true" allowtransparency="true" frameborder="0" scrolling="0" width="100" height="20" tabindex="-1"></iframe>
19− </ul>
14+ <p>DevDocs combines multiple developer documentations in a clean and organized web UI with instant search, offline support, mobile version, dark theme, keyboard shortcuts, and more.
15+ <p>DevDocs is free and <a href="https://github.com/freeCodeCamp/devdocs">open source</a>. It was created by <a href="https://thibaut.me">Thibaut Courouble</a> and is operated by <a href="https://www.freecodecamp.org/">freeCodeCamp</a>.
2016 <p>To keep up-to-date with the latest news:
2117 <ul>
2218 <li>Follow <a href="https://twitter.com/DevDocs">@DevDocs</a> on Twitter
23− <li>Watch the repository on <a href="https://github.com/freeCodeCamp/devdocs/subscription">GitHub</a>
24− <li>Join the <a href="https://groups.google.com/d/forum/devdocs">mailing list</a>
19+ <li>Watch the repository on <a href="https://github.com/freeCodeCamp/devdocs/subscription">GitHub</a> <iframe class="_github-btn" src="https://ghbtns.com/github-btn.html?user=freeCodeCamp&repo=devdocs&type=watch&count=true" allowtransparency="true" frameborder="0" scrolling="0" width="100" height="20" tabindex="-1"></iframe>
20+ <li>Join the <a href="https://gitter.im/FreeCodeCamp/DevDocs">Gitter</a> chat room
2521 </ul>
2622
2723 <h2 class="_block-heading" id="copyright">Copyright and License</h2>
2824 <p class="_note">
29− <strong>Copyright 2013&ndash;2018 Thibaut Courouble and <a href="https://github.com/freeCodeCamp/devdocs/graphs/contributors">other contributors</a></strong><br>
25+ <strong>Copyright 2013&ndash;2019 Thibaut Courouble and <a href="https://github.com/freeCodeCamp/devdocs/graphs/contributors">other contributors</a></strong><br>
3026 This software is licensed under the terms of the Mozilla Public License v2.0.<br>
3127 You may obtain a copy of the source code at <a href="https://github.com/freeCodeCamp/devdocs">github.com/freeCodeCamp/devdocs</a>.<br>
3228 For more information, see the <a href="https://github.com/freeCodeCamp/devdocs/blob/master/COPYRIGHT">COPYRIGHT</a>
@@ -48,11 +44,10 @@ app.templates.aboutPage = -> """ · 15 unmodified lines
4844 <dt>Where can I suggest new docs and features?
4945 <dd>You can suggest and vote for new docs on the <a href="https://trello.com/b/6BmTulfx/devdocs-documentation">Trello board</a>.<br>
5046 If you have a specific feature request, add it to the <a href="https://github.com/freeCodeCamp/devdocs/issues">issue tracker</a>.<br>
51− Otherwise use the <a href="https://groups.google.com/d/forum/devdocs">mailing list</a>.
47+ Otherwise, come talk to us in the <a href="https://gitter.im/FreeCodeCamp/DevDocs">Gitter</a> chat room.
5248 <dt>Where can I report bugs?
5349 <dd>In the <a href="https://github.com/freeCodeCamp/devdocs/issues">issue tracker</a>. Thanks!
5450 </dl>
55− <p>For anything else, feel free to email me at <a href="mailto:thibaut@devdocs.io">thibaut@devdocs.io</a>.
5651
5752 <h2 class="_block-heading" id="credits">Credits</h2>
5853
@@ -76,12 +71,13 @@ app.templates.aboutPage = -> """ · 17 unmodified lines
7671
7772 <h2 class="_block-heading" id="privacy">Privacy Policy</h2>
7873 <ul>
79− <li><a href="https://devdocs.io">devdocs.io</a> ("App") is operated by Thibaut Courouble ("We").
80− <li>We do not collect personal information.
81− <li>We use Google Analytics, Gauges and Sentry to collect anonymous traffic information and improve the app.
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.
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.
8278 <li>The app uses cookies to store user preferences.
8379 <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.
84− <li>If you have any questions regarding privacy, please email <a href="mailto:thibaut@devdocs.io">thibaut@devdocs.io</a>.
80+ <li>If you have any questions regarding privacy, please email <a href="mailto:privacy@freecodecamp.org">privacy@freecodecamp.org</a>.
8581 </ul>
8682 """
8783
@@ -92,7 +88,7 @@ credits = [ · 4 unmodified lines
9288 'https://creativecommons.org/licenses/by/4.0/'
9389 ], [
9490 'Ansible',
95− '2012-2018 Michael DeHaan<br>&copy; 2018 Red Hat, Inc.',
91+ '2012-2018 Michael DeHaan<br>&copy; 2018–2019 Red Hat, Inc.',
9692 'GPLv3',
9793 'https://raw.githubusercontent.com/ansible/ansible/devel/COPYING'
9894 ], [
@@ -127,7 +123,7 @@ credits = [ · 28 unmodified lines
127123 'https://raw.githubusercontent.com/petkaantonov/bluebird/master/LICENSE'
128124 ], [
129125 'Bootstrap',
130− '2011-2018 Twitter, Inc.<br>2011-2018 The Bootstrap Authors',
126+ '2011-2019 Twitter, Inc.<br>2011-2019 The Bootstrap Authors',
131127 'CC BY',
132128 'https://creativecommons.org/licenses/by/3.0/'
133129 ], [
@@ -167,7 +163,7 @@ credits = [ · 33 unmodified lines
167163 'https://github.com/clojure/clojure/blob/master/epl-v10.html'
168164 ], [
169165 'CMake',
170− '2000-2018 Kitware, Inc. and Contributors',
166+ '2000-2019 Kitware, Inc. and Contributors',
171167 'BSD',
172168 'https://cmake.org/licensing/'
173169 ], [
@@ -190,6 +186,11 @@ credits = [ · 16 unmodified lines
190186 '2009-2018 Jeremy Ashkenas',
191187 'MIT',
192188 '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'
193194 ], [
194195 'Cordova',
195196 '2012-2018 The Apache Software Foundation',
@@ -206,6 +207,12 @@ credits = [ · 10 unmodified lines
206207 'Apache',
207208 'https://raw.githubusercontent.com/crystal-lang/crystal/master/LICENSE'
208209 ], [
210+ 'Cypress',
211+ '2017 Cypress.io',
212+ 'MIT',
213+ 'https://raw.githubusercontent.com/cypress-io/cypress-documentation/develop/LICENSE.md'
214+ ],
215+ [
209216 'D',
210217 '1999-2018 The D Language Foundation',
211218 'Boost',
@@ -227,7 +234,7 @@ credits = [ · 15 unmodified lines
227234 'https://raw.githubusercontent.com/django/django/master/LICENSE'
228235 ], [
229236 'Docker',
230− '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.',
231238 'Apache',
232239 'https://raw.githubusercontent.com/docker/docker.github.io/master/LICENSE'
233240 ], [
@@ -397,7 +404,7 @@ credits = [ · 163 unmodified lines
397404 'https://raw.githubusercontent.com/koajs/koa/master/LICENSE'
398405 ], [
399406 'Kotlin',
400− '2010-2018 JetBrains s.r.o.',
407+ '2010-2019 JetBrains s.r.o.',
401408 'Apache',
402409 'https://raw.githubusercontent.com/JetBrains/kotlin/master/license/LICENSE.txt'
403410 ], [
@@ -407,7 +414,7 @@ credits = [ · 3 unmodified lines
407414 'https://raw.githubusercontent.com/laravel/framework/master/LICENSE.txt'
408415 ], [
409416 'Leaflet',
410− '2010-2018 Vladimir Agafonkin<br>&copy; 2010-2011, CloudMade<br>Maps &copy; OpenStreetMap contributors.',
417+ '2010-2019 Vladimir Agafonkin<br>&copy; 2010-2011, CloudMade<br>Maps &copy; OpenStreetMap contributors.',
411418 'BSD',
412419 'https://raw.githubusercontent.com/Leaflet/Leaflet/master/LICENSE'
413420 ], [
@@ -482,7 +489,7 @@ credits = [ · 68 unmodified lines
482489 'https://github.com/LearnBoost/mongoose/blob/master/README.md#license'
483490 ], [
484491 'nginx',
485− '2002-2018 Igor Sysoev<br>&copy; 2011-2018 Nginx, Inc.',
492+ '2002-2019 Igor Sysoev<br>&copy; 2011-2019 Nginx, Inc.',
486493 'BSD',
487494 'http://nginx.org/LICENSE'
488495 ], [
@@ -527,7 +534,7 @@ credits = [ · 38 unmodified lines
527534 'https://raw.githubusercontent.com/OpenTSDB/opentsdb.net/gh-pages/COPYING.LESSER'
528535 ], [
529536 'Padrino',
530− '2010-2016 Padrino',
537+ '2010-2019 Padrino',
531538 'MIT',
532539 'https://raw.githubusercontent.com/padrino/padrino-framework/master/padrino/LICENSE.txt'
533540 ], [
@@ -577,7 +584,7 @@ credits = [ · 43 unmodified lines
577584 'https://raw.githubusercontent.com/GoogleChrome/puppeteer/master/LICENSE'
578585 ], [
579586 'Pygame',
580− 'Pygame Developpers',
587+ 'Pygame Developers',
581588 'LGPLv2.1',
582589 'https://raw.githubusercontent.com/pygame/pygame/master/LICENSE'
583590 ], [
@@ -641,11 +648,23 @@ credits = [ · 57 unmodified lines
641648 'MIT',
642649 'https://raw.githubusercontent.com/rust-lang/rust/master/LICENSE-MIT'
643650 ], [
651+ 'Salt Stack',
652+ '2019 SaltStack',
653+ 'Apache',
654+ 'https://raw.githubusercontent.com/saltstack/salt/develop/LICENSE'
655+ ],
656+ [
644657 'Sass',
645658 '2006-2016 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein',
646659 'MIT',
647660 'https://raw.githubusercontent.com/sass/sass/stable/MIT-LICENSE'
648661 ], [
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+ [
649668 'scikit-image',
650669 '2011 the scikit-image team',
651670 'BSD',
@@ -720,6 +739,11 @@ credits = [ · 68 unmodified lines
720739 '2013-2018 Evan You, Vue.js contributors',
721740 'MIT',
722741 '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'
723747 ], [
724748 'Vulkan',
725749 '2014-2017 Khronos Group Inc.<br>Vulkan and the Vulkan logo are registered trademarks of the Khronos Group Inc.',
@@ -731,6 +755,12 @@ credits = [ · 5 unmodified lines
731755 'CC BY',
732756 'https://creativecommons.org/licenses/by/4.0/'
733757 ], [
758+ 'Wordpress',
759+ '2003-2019 WordPress Foundation',
760+ 'GPLv2+',
761+ 'https://wordpress.org/about/license/'
762+ ],
763+ [
734764 'Yarn',
735765 '2016-present Yarn Contributors',
736766 'BSD',
assets/javascripts/templates/pages/offline_tmpl.coffee+9−4
@@ -25,8 +25,8 @@ app.templates.offlinePage = (docs) -> """ · 24 unmodified lines
2525 <h2 class="_block-heading">Questions & Answers</h2>
2626 <dl>
2727 <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>
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.
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://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.
3030 <dt>Can I close the tab/browser?
3131 <dd>#{canICloseTheTab()}
3232 <dt>What if I don't update a documentation?
@@ -41,10 +41,15 @@ app.templates.offlinePage = (docs) -> """ · 8 unmodified lines
4141 """
4242
4343 canICloseTheTab = ->
44− if app.AppCache.isEnabled()
44+ if app.ServiceWorker.isEnabled()
4545 """ 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). """
4646 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>
4853 The current tab will continue to function even when you go offline (provided you installed all the documentations beforehand). """
4954
5055 app.templates.offlineDoc = (doc, status) ->
assets/javascripts/templates/pages/root_tmpl.coffee.erb+3−3
@@ -8,13 +8,13 @@ app.templates.intro = """ · 7 unmodified lines
88 <p>Thanks for downloading DevDocs. Here are a few things you should know:
99 <ol class="_intro-list">
1010 <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>.
1212 <li>Run <code>thor docs:list</code> to see all available documentations.
1313 <li>Run <code>thor docs:download &lt;name&gt;</code> to download documentations.
1414 <li>Run <code>thor docs:download --installed</code> to update all downloaded documentations.
1515 <li>To be notified about new versions, don't forget to <a href="https://github.com/freeCodeCamp/devdocs/subscription">watch the repository</a> on GitHub.
1616 <li>The <a href="https://github.com/freeCodeCamp/devdocs/issues">issue tracker</a> is the preferred channel for bug reports and
17− feature requests. For everything else, use the <a href="https://groups.google.com/d/forum/devdocs">mailing list</a>.
17+ feature requests. For everything else, use <a href="https://gitter.im/FreeCodeCamp/DevDocs">Gitter</a>.
1818 <li>Contributions are welcome. See the <a href="https://github.com/freeCodeCamp/devdocs/blob/master/CONTRIBUTING.md">guidelines</a>.
1919 <li>DevDocs is licensed under the terms of the Mozilla Public License v2.0. For more information,
2020 see the <a href="https://github.com/freeCodeCamp/devdocs/blob/master/COPYRIGHT">COPYRIGHT</a> and
@@ -39,7 +39,7 @@ app.templates.intro = """ · 18 unmodified lines
3939 <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>.
4040 <li>For the latest news, follow <a href="https://twitter.com/DevDocs">@DevDocs</a>.
4141 <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>
4343 <li>And if you're new to coding, check out <a href="https://www.freecodecamp.org/">freeCodeCamp's open source curriculum</a>.
4444 </ol>
4545 <p>Happy coding!
assets/javascripts/templates/pages/settings_tmpl.coffee+8−0
@@ -15,6 +15,14 @@ app.templates.settingsPage = (settings) -> """ · 14 unmodified lines
1515 <input type="checkbox" form="settings" name="layout" value="_sidebar-hidden"#{if settings['_sidebar-hidden'] then ' checked' else ''}>Automatically hide and show the sidebar
1616 <small>Tip: drag the edge of the sidebar to resize it.</small>
1717 </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>
1826 </div>
1927 </div>
2028
assets/javascripts/tracking.js+27−23
@@ -1,28 +1,32 @@
11 try {
2− if (app.config.env == 'production') {
3− (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
4− (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
5− m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
6− })(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
7− ga('create', 'UA-5544833-12', 'devdocs.io');
8− page.track(function() {
9− ga('send', 'pageview', {
10− page: location.pathname + location.search + location.hash,
11− dimension1: app.router.context && app.router.context.doc && app.router.context.doc.slug_without_version
2+ if (app.config.env === 'production') {
3+ if (Cookies.get('analyticsConsent') === '1') {
4+ (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
5+ (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
6+ m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
7+ })(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
8+ ga('create', 'UA-5544833-12', 'devdocs.io');
9+ page.track(function() {
10+ ga('send', 'pageview', {
11+ page: location.pathname + location.search + location.hash,
12+ dimension1: app.router.context && app.router.context.doc && app.router.context.doc.slug_without_version
13+ });
1214 });
13− });
1415
15− page.track(function() {
16− if (window._gauges)
17− _gauges.push(['track']);
18− else
19− (function() {
20− var _gauges=_gauges||[];!function(){var a=document.createElement("script");
21− a.type="text/javascript",a.async=!0,a.id="gauges-tracker",
22− a.setAttribute("data-site-id","51c15f82613f5d7819000067"),
23− a.src="https://secure.gaug.es/track.js";var b=document.getElementsByTagName("script")[0];
24− b.parentNode.insertBefore(a,b)}();
25− })();
26− });
16+ page.track(function() {
17+ if (window._gauges)
18+ _gauges.push(['track']);
19+ else
20+ (function() {
21+ var _gauges=_gauges||[];!function(){var a=document.createElement("script");
22+ a.type="text/javascript",a.async=!0,a.id="gauges-tracker",
23+ a.setAttribute("data-site-id","51c15f82613f5d7819000067"),
24+ a.src="https://secure.gaug.es/track.js";var b=document.getElementsByTagName("script")[0];
25+ b.parentNode.insertBefore(a,b)}();
26+ })();
27+ });
28+ } else {
29+ resetAnalytics();
30+ }
2731 }
2832 } catch(e) { }
assets/javascripts/vendor/fastclick.js+2−2
@@ -413,7 +413,7 @@ · 412 unmodified lines
413413 // when the user next taps anywhere else on the page, new touchstart and touchend events are dispatched
414414 // with the same identifier as the touch event that previously triggered the click that triggered the alert.
415415 // 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.
417417 // Issue 120: touch.identifier is 0 when Chrome dev tools 'Emulate touch events' is set with an iOS device UA string,
418418 // which causes all touch events to be ignored. As this block only applies to iOS, and iOS identifiers are always long,
419419 // random integers, it's safe to to continue if the identifier is 0 here.
@@ -805,7 +805,7 @@ · 385 unmodified lines
805805 }
806806 }
807807
808− // IE11: prefixed -ms-touch-action is no longer supported and it's recomended to use non-prefixed version
808+ // IE11: prefixed -ms-touch-action is no longer supported and it's recommended to use non-prefixed version
809809 // http://msdn.microsoft.com/en-us/library/windows/apps/Hh767313.aspx
810810 if (layer.style.touchAction === 'none' || layer.style.touchAction === 'manipulation') {
811811 return true;
assets/javascripts/vendor/raven.js+1−1
@@ -856,7 +856,7 @@ Raven.prototype = { · 855 unmodified lines
856856 },
857857
858858 _triggerEvent: function(eventType, options) {
859− // NOTE: `event` is a native browser thing, so let's avoid conflicting wiht it
859+ // NOTE: `event` is a native browser thing, so let's avoid conflicting with it
860860 var evt, key;
861861
862862 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
153153 return
154154
155155 afterRoute: (route, context) =>
156+ if route != 'entry' and route != 'type'
157+ resetFavicon()
158+
156159 switch route
157160 when 'root'
158161 @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
4040 if app.disabledDocs.findBy 'slug', @entry.doc.slug
4141 @hiddenView = new app.views.HiddenPage @el, @entry
4242
43+ setFaviconForDoc(@entry.doc)
4344 @delay @polyfillMathML
4445 @trigger 'loaded'
4546 return
@@ -123,7 +124,7 @@ class app.views.EntryPage extends app.View · 77 unmodified lines
123124 @render @tmpl('pageLoadError')
124125 @resetClass()
125126 @addClass @constructor.errorClass
126− app.appCache?.update()
127+ app.serviceWorker?.update()
127128 return
128129
129130 cache: ->
assets/javascripts/views/content/settings_page.coffee+10−11
@@ -1,7 +1,4 @@
11 class app.views.SettingsPage extends app.View
2− LAYOUTS = ['_max-width', '_sidebar-hidden', '_native-scrollbars']
3− SIDEBAR_HIDDEN_LAYOUT = '_sidebar-hidden'
4−
52 @className: '_static'
63
74 @events:
@@ -17,31 +14,31 @@ class app.views.SettingsPage extends app.View · 9 unmodified lines
1714 settings.dark = app.settings.get('dark')
1815 settings.smoothScroll = !app.settings.get('fastScroll')
1916 settings.arrowScroll = app.settings.get('arrowScroll')
20− settings[layout] = app.settings.hasLayout(layout) for layout in LAYOUTS
17+ 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
2120 settings
2221
2322 getTitle: ->
2423 'Preferences'
2524
2625 toggleDark: (enable) ->
27− html = document.documentElement
28− html.classList.toggle('_theme-default')
29− html.classList.toggle('_theme-dark')
3026 app.settings.set('dark', !!enable)
31− app.appCache?.updateInBackground()
3227 return
3328
3429 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')
3730 app.settings.setLayout(layout, enable)
38− app.appCache?.updateInBackground()
3931 return
4032
4133 toggleSmoothScroll: (enable) ->
4234 app.settings.set('fastScroll', !enable)
4335 return
4436
37+ toggleAnalyticsConsent: (enable) ->
38+ app.settings.set('analyticsConsent', if enable then '1' else '0')
39+ resetAnalytics() unless enable
40+ return
41+
4542 toggle: (name, enable) ->
4643 app.settings.set(name, enable)
4744 return
@@ -85,6 +82,8 @@ class app.views.SettingsPage extends app.View · 37 unmodified lines
8582 @toggleSmoothScroll input.checked
8683 when 'import'
8784 @import input.files[0], input
85+ when 'analyticsConsent'
86+ @toggleAnalyticsConsent input.checked
8887 else
8988 @toggle input.name, input.checked
9089 return
assets/javascripts/views/content/type_page.coffee+1−0
@@ -9,6 +9,7 @@ class app.views.TypePage extends app.View · 8 unmodified lines
99
1010 render: (@type) ->
1111 @html @tmpl('typePage', @type)
12+ setFaviconForDoc(@type.doc)
1213 return
1314
1415 getTitle: ->
assets/javascripts/views/layout/document.coffee+7−5
@@ -75,9 +75,11 @@ class app.views.Document extends app.View · 74 unmodified lines
7575 return unless target.hasAttribute('data-behavior')
7676 $.stopEvent(event)
7777 switch target.getAttribute('data-behavior')
78− when 'back' then history.back()
79− when 'reload' then window.location.reload()
80− when 'reboot' then app.reboot()
81− when 'hard-reload' then app.reload()
82− when 'reset' then app.reset() if confirm('Are you sure you want to reset DevDocs?')
78+ when 'back' then history.back()
79+ when 'reload' then window.location.reload()
80+ when 'reboot' then app.reboot()
81+ when 'hard-reload' then app.reload()
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()
8385 return
assets/javascripts/views/layout/resizer.coffee+2−9
@@ -11,9 +11,6 @@ class app.views.Resizer extends app.View · 10 unmodified lines
1111 init: ->
1212 @el.setAttribute('draggable', 'true')
1313 @appendTo $('._app')
14−
15− @style = $('style[data-resizer]')
16− @size = @style.getAttribute('data-size')
1714 return
1815
1916 MIN = 260
@@ -24,15 +21,11 @@ class app.views.Resizer extends app.View · 4 unmodified lines
2421 return unless value > 0
2522 value = Math.min(Math.max(Math.round(value), MIN), MAX)
2623 newSize = "#{value}px"
27− @style.innerHTML = @style.innerHTML.replace(new RegExp(@size, 'g'), newSize)
28− @size = newSize
29− if save
30− app.settings.setSize(value)
31− app.appCache?.updateInBackground()
24+ document.documentElement.style.setProperty('--sidebarWidth', newSize)
25+ app.settings.setSize(value) if save
3226 return
3327
3428 onDragStart: (event) =>
35− @style.removeAttribute('disabled')
3629 event.dataTransfer.effectAllowed = 'link'
3730 event.dataTransfer.setData('Text', '')
3831 $.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
2525 if super
2626 @render()
2727 document.body.classList.remove(SIDEBAR_HIDDEN_LAYOUT)
28− app.appCache?.on 'progress', @onAppCacheProgress
2928 return
3029
3130 deactivate: ->
@@ -33,7 +32,6 @@ class app.views.Settings extends app.View · 1 unmodified line
3332 @resetClass()
3433 @docPicker.detach()
3534 document.body.classList.add(SIDEBAR_HIDDEN_LAYOUT) if app.settings.hasLayout(SIDEBAR_HIDDEN_LAYOUT)
36− app.appCache?.off 'progress', @onAppCacheProgress
3735 return
3836
3937 render: ->
@@ -52,7 +50,7 @@ class app.views.Settings extends app.View · 12 unmodified lines
5250 docs = @docPicker.getSelectedDocs()
5351 app.settings.setDocs(docs)
5452
55− @saveBtn.textContent = if app.appCache then 'Downloading\u2026' else 'Saving\u2026'
53+ @saveBtn.textContent = 'Saving\u2026'
5654 disabledDocs = new app.collections.Docs(doc for doc in app.docs.all() when docs.indexOf(doc.slug) is -1)
5755 disabledDocs.uninstall ->
5856 app.db.migrate()
@@ -83,9 +81,3 @@ class app.views.Settings extends app.View · 24 unmodified lines
8381 $.stopEvent(event)
8482 app.router.show '/'
8583 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
3030 .on 'results', @onResults
3131 .on 'end', @onEnd
3232
33+ @scope
34+ .on 'change', @onScopeChange
35+
3336 app.on 'ready', @onReady
3437 $.on window, 'hashchange', @searchUrl
3538 $.on window, 'focus', @onWindowFocus
@@ -138,6 +141,11 @@ class app.views.Search extends app.View · 102 unmodified lines
138141 $.stopEvent(event)
139142 return
140143
144+ onScopeChange: =>
145+ @value = ''
146+ @onInput()
147+ return
148+
141149 afterRoute: (name, context) =>
142150 return if app.shortcuts.eventInProgress?.name is 'escape'
143151 @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
66 tag: '._search-tag'
77
88 @events:
9+ click: 'onClick'
910 keydown: 'onKeydown'
11+ textInput: 'onTextInput'
1012
1113 @routes:
1214 after: 'afterRoute'
@@ -87,17 +89,33 @@ class app.views.SearchScope extends app.View · 74 unmodified lines
8789 @trigger 'change', null, previousDoc
8890 return
8991
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+
90103 onKeydown: (event) =>
91104 if event.which is 8 # backspace
92− if @doc and not @input.value
93− $.stopEvent(event)
105+ if @doc and @input.selectionEnd is 0
94106 @reset()
95− else if not @doc and @input.value
107+ $.stopEvent(event)
108+ else if not @doc and @input.value and not $.isChromeForAndroid()
96109 return if event.ctrlKey or event.metaKey or event.altKey or event.shiftKey
97110 if event.which is 9 or # tab
98111 (event.which is 32 and app.isMobile()) # space
99− @search @input.value[0...@input.selectionStart]
100− $.stopEvent(event) if @doc
112+ @doScopeSearch(event)
113+ return
114+
115+ onTextInput: (event) =>
116+ return unless $.isChromeForAndroid()
117+ if not @doc and @input.value and event.data == ' '
118+ @doScopeSearch(event)
101119 return
102120
103121 extractHashValue: ->
assets/javascripts/views/sidebar/sidebar.coffee+1−1
@@ -28,7 +28,7 @@ class app.views.Sidebar extends app.View · 27 unmodified lines
2828
2929 app.on 'ready', @onReady
3030
31− $.on document.documentElement, 'mouseleave', (event) => @display() if event.clientX < 10
31+ $.on document.documentElement, 'mouseleave', (event) => @display() unless event.clientX <= 0
3232 $.on document.documentElement, 'mouseenter', => @resetDisplay(forceNoHover: false)
3333 return
3434