History

Fix merge conflicts

Jasper van Merle committed 7 years ago

Commit 8aa8ae12 parents 85f57fe 536003c293 files · +2484 −1317Browse files

Showing the changes to assets/javascripts (27 of 293 files). Show all

Unified · Split

27 files
  1. assets/javascripts/app/app.coffee +16 −12
  2. assets/javascripts/app/appcache.coffee +0 −42
  3. assets/javascripts/app/config.coffee.erb +2 −0
  4. assets/javascripts/app/serviceworker.coffee +49 −0
  5. assets/javascripts/app/settings.coffee +32 −0
  6. assets/javascripts/app/update_checker.coffee +3 −3
  7. assets/javascripts/lib/license.coffee +1 −1
  8. assets/javascripts/lib/page.coffee +18 −2
  9. assets/javascripts/lib/util.coffee +4 −0
  10. assets/javascripts/news.json +7 −0
  11. assets/javascripts/templates/error_tmpl.coffee +9 −12
  12. assets/javascripts/templates/notif_tmpl.coffee +6 −0
  13. assets/javascripts/templates/pages/about_tmpl.coffee +29 −23
  14. assets/javascripts/templates/pages/offline_tmpl.coffee +9 −4
  15. assets/javascripts/templates/pages/root_tmpl.coffee.erb +3 −14
  16. assets/javascripts/templates/pages/settings_tmpl.coffee +4 −0
  17. assets/javascripts/tracking.js +27 −23
  18. assets/javascripts/views/content/entry_page.coffee +1 −1
  19. assets/javascripts/views/content/offline_page.coffee +1 −0
  20. assets/javascripts/views/content/root_page.coffee +0 −4
  21. assets/javascripts/views/content/settings_page.coffee +9 −12
  22. assets/javascripts/views/layout/document.coffee +7 −5
  23. assets/javascripts/views/layout/resizer.coffee +2 −9
  24. assets/javascripts/views/layout/settings.coffee +1 −9
  25. assets/javascripts/views/search/search.coffee +8 −0
  26. assets/javascripts/views/search/search_scope.coffee +23 −5
  27. assets/javascripts/views/sidebar/sidebar.coffee +1 −1
assets/javascripts/app/app.coffee+16−12
@@ -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
@@ -150,7 +152,7 @@ · 127 unmodified lines
150152 saveDocs: ->
151153 @settings.setDocs(doc.slug for doc in @docs.all())
152154 @db.migrate()
153− @appCache?.updateInBackground()
155+ @serviceWorker?.updateInBackground()
154156
155157 welcomeBack: ->
156158 visitCount = @settings.get('count')
@@ -160,17 +162,24 @@ · 3 unmodified lines
160162 new app.views.Updates()
161163 @updateChecker = new app.UpdateChecker()
162164
165+ reboot: ->
166+ if location.pathname isnt '/' and location.pathname isnt '/settings'
167+ window.location = "/##{location.pathname}"
168+ else
169+ window.location = '/'
170+ return
171+
163172 reload: ->
164173 @docs.clearCache()
165174 @disabledDocs.clearCache()
166− if @appCache then @appCache.reload() else window.location = '/'
175+ if @serviceWorker then @serviceWorker.reload() else @reboot()
167176 return
168177
169178 reset: ->
170179 @localStorage.reset()
171180 @settings.reset()
172181 @db?.reset()
173− @appCache?.update()
182+ @serviceWorker?.update()
174183 window.location = '/'
175184 return
176185
@@ -189,9 +198,9 @@ · 12 unmodified lines
189198 return
190199
191200 indexHost: ->
192− # 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
193202 # enabled because it doesn't support caching URLs that use CORS.
194− @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']
195204
196205 onBootError: (args...) ->
197206 @trigger 'bootError'
@@ -248,7 +257,7 @@ · 50 unmodified lines
248257 matchMedia: !!window.matchMedia
249258 insertAdjacentHTML: !!document.body.insertAdjacentHTML
250259 defaultPrevented: document.createEvent('CustomEvent').defaultPrevented is false
251− cssGradients: supportsCssGradients()
260+ cssVariables: !!CSS?.supports?('(--t: 0)')
252261
253262 for key, value of features when not value
254263 Raven.captureMessage "unsupported/#{key}", level: 'info'
@@ -271,9 +280,4 @@ · 16 unmodified lines
271280 isInvalidLocation: ->
272281 @config.env is 'production' and location.host.indexOf(app.config.production_host) isnt 0
273282
274−supportsCssGradients = ->
275− el = document.createElement('div')
276− el.style.cssText = "background-image: -webkit-linear-gradient(top, #000, #fff); background-image: linear-gradient(to top, #000, #fff);"
277− el.style.backgroundImage.indexOf('gradient') >= 0
278−
279283 $.extend app, Events
assets/javascripts/app/appcache.coffee+0−42
@@ -1,42 +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', -> window.location = '/'
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+2−0
@@ -13,3 +13,5 @@ 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+ service_worker_path: '/service-worker.js'
17+ 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+32−0
@@ -5,6 +5,7 @@ class app.Settings · 4 unmodified lines
55 'manualUpdate'
66 'fastScroll'
77 'arrowScroll'
8+ 'analyticsConsent'
89 'docs'
910 'dark'
1011 'layout'
@@ -20,6 +21,8 @@ class app.Settings · 9 unmodified lines
2021 'news'
2122 ]
2223
24+ LAYOUTS: ['_max-width', '_sidebar-hidden', '_native-scrollbars']
25+
2326 @defaults:
2427 count: 0
2528 hideDisabled: false
@@ -27,6 +30,7 @@ class app.Settings · 1 unmodified line
2730 news: 0
2831 manualUpdate: false
2932 schema: 1
33+ analyticsConsent: false
3034
3135 constructor: ->
3236 @store = new CookieStore
@@ -39,6 +43,7 @@ class app.Settings · 6 unmodified lines
3943 set: (key, value) ->
4044 @store.set(key, value)
4145 delete @cache[key]
46+ @toggleDark(value) if key == 'dark'
4247 return
4348
4449 del: (key) ->
@@ -64,6 +69,8 @@ class app.Settings · 19 unmodified lines
6469 return
6570
6671 setLayout: (name, enable) ->
72+ @toggleLayout(name, enable)
73+
6774 layout = (@store.get('layout') || '').split(' ')
6875 $.arrayDelete(layout, '')
6976
@@ -105,3 +112,28 @@ class app.Settings · 35 unmodified lines
105112 @store.reset()
106113 @cache = {}
107114 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/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+18−2
@@ -190,7 +190,7 @@ isSameOrigin = (url) -> · 189 unmodified lines
190190
191191 updateCanonicalLink = ->
192192 @canonicalLink ||= document.head.querySelector('link[rel="canonical"]')
193− @canonicalLink.setAttribute('href', "http://#{location.host}#{location.pathname}")
193+ @canonicalLink.setAttribute('href', "https://#{location.host}#{location.pathname}")
194194
195195 trackers = []
196196
@@ -199,5 +199,21 @@ page.track = (fn) -> · 2 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] == '_'
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/news.json+7−0
@@ -1,5 +1,12 @@
11 [
22 [
3+ "2019-07-19",
4+ "Replaced the AppCache with a Service Worker (which makes DevDocs an installable PWA) and fixed layout preferences on Firefox."
5+ ],
6+ [
7+ "2018-09-23",
8+ "New documentations: <a href=\"/puppeteer/\">Puppeteer</a> and <a href=\"/handlebars/\">Handlebars.js</a>"
9+ ], [
310 "2018-08-12",
411 "New documentations: <a href=\"/dart/\">Dart</a> and <a href=\"/qt/\">Qt</a>"
512 ], [
assets/javascripts/templates/error_tmpl.coffee+9−12
@@ -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
@@ -56,21 +56,18 @@ app.templates.unsupportedBrowser = """ · 36 unmodified lines
5656 <h1 class="_fail-title">Your browser is unsupported, sorry.</h1>
5757 <p class="_fail-text">DevDocs is an API documentation browser which supports the following browsers:
5858 <ul class="_fail-list">
59− <li>Recent versions of Chrome and Firefox
60− <li>Safari 5.1+
61− <li>Opera 12.1+
62− <li>Internet Explorer 10+
63− <li>iOS 6+
64− <li>Android 4.1+
65− <li>Windows Phone 8+
59+ <li>Recent versions of Firefox, Chrome, or Opera
60+ <li>Safari 11.1+
61+ <li>Edge 17+
62+ <li>iOS 11.3+
6663 </ul>
6764 <p class="_fail-text">
68− If you're unable to upgrade, I apologize.
69− 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.
7067 <p class="_fail-text">
7168 Note: if you're already using one of the browsers above, check your settings and add-ons.
7269 The app uses feature detection, not user agent sniffing.
7370 <p class="_fail-text">
74− &mdash; Thibaut <a href="https://twitter.com/DevDocs" class="_fail-link">@DevDocs</a>
71+ &mdash; <a href="https://twitter.com/DevDocs">@DevDocs</a>
7572 </div>
7673 """
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+29−23
@@ -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
@@ -102,7 +98,7 @@ credits = [ · 14 unmodified lines
10298 'https://www.apache.org/licenses/LICENSE-2.0'
10399 ], [
104100 'Async',
105− '2010-2017 Caolan McMahon',
101+ '2010-2018 Caolan McMahon',
106102 'MIT',
107103 'https://raw.githubusercontent.com/caolan/async/master/LICENSE'
108104 ], [
@@ -192,7 +188,7 @@ credits = [ · 83 unmodified lines
192188 'https://raw.githubusercontent.com/jashkenas/coffeescript/master/LICENSE'
193189 ], [
194190 'Cordova',
195− '2012-2017 The Apache Software Foundation',
191+ '2012-2018 The Apache Software Foundation',
196192 'Apache',
197193 'https://raw.githubusercontent.com/apache/cordova-docs/master/LICENSE'
198194 ], [
@@ -310,6 +306,11 @@ credits = [ · 111 unmodified lines
310306 'GruntJS Team',
311307 'MIT',
312308 'https://github.com/gruntjs/grunt-docs/blob/master/package.json#L10'
309+ ], [
310+ 'Handlebars',
311+ '2011-2017 Yehuda Katz',
312+ 'MIT',
313+ 'https://raw.githubusercontent.com/wycats/handlebars.js/master/LICENSE'
313314 ], [
314315 'Haskell',
315316 'The University of Glasgow',
@@ -367,7 +368,7 @@ credits = [ · 51 unmodified lines
367368 'https://raw.githubusercontent.com/jquery/api.jqueryui.com/master/LICENSE.txt'
368369 ], [
369370 'Julia',
370− '2009-2016 Jeff Bezanson, Stefan Karpinski, Viral B. Shah, and other contributors',
371+ '2009-2018 Jeff Bezanson, Stefan Karpinski, Viral B. Shah, and other contributors',
371372 'MIT',
372373 'https://raw.githubusercontent.com/JuliaLang/julia/master/LICENSE.md'
373374 ], [
@@ -437,7 +438,7 @@ credits = [ · 63 unmodified lines
437438 'https://daringfireball.net/projects/markdown/license'
438439 ], [
439440 'Matplotlib',
440− '2012-2017 Matplotlib Development Team. All rights reserved.',
441+ '2012-2018 Matplotlib Development Team. All rights reserved.',
441442 'Custom',
442443 'https://raw.githubusercontent.com/matplotlib/matplotlib/master/LICENSE/LICENSE'
443444 ], [
@@ -560,6 +561,11 @@ credits = [ · 116 unmodified lines
560561 '1996-2018 The PostgreSQL Global Development Group<br>&copy; 1994 The Regents of the University of California',
561562 'PostgreSQL',
562563 'https://www.postgresql.org/about/licence/'
564+ ], [
565+ 'Puppeteer',
566+ '2017 Google Inc',
567+ 'Apache',
568+ 'https://raw.githubusercontent.com/GoogleChrome/puppeteer/master/LICENSE'
563569 ], [
564570 'Pygame',
565571 'Pygame Developpers',
@@ -637,7 +643,7 @@ credits = [ · 71 unmodified lines
637643 'http://scikit-image.org/docs/dev/license.html'
638644 ], [
639645 'scikit-learn',
640− '2007-2017 The scikit-learn developers',
646+ '2007-2018 The scikit-learn developers',
641647 'BSD',
642648 'https://raw.githubusercontent.com/scikit-learn/scikit-learn/master/COPYING'
643649 ], [
@@ -682,9 +688,9 @@ credits = [ · 38 unmodified lines
682688 'https://raw.githubusercontent.com/hashicorp/terraform-website/master/LICENSE.md'
683689 ], [
684690 'Twig',
685− '2009-2017 The Twig Team',
691+ '2009-2018 The Twig Team',
686692 'BSD',
687− 'https://twig.sensiolabs.org/license'
693+ 'https://twig.symfony.com/license'
688694 ], [
689695 'TypeScript',
690696 'Microsoft and other contributors',
@@ -692,7 +698,7 @@ credits = [ · 1 unmodified line
692698 'https://raw.githubusercontent.com/Microsoft/TypeScript-Handbook/master/LICENSE'
693699 ], [
694700 'Underscore.js',
695− '2009-2017 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors',
701+ '2009-2018 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors',
696702 'MIT',
697703 'https://raw.githubusercontent.com/jashkenas/underscore/master/LICENSE'
698704 ], [
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−14
@@ -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!
@@ -72,14 +72,3 @@ app.templates.androidWarning = """ · 26 unmodified lines
7272 <p>To install DevDocs on your phone, visit <a href="https://devdocs.io" target="_blank" rel="noopener">devdocs.io</a> in Chrome and select "Add to home screen" in the menu.
7373 </div>
7474 """
75−
76−app.templates.httpWarning = """
77− <div class="_intro"><div class="_intro-message">
78− <h2 class="_intro-title">Hi there!</h2>
79− <p>DevDocs is migrating to HTTPS.
80− <p>Please update your bookmarks to point to <a href="https://devdocs.io">https://devdocs.io</a>.
81− <p>When using the HTTPS version, your preferences will carry over automatically, but your offline data will be reset. Simply re-download documentation in the <a href="https://devdocs.io/offline">Offline area</a>, and you'll be all set to use DevDocs securely offline.
82− <p>Sorry for the inconvenience. This migration is needed because browsers are removing support for certain DOM APIs that power DevDocs's offline mode over non-secure origins.
83− <p>Thanks for using DevDocs, and happy coding!
84− </div></div>
85−"""
assets/javascripts/templates/pages/settings_tmpl.coffee+4−0
@@ -19,6 +19,10 @@ app.templates.settingsPage = (settings) -> """ · 18 unmodified lines
1919 <input type="checkbox" form="settings" name="autoInstall" value="_auto-install"#{if settings.autoInstall then ' checked' else ''}>Automatically download documentation for offline use
2020 <small>Only enable this when bandwidth isn't a concern to you.</small>
2121 </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>
2226 </div>
2327 </div>
2428
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/views/content/entry_page.coffee+1−1
@@ -123,7 +123,7 @@ class app.views.EntryPage extends app.View · 122 unmodified lines
123123 @render @tmpl('pageLoadError')
124124 @resetClass()
125125 @addClass @constructor.errorClass
126− app.appCache?.update()
126+ app.serviceWorker?.update()
127127 return
128128
129129 cache: ->
assets/javascripts/views/content/offline_page.coffee+1−0
@@ -57,6 +57,7 @@ class app.views.OfflinePage extends app.View · 56 unmodified lines
5757 doc[action](@onInstallSuccess.bind(@, doc), @onInstallError.bind(@, doc), @onInstallProgress.bind(@, doc))
5858 el.parentNode.innerHTML = "#{el.textContent.replace(/e$/, '')}ing…"
5959 else if action = el.getAttribute('data-action-all')
60+ return unless action isnt 'uninstall' or window.confirm('Uninstall all docs?')
6061 app.db.migrate()
6162 $.click(el) for el in @findAll("[data-action='#{action}']")
6263 return
assets/javascripts/views/content/root_page.coffee+0−4
@@ -19,10 +19,6 @@ class app.views.RootPage extends app.View · 18 unmodified lines
1919 else
2020 'intro'
2121
22− # temporary
23− if location.host is 'devdocs.io' and location.protocol is 'http:'
24− tmpl = 'httpWarning'
25−
2622 @append @tmpl(tmpl)
2723 return
2824
assets/javascripts/views/content/settings_page.coffee+9−12
@@ -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:
@@ -18,32 +15,30 @@ class app.views.SettingsPage extends app.View · 10 unmodified lines
1815 settings.smoothScroll = !app.settings.get('fastScroll')
1916 settings.arrowScroll = app.settings.get('arrowScroll')
2017 settings.autoInstall = app.settings.get('autoInstall')
21− settings[layout] = app.settings.hasLayout(layout) for layout in LAYOUTS
18+ settings.analyticsConsent = app.settings.get('analyticsConsent')
19+ settings[layout] = app.settings.hasLayout(layout) for layout in app.settings.LAYOUTS
2220 settings
2321
2422 getTitle: ->
2523 'Preferences'
2624
2725 toggleDark: (enable) ->
28− css = $('link[rel="stylesheet"][data-alt]')
29− alt = css.getAttribute('data-alt')
30− css.setAttribute('data-alt', css.getAttribute('href'))
31− css.setAttribute('href', alt)
3226 app.settings.set('dark', !!enable)
33− app.appCache?.updateInBackground()
3427 return
3528
3629 toggleLayout: (layout, enable) ->
37− document.body.classList[if enable then 'add' else 'remove'](layout) unless layout is SIDEBAR_HIDDEN_LAYOUT
38− document.body.classList[if $.overlayScrollbarsEnabled() then 'add' else 'remove']('_overlay-scrollbars')
3930 app.settings.setLayout(layout, enable)
40− app.appCache?.updateInBackground()
4131 return
4232
4333 toggleSmoothScroll: (enable) ->
4434 app.settings.set('fastScroll', !enable)
4535 return
4636
37+ toggleAnalyticsConsent: (enable) ->
38+ app.settings.set('analyticsConsent', if enable then '1' else '0')
39+ resetAnalytics() unless enable
40+ return
41+
4742 toggle: (name, enable) ->
4843 app.settings.set(name, enable)
4944 return
@@ -87,6 +82,8 @@ class app.views.SettingsPage extends app.View · 37 unmodified lines
8782 @toggleSmoothScroll input.checked
8883 when 'import'
8984 @import input.files[0], input
85+ when 'analyticsConsent'
86+ @toggleAnalyticsConsent input.checked
9087 else
9188 @toggle input.name, input.checked
9289 return
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 window.location = '/'
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') && app.reboot()
84+ when 'decline-analytics' then Cookies.set('analyticsConsent', '0') && 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