Unified · Split
| @@ -1,6 +1,7 @@ | |||
| 1 | FROM ruby:2.6.0 | 1 | FROM ruby:2.6.0 |
| 2 | 2 | ||
| 3 | ENV LANG=C.UTF-8 | 3 | ENV LANG=C.UTF-8 |
| 4 | ENV ENABLE_SERVICE_WORKER=true | ||
| 4 | 5 | ||
| 5 | WORKDIR /devdocs | 6 | WORKDIR /devdocs |
| 6 | 7 | ||
| @@ -1,6 +1,7 @@ | |||
| 1 | FROM ruby:2.6.0-alpine | 1 | FROM ruby:2.6.0-alpine |
| 2 | 2 | ||
| 3 | ENV LANG=C.UTF-8 | 3 | ENV LANG=C.UTF-8 |
| 4 | ENV ENABLE_SERVICE_WORKER=true | ||
| 4 | 5 | ||
| 5 | WORKDIR /devdocs | 6 | WORKDIR /devdocs |
| 6 | 7 | ||
| @@ -14,3 +14,4 @@ app.config = · 13 unmodified lines | |||
| 14 | release: <%= Time.now.utc.httpdate.to_json %> | 14 | release: <%= Time.now.utc.httpdate.to_json %> |
| 15 | mathml_stylesheet: '<%= App.cdn_origin %>/mathml.css' | 15 | mathml_stylesheet: '<%= App.cdn_origin %>/mathml.css' |
| 16 | service_worker_path: '/service-worker.js' | 16 | service_worker_path: '/service-worker.js' |
| 17 | service_worker_enabled: <%= App.environment == 'production' || !ENV["ENABLE_SERVICE_WORKER"].nil? %> | ||
| @@ -2,7 +2,7 @@ class app.ServiceWorker · 1 unmodified line | |||
| 2 | $.extend @prototype, Events | 2 | $.extend @prototype, Events |
| 3 | 3 | ||
| 4 | @isEnabled: -> | 4 | @isEnabled: -> |
| 5 | !!navigator.serviceWorker | 5 | !!navigator.serviceWorker and app.config.service_worker_enabled |
| 6 | 6 | ||
| 7 | constructor: -> | 7 | constructor: -> |
| 8 | @registration = null | 8 | @registration = null |
| @@ -5,7 +5,6 @@ class app.Settings · 4 unmodified lines | |||
| 5 | 'manualUpdate' | 5 | 'manualUpdate' |
| 6 | 'fastScroll' | 6 | 'fastScroll' |
| 7 | 'arrowScroll' | 7 | 'arrowScroll' |
| 8 | 'bypassCache' | ||
| 9 | 'docs' | 8 | 'docs' |
| 10 | 'dark' | 9 | 'dark' |
| 11 | 'layout' | 10 | 'layout' |
| @@ -92,11 +91,6 @@ class app.Settings · 80 unmodified lines | |||
| 92 | @set 'size', value | 91 | @set 'size', value |
| 93 | return | 92 | return |
| 94 | 93 | ||
| 95 | setBypassCache: (value) -> | ||
| 96 | @set 'bypassCache', value | ||
| 97 | app.serviceWorker?.updateInBackground() | ||
| 98 | return | ||
| 99 | |||
| 100 | dump: -> | 94 | dump: -> |
| 101 | @store.dump() | 95 | @store.dump() |
| 102 | 96 | ||
| @@ -44,7 +44,12 @@ canICloseTheTab = -> · 43 unmodified lines | |||
| 44 | if app.ServiceWorker.isEnabled() | 44 | if app.ServiceWorker.isEnabled() |
| 45 | """ Yes! Even offline, you can open a new tab, go to <a href="//devdocs.io">devdocs.io</a>, and everything will work as if you were online (provided you installed all the documentations you want to use beforehand). """ | 45 | """ Yes! Even offline, you can open a new tab, go to <a href="//devdocs.io">devdocs.io</a>, and everything will work as if you were online (provided you installed all the documentations you want to use beforehand). """ |
| 46 | else | 46 | else |
| 47 | """ No. Service Workers aren't available in your browser (or are 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 ENABLE_SERVICE_WORKERS environment variable)" | ||
| 51 | |||
| 52 | """ No. Service Workers #{reason}, so loading <a href="//devdocs.io">devdocs.io</a> offline won't work.<br> | ||
| 48 | The current tab will continue to function even when you go offline (provided you installed all the documentations beforehand). """ | 53 | The current tab will continue to function even when you go offline (provided you installed all the documentations beforehand). """ |
| 49 | 54 | ||
| 50 | app.templates.offlineDoc = (doc, status) -> | 55 | app.templates.offlineDoc = (doc, status) -> |
| @@ -8,7 +8,7 @@ app.templates.intro = """ · 7 unmodified lines | |||
| 8 | <p>Thanks for downloading DevDocs. Here are a few things you should know: | 8 | <p>Thanks for downloading DevDocs. Here are a few things you should know: |
| 9 | <ol class="_intro-list"> | 9 | <ol class="_intro-list"> |
| 10 | <li>Your local version of DevDocs won't self-update. Unless you're modifying the code, | 10 | <li>Your local version of DevDocs won't self-update. Unless you're modifying the code, |
| 11 | I recommend using the hosted version at <a href="https://devdocs.io">devdocs.io</a>. | 11 | we recommend using the hosted version at <a href="https://devdocs.io">devdocs.io</a>. |
| 12 | <li>Run <code>thor docs:list</code> to see all available documentations. | 12 | <li>Run <code>thor docs:list</code> to see all available documentations. |
| 13 | <li>Run <code>thor docs:download <name></code> to download documentations. | 13 | <li>Run <code>thor docs:download <name></code> to download documentations. |
| 14 | <li>Run <code>thor docs:download --installed</code> to update all downloaded documentations. | 14 | <li>Run <code>thor docs:download --installed</code> to update all downloaded documentations. |
| @@ -15,10 +15,6 @@ app.templates.settingsPage = (settings) -> """ · 14 unmodified lines | |||
| 15 | <input type="checkbox" form="settings" name="layout" value="_sidebar-hidden"#{if settings['_sidebar-hidden'] then ' checked' else ''}>Automatically hide and show the sidebar | 15 | <input type="checkbox" form="settings" name="layout" value="_sidebar-hidden"#{if settings['_sidebar-hidden'] then ' checked' else ''}>Automatically hide and show the sidebar |
| 16 | <small>Tip: drag the edge of the sidebar to resize it.</small> | 16 | <small>Tip: drag the edge of the sidebar to resize it.</small> |
| 17 | </label> | 17 | </label> |
| 18 | <label class="_settings-label"> | ||
| 19 | <input type="checkbox" form="settings" name="bypassCache" value="1"#{if settings.bypassCache then ' checked' else ''}>Bypass Service Worker cache | ||
| 20 | <small>When this is checked, the Service Worker will always fetch the latest version of requested files. Useful when making changes to the DevDocs source code.</small> | ||
| 21 | </label> | ||
| 22 | </div> | 18 | </div> |
| 23 | </div> | 19 | </div> |
| 24 | 20 | ||
| @@ -14,7 +14,6 @@ class app.views.SettingsPage extends app.View · 13 unmodified lines | |||
| 14 | settings.dark = app.settings.get('dark') | 14 | settings.dark = app.settings.get('dark') |
| 15 | settings.smoothScroll = !app.settings.get('fastScroll') | 15 | settings.smoothScroll = !app.settings.get('fastScroll') |
| 16 | settings.arrowScroll = app.settings.get('arrowScroll') | 16 | settings.arrowScroll = app.settings.get('arrowScroll') |
| 17 | settings.bypassCache = app.settings.get('bypassCache') | ||
| 18 | settings[layout] = app.settings.hasLayout(layout) for layout in app.settings.LAYOUTS | 17 | settings[layout] = app.settings.hasLayout(layout) for layout in app.settings.LAYOUTS |
| 19 | settings | 18 | settings |
| 20 | 19 | ||
| @@ -33,10 +32,6 @@ class app.views.SettingsPage extends app.View · 12 unmodified lines | |||
| 33 | app.settings.set('fastScroll', !enable) | 32 | app.settings.set('fastScroll', !enable) |
| 34 | return | 33 | return |
| 35 | 34 | ||
| 36 | toggleBypassCache: (enable) -> | ||
| 37 | app.settings.setBypassCache(!!enable) | ||
| 38 | return | ||
| 39 | |||
| 40 | toggle: (name, enable) -> | 35 | toggle: (name, enable) -> |
| 41 | app.settings.set(name, enable) | 36 | app.settings.set(name, enable) |
| 42 | return | 37 | return |
| @@ -80,8 +75,6 @@ class app.views.SettingsPage extends app.View · 37 unmodified lines | |||
| 80 | @toggleSmoothScroll input.checked | 75 | @toggleSmoothScroll input.checked |
| 81 | when 'import' | 76 | when 'import' |
| 82 | @import input.files[0], input | 77 | @import input.files[0], input |
| 83 | when 'bypassCache' | ||
| 84 | @toggleBypassCache input.checked | ||
| 85 | else | 78 | else |
| 86 | @toggle input.name, input.checked | 79 | @toggle input.name, input.checked |
| 87 | return | 80 | return |
| @@ -2,8 +2,6 @@ Adding a documentation may look like a daunting task but once you get the hang o · 1 unmodified line | |||
| 2 | 2 | ||
| 3 | **Note:** please read the [contributing guidelines](../.github/CONTRIBUTING.md) before submitting a new documentation. | 3 | **Note:** please read the [contributing guidelines](../.github/CONTRIBUTING.md) before submitting a new documentation. |
| 4 | 4 | ||
| 5 | **Note:** when editing any of the files in the `assets` directory or the `public` directory, you'll have to bypass the service worker cache. To do this, go to the Preferences page on your local instance of DevDocs, check "Bypass Service Worker cache" and refresh the page. | ||
| 6 | |||
| 7 | 1. Create a subclass of `Docs::UrlScraper` or `Docs::FileScraper` in the `lib/docs/scrapers/` directory. Its name should be the [PascalCase](http://api.rubyonrails.org/classes/String.html#method-i-camelize) equivalent of the filename (e.g. `my_doc` → `MyDoc`) | 5 | 1. Create a subclass of `Docs::UrlScraper` or `Docs::FileScraper` in the `lib/docs/scrapers/` directory. Its name should be the [PascalCase](http://api.rubyonrails.org/classes/String.html#method-i-camelize) equivalent of the filename (e.g. `my_doc` → `MyDoc`) |
| 8 | 2. Add the appropriate class attributes and filter options (see the [Scraper Reference](./scraper-reference.md) page). | 6 | 2. Add the appropriate class attributes and filter options (see the [Scraper Reference](./scraper-reference.md) page). |
| 9 | 3. Check that the scraper is listed in `thor docs:list`. | 7 | 3. Check that the scraper is listed in `thor docs:list`. |
| @@ -205,8 +205,28 @@ class App < Sinatra::Application · 204 unmodified lines | |||
| 205 | ].compact | 205 | ].compact |
| 206 | end | 206 | end |
| 207 | 207 | ||
| 208 | def bypass_cache? | 208 | def service_worker_cache_name |
| 209 | !memoized_cookies['bypassCache'].nil? | 209 | # Returns the digest of the last modified asset |
| 210 | # When a manifest exist, this digest is only calculated once based on the asset manifest because it never changes without a server restart | ||
| 211 | # If a manifest does not exist, it is calculated every time this method is called because the assets can change while the server is running | ||
| 212 | if File.exist?(App.assets_manifest_path) | ||
| 213 | return @@service_worker_cache_name ||= | ||
| 214 | Sprockets::Manifest | ||
| 215 | .new(nil, App.assets_manifest_path) | ||
| 216 | .files | ||
| 217 | .values | ||
| 218 | .sort_by {|file| file["mtime"]} | ||
| 219 | .reverse | ||
| 220 | .first["digest"] | ||
| 221 | else | ||
| 222 | last_modified_file = App.sprockets | ||
| 223 | .each_file | ||
| 224 | .to_a | ||
| 225 | .reject {|file| file.start_with?(App.docs_path)} | ||
| 226 | .max_by {|file| File.mtime(file)} | ||
| 227 | |||
| 228 | return App.sprockets.pack_base64digest(App.sprockets.file_digest(last_modified_file)) | ||
| 229 | end | ||
| 210 | end | 230 | end |
| 211 | 231 | ||
| 212 | def redirect_via_js(path) | 232 | def redirect_via_js(path) |
| @@ -1,6 +1,6 @@ | |||
| 1 | <%# Use the hash of the application.js file as cache name or 'app' if not running in production %> | 1 | <%# The name of the cache to store responses in %> |
| 2 | <%# This ensures that the cache is always updated if the hash of the application.js file changes %> | 2 | <%# If the cache name changes DevDocs is assumed to be updated %> |
| 3 | const cacheName = '<%= javascript_path('application', asset_host: false).scan(/application-([^\.]+)\.js/).last&.first || 'app' %>'; | 3 | const cacheName = '<%= service_worker_cache_name %>'; |
| 4 | 4 | ||
| 5 | <%# Url's to cache when the service worker is installed %> | 5 | <%# Url's to cache when the service worker is installed %> |
| 6 | const urlsToCache = [ | 6 | const urlsToCache = [ |
| @@ -32,9 +32,6 @@ self.addEventListener('activate', event => { · 25 unmodified lines | |||
| 32 | <%# Handle HTTP requests %> | 32 | <%# Handle HTTP requests %> |
| 33 | self.addEventListener('fetch', event => { | 33 | self.addEventListener('fetch', event => { |
| 34 | event.respondWith((async () => { | 34 | event.respondWith((async () => { |
| 35 | <% if bypass_cache? %> | ||
| 36 | return fetch(event.request); | ||
| 37 | <% else %> | ||
| 38 | const cachedResponse = await caches.match(event.request); | 35 | const cachedResponse = await caches.match(event.request); |
| 39 | if (cachedResponse) return cachedResponse; | 36 | if (cachedResponse) return cachedResponse; |
| 40 | 37 | ||
| @@ -53,6 +50,5 @@ self.addEventListener('fetch', event => { · 12 unmodified lines | |||
| 53 | 50 | ||
| 54 | throw err; | 51 | throw err; |
| 55 | } | 52 | } |
| 56 | <% end %> | ||
| 57 | })()); | 53 | })()); |
| 58 | }); | 54 | }); |