From 9f79940f915ca08cb49476790f2e589214595704 Mon Sep 17 00:00:00 2001 From: "David Humphrey (:humph) david.humphrey@senecacollege.ca" Date: Tue, 5 Apr 2016 19:08:37 -0400 Subject: [PATCH] Fix #546 - Use sw-precache to make Bramble offline ready Update docs Review fixes --- CONTRIBUTING.md | 9 +++ Gruntfile.js | 40 ++++++++++- package.json | 2 +- src/bramble-sw.js | 7 ++ src/main.js | 39 ++++++++++ sw-cache-file-list.json | 155 ++++++++++++++++++++++++++++++++++++++++ 6 files changed, 249 insertions(+), 3 deletions(-) create mode 100644 src/bramble-sw.js create mode 100644 sw-cache-file-list.json diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2ce990fded4..f17a80837ec 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -121,3 +121,12 @@ code contributions, reviewing pull requests, and providing feedback and suggesti direction of the project. Even if you're not a committer, you're still welcome to give feedback on any pull request! + +## Adding New Files + +When run in production (i.e., the resulting `dist/` dir from running `grunt build-browser-compressed`), +Bramble uses a Service Worker to cache and serve the app offline. In order to do this, the +`swPrecache` grunt task generates a Service Worker ready to cache and serve all the necessary +files. This file list is generated statically at build time based on the contents of the +`sw-cache-file-list.json` file. If you add new files (e.g., a new default extension), make sure +you add URL entries to this cache list. \ No newline at end of file diff --git a/Gruntfile.js b/Gruntfile.js index e7065f12bcc..d676d015905 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -27,6 +27,8 @@ var habitat = require('habitat'); habitat.load(); var env = new habitat(); +var Path = require('path'); + var GIT_BRANCH = env.get("BRAMBLE_MAIN_BRANCH") || "bramble"; var GIT_REMOTE = env.get("BRAMBLE_MAIN_REMOTE") || "upstream"; @@ -34,6 +36,7 @@ module.exports = function (grunt) { 'use strict'; var autoprefixer = require('autoprefixer-core'); + var swPrecache = require('sw-precache'); // load dependencies require('load-grunt-tasks')(grunt, {pattern: ['grunt-contrib-*', 'grunt-targethtml', 'grunt-usemin', 'grunt-cleanempty', 'grunt-npm', 'grunt-git', 'grunt-update-submodules', 'grunt-exec']}); @@ -518,6 +521,12 @@ module.exports = function (grunt) { localize: 'node scripts/properties2js', 'localize-dist': 'node scripts/properties2js dist', 'clean-nls': 'rm -fr src/nls && git checkout -- src/nls' + }, + + swPrecache: { + dist: { + rootDir: 'dist' + } } }); @@ -575,6 +584,29 @@ module.exports = function (grunt) { ]); }); + grunt.registerMultiTask('swPrecache', function() { + var done = this.async(); + var rootDir = this.data.rootDir; + var files = (function() { + return (require('./sw-cache-file-list.json')).files; + }()); + + var config = { + cacheId: 'bramble-cache::' + Date.now(), + logger: grunt.log.writeln, + staticFileGlobs: files, + stripPrefix: 'dist/', + ignoreUrlParametersMatching: [/./] + }; + + swPrecache.write(Path.join(rootDir, 'bramble-sw.js'), config, function(err) { + if(err) { + grunt.fail.warn(err); + } + done(); + }); + }); + // task: install grunt.registerTask('install', ['write-config', 'less']); @@ -615,8 +647,12 @@ module.exports = function (grunt) { 'uglify' ]); - // task: build dist/ for browser, pre-compressed with gzip - grunt.registerTask('build-browser-compressed', ['build-browser', 'compress']); + // task: build dist/ for browser, pre-compressed with gzip and SW precache + grunt.registerTask('build-browser-compressed', [ + 'build-browser', + 'compress', + 'swPrecache' + ]); // task: undo changes to the src/nls directory grunt.registerTask('unlocalize', ['exec:clean-nls']); diff --git a/package.json b/package.json index c2967054c56..652e9120c37 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,6 @@ "load-grunt-tasks": "0.2.0", "q": "0.9.2", "semver": "^4.1.0", - "jshint": "2.1.4", "xmldoc": "^0.1.2", "grunt-cleanempty": "1.0.3" }, @@ -56,6 +55,7 @@ "properties-parser": "0.3.1", "request": "^2.69.0", "requirejs": "2.1.22", + "sw-precache": "^3.1.1", "tar-fs": "^1.11.1" }, "scripts": { diff --git a/src/bramble-sw.js b/src/bramble-sw.js new file mode 100644 index 00000000000..c9e9cbc6864 --- /dev/null +++ b/src/bramble-sw.js @@ -0,0 +1,7 @@ +/** + * This is a service worker stub, meant only to allow development builds + * to load properly. The actual bramble-sw.js file is generated at build + * time, see Gruntfile and swPrecache task. + */ + +console.log("[Bramble] Service Worker cache not running in development."); diff --git a/src/main.js b/src/main.js index ea10f9af75a..ad865c6ec6c 100644 --- a/src/main.js +++ b/src/main.js @@ -70,6 +70,45 @@ if (window.location.search.indexOf("testEnvironment") > -1) { }); } +/** + * Service Worker offline cache registration. The bramble-sw.js file + * is generated by Grunt as part of a dist/ build, and will not do anything + * in dev builds. + */ +if ('serviceWorker' in navigator) { + navigator.serviceWorker.register('bramble-sw.js').then(function(reg) { + "use strict"; + + reg.onupdatefound = function() { + var installingWorker = reg.installing; + + installingWorker.onstatechange = function() { + switch (installingWorker.state) { + case 'installed': + if (navigator.serviceWorker.controller) { + // At this point, the old content will have been purged and the fresh content will + // have been added to the cache. + // It's the perfect time to display a "New content is available; please refresh." + // message in the page's interface. + console.log('[Bramble] New or updated content is available.'); + } else { + // At this point, everything has been precached. + // It's the perfect time to display a "Content is cached for offline use." message. + console.log('[Bramble] Content is now available offline!'); + } + break; + case 'redundant': + console.error('[Bramble] The installing service worker became redundant.'); + break; + } + }; + }; + }).catch(function(e) { + "use strict"; + console.error('[Bramble] Error during service worker registration:', e); + }); +} + define(function (require) { "use strict"; diff --git a/sw-cache-file-list.json b/sw-cache-file-list.json new file mode 100644 index 00000000000..9e94df7dec8 --- /dev/null +++ b/sw-cache-file-list.json @@ -0,0 +1,155 @@ +{ + "files": [ + "dist/hosted.*", + "dist/bramble.js", + "dist/index.html", + "dist/xorigin.js", + "dist/dependencies.js", + "dist/main.js", + "dist/config.json", + + "dist/styles/*.css", + "dist/styles/images/*.*", + + "dist/CodeMirror/lib/codemirror.css", + "dist/CodeMirror/mode/css/css.js", + "dist/CodeMirror/mode/htmlmixed/htmlmixed.js", + "dist/CodeMirror/mode/javascript/javascript.js", + "dist/CodeMirror/mode/xml/xml.js", + "dist/CodeMirror/mode/markdown/markdown.js", + "dist/CodeMirror/mode/meta/meta.js", + "dist/thirdparty/CodeMirror/addon/fold/brace-fold.js", + "dist/thirdparty/CodeMirror/addon/fold/comment-fold.js", + "dist/thirdparty/CodeMirror/addon/fold/markdown-fold.js", + + "dist/thirdparty/thirdparty.min.js", + "dist/thirdparty/require.min.js", + "dist/thirdparty/text/text.js", + + "dist/extensions/default/CSSCodeHints/main.js", + "dist/extensions/default/CSSCodeHints/CSSProperties.json", + "dist/extensions/default/CSSCodeHints/styles/brackets-css-hints.css", + + "dist/extensions/default/HTMLCodeHints/main.js", + "dist/extensions/default/HTMLCodeHints/HtmlTags.json", + "dist/extensions/default/HTMLCodeHints/HtmlAttributes.json", + + "dist/extensions/default/JavaScriptCodeHints/main.js", + "dist/extensions/default/JavaScriptCodeHints/ParameterHintManager.js", + "dist/extensions/default/JavaScriptCodeHints/ParameterHintTemplate.html", + "dist/extensions/default/JavaScriptCodeHints/HintUtils.js", + "dist/extensions/default/JavaScriptCodeHints/ScopeManager.js", + "dist/extensions/default/JavaScriptCodeHints/Session.js", + "dist/extensions/default/JavaScriptCodeHints/MessageIds.js", + "dist/extensions/default/JavaScriptCodeHints/Preferences.js", + "dist/extensions/default/JavaScriptCodeHints/HintUtils2.js", + "dist/extensions/default/JavaScriptCodeHints/keyboard.json", + "dist/extensions/default/JavaScriptCodeHints/tern-worker.js", + "dist/extensions/default/JavaScriptCodeHints/styles/brackets-js-hints.css", + "dist/extensions/default/JavaScriptCodeHints/thirdparty/acorn/acorn.js", + "dist/extensions/default/JavaScriptCodeHints/thirdparty/acorn/acorn_loose.js", + "dist/extensions/default/JavaScriptCodeHints/thirdparty/acorn/util/walk.js", + "dist/extensions/default/JavaScriptCodeHints/thirdparty/tern/defs/ecma5.json", + "dist/extensions/default/JavaScriptCodeHints/thirdparty/tern/defs/browser.json", + "dist/extensions/default/JavaScriptCodeHints/thirdparty/tern/lib/tern.js", + "dist/extensions/default/JavaScriptCodeHints/thirdparty/tern/lib/infer.js", + "dist/extensions/default/JavaScriptCodeHints/thirdparty/tern/lib/def.js", + "dist/extensions/default/JavaScriptCodeHints/thirdparty/tern/lib/signal.js", + + "dist/extensions/default/InlineColorEditor/main.js", + "dist/extensions/default/InlineColorEditor/InlineColorEditor.js", + "dist/extensions/default/InlineColorEditor/ColorEditor.js", + "dist/extensions/default/InlineColorEditor/ColorEditorTemplate.html", + "dist/extensions/default/InlineColorEditor/thirdparty/tinycolor-min.js", + "dist/extensions/default/InlineColorEditor/css/main.css", + "dist/extensions/default/InlineColorEditor/*.png", + + "dist/extensions/default/JavaScriptQuickEdit/main.js", + + "dist/extensions/default/QuickOpenCSS/main.js", + + "dist/extensions/default/QuickOpenHTML/main.js", + + "dist/extensions/default/QuickOpenJavaScript/main.js", + + "dist/extensions/default/QuickView/main.js", + "dist/extensions/default/QuickView/QuickViewTemplate.html", + "dist/extensions/default/QuickView/QuickView.css", + "dist/extensions/default/QuickView/*.png", + + "dist/extensions/default/WebPlatformDocs/InlineDocsViewer.html", + "dist/extensions/default/WebPlatformDocs/WebPlatformDocs.css", + "dist/extensions/default/WebPlatformDocs/main.js", + "dist/extensions/default/WebPlatformDocs/InlineDocsViewer.js", + "dist/extensions/default/WebPlatformDocs/css.json", + "dist/extensions/default/WebPlatformDocs/html.json", + "dist/extensions/default/WebPlatformDocs/logo.svg", + + "dist/extensions/default/CodeFolding/main.js", + "dist/extensions/default/CodeFolding/Prefs.js", + "dist/extensions/default/CodeFolding/main.css", + "dist/extensions/default/CodeFolding/foldhelpers/foldgutter.js", + "dist/extensions/default/CodeFolding/foldhelpers/foldcode.js", + "dist/extensions/default/CodeFolding/foldhelpers/indentFold.js", + + "dist/extensions/default/Autosave/main.js", + + "dist/extensions/default/bramble/main.js", + "dist/extensions/default/bramble/lib/iframe-browser.js", + "dist/extensions/default/bramble/lib/UI.js", + "dist/extensions/default/bramble/lib/launcher.js", + "dist/extensions/default/bramble/nohost/main.js", + "dist/extensions/default/bramble/lib/PostMessageTransport.js", + "dist/extensions/default/bramble/lib/xhr/XHRHandler.js", + "dist/extensions/default/bramble/lib/xhr/XHRShim.js", + "dist/extensions/default/bramble/lib/Theme.js", + "dist/extensions/default/bramble/lib/RemoteCommandHandler.js", + "dist/extensions/default/bramble/lib/RemoteEvents.js", + "dist/extensions/default/bramble/nohost/HTMLServer.js", + "dist/extensions/default/bramble/nohost/StaticServer.js", + "dist/extensions/default/bramble/lib/compatibility.js", + "dist/extensions/default/bramble/lib/Tutorial.js", + "dist/extensions/default/bramble/lib/MouseManager.js", + "dist/extensions/default/bramble/lib/LinkManager.js", + "dist/extensions/default/bramble/lib/PostMessageTransportRemote.js", + "dist/extensions/default/bramble/lib/Mobile.html", + "dist/extensions/default/bramble/lib/MouseManagerRemote.js", + "dist/extensions/default/bramble/lib/LinkManagerRemote.js", + "dist/extensions/default/bramble/stylesheets/style.css", + "dist/extensions/default/bramble/stylesheets/sidebarTheme.css", + "dist/extensions/default/bramble/stylesheets/lightTheme.css", + "dist/extensions/default/bramble/stylesheets/darkTheme.css", + "dist/extensions/default/bramble/**/*.svg", + + "dist/extensions/default/brackets-paste-and-indent/main.js", + + "dist/extensions/default/BrambleUrlCodeHints/main.js", + "dist/extensions/default/BrambleUrlCodeHints/camera/index.js", + "dist/extensions/default/BrambleUrlCodeHints/selfie.js", + "dist/extensions/default/BrambleUrlCodeHints/camera/interface.js", + "dist/extensions/default/BrambleUrlCodeHints/camera/video.js", + "dist/extensions/default/BrambleUrlCodeHints/camera/photo.js", + "dist/extensions/default/BrambleUrlCodeHints/camera/utils.js", + "dist/extensions/default/BrambleUrlCodeHints/camera-dialog.js", + "dist/extensions/default/BrambleUrlCodeHints/data.json", + "dist/extensions/default/BrambleUrlCodeHints/camera/selfieWidget.html", + "dist/extensions/default/BrambleUrlCodeHints/camera/camera-shutter-click-08.mp3", + "dist/extensions/default/BrambleUrlCodeHints/dialog.html", + "dist/extensions/default/BrambleUrlCodeHints/style.css", + "dist/extensions/default/BrambleUrlCodeHints/*.png", + + "dist/extensions/default/UploadFiles/main.js", + "dist/extensions/default/UploadFiles/UploadFilesDialog.js", + "dist/extensions/default/UploadFiles/htmlContent/upload-files-dialog.html", + "dist/extensions/default/UploadFiles/styles.css", + "dist/extensions/default/UploadFiles/images/*.svg", + + "dist/extensions/default/bramble-move-file/main.js", + "dist/extensions/default/bramble-move-file/MoveToDialog.js", + "dist/extensions/default/bramble-move-file/MoveUtils.js", + "dist/extensions/default/bramble-move-file/htmlContent/move-to-dialog.html", + "dist/extensions/default/bramble-move-file/htmlContent/directory-tree.html", + "dist/extensions/default/bramble-move-file/styles/style.css", + "dist/extensions/default/bramble-move-file/images/*.svg" + ] +}