jekyll-theme-open-course 0.0.1 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +6 -6
  3. data/_config.yml +35 -2
  4. data/_data/utility.yml +1 -1
  5. data/_layouts/calendar.html +68 -17
  6. data/_layouts/default.html +39 -22
  7. data/_layouts/error.html +10 -0
  8. data/_layouts/policies.html +17 -0
  9. data/_layouts/projects.html +71 -0
  10. data/_sass/_base.scss +108 -0
  11. data/_sass/_colors.scss +190 -0
  12. data/_sass/_fonts.scss +10 -0
  13. data/_sass/_typography.scss +358 -0
  14. data/assets/css/print.css +26 -24
  15. data/assets/css/screen.scss +22 -0
  16. data/assets/js/site.js +289 -0
  17. data/assets/js/sw.js +220 -0
  18. data/exe/jtoc +76 -0
  19. data/lib/jtoc.rb +177 -0
  20. data/lib/starter_files/_config.yml.erb +84 -0
  21. data/lib/starter_files/_data/calendar.yml.erb +6 -0
  22. data/lib/starter_files/_data/utility.yml +35 -0
  23. data/lib/starter_files/index.md.erb +5 -0
  24. data/lib/starter_files/offline/index.md +7 -0
  25. data/lib/starter_files/syllabus/_policies/academic-integrity.md +3 -0
  26. data/lib/starter_files/syllabus/_policies/assignment-submission.md +3 -0
  27. data/lib/starter_files/syllabus/_policies/books.md +17 -0
  28. data/lib/starter_files/syllabus/_policies/description.md +3 -0
  29. data/lib/starter_files/syllabus/_policies/goals.md +3 -0
  30. data/lib/starter_files/syllabus/_policies/grading-criteria.md +3 -0
  31. data/lib/starter_files/syllabus/_policies/grading-policy.md +3 -0
  32. data/lib/starter_files/syllabus/_policies/late-work.md +3 -0
  33. data/lib/starter_files/syllabus/_policies/materials.md +17 -0
  34. data/lib/starter_files/syllabus/_policies/objectives.md +3 -0
  35. data/lib/starter_files/syllabus/_policies/outcomes.md +3 -0
  36. data/lib/starter_files/syllabus/_policies/participation.md +3 -0
  37. data/lib/starter_files/syllabus/_policies/special-needs.md +3 -0
  38. data/lib/starter_files/syllabus/_policies/technology-policy.md +3 -0
  39. data/lib/starter_files/syllabus/_projects/project-00.md.erb +23 -0
  40. data/lib/starter_files/syllabus/_weeks/week-00.md.erb +11 -0
  41. data/projects/index.md +1 -30
  42. metadata +39 -26
  43. data/_data/weeks/01.yml +0 -21
  44. data/_data/weeks/02.yml +0 -1
  45. data/_data/weeks/03.yml +0 -1
  46. data/_data/weeks/04.yml +0 -1
  47. data/_data/weeks/05.yml +0 -1
  48. data/_data/weeks/06.yml +0 -1
  49. data/_data/weeks/07.yml +0 -1
  50. data/_data/weeks/08.yml +0 -1
  51. data/_data/weeks/09.yml +0 -1
  52. data/_data/weeks/10.yml +0 -1
  53. data/_data/weeks/11.yml +0 -1
  54. data/_data/weeks/12.yml +0 -1
  55. data/_data/weeks/13.yml +0 -1
  56. data/_data/weeks/14.yml +0 -1
  57. data/_data/weeks/15.yml +0 -1
  58. data/_data/weeks/16.yml +0 -1
  59. data/_projects/project-01.md +0 -25
  60. data/_projects/project-02.md +0 -25
  61. data/_projects/project-03.md +0 -25
  62. data/assets/.keep +0 -0
  63. data/assets/css/screen.css +0 -408
@@ -1,31 +1,33 @@
1
- #navigation,
2
- #this-week,
3
- #links,
4
- .fineprint + .fineprint,
5
- .week h4 a {
6
- display: none;
7
- }
8
1
  #header,
9
- #this-week,
10
- #content,
11
- #links,
12
- #instructor,
13
2
  #footer {
14
- padding-right: 0.1in;
15
- padding-left: 0.3in;
3
+ color: inherit;
16
4
  }
17
- #header small {
18
- position: static;
19
- }
20
- html {
21
- color: #000;
22
- font-size: 11pt;
5
+ a,
6
+ #header a {
7
+ text-decoration: none;
8
+ color: inherit;
23
9
  }
24
- a {
25
- color: #000;
10
+ #content a[href^="http"]::after {
11
+ display: inline;
12
+ content: "(" attr(href) ")";
13
+ font-family: Menlo, Monaco, "Droid Sans Mono", Courier, "Courier New", monospace;
14
+ font-size: 0.85em;
15
+ color: #CCC;
26
16
  text-decoration: none;
27
17
  }
28
- a[href^='http']::after {
29
- content: " (" attr(href) ")";
30
- color: #666;
18
+ p,li {
19
+ -webkit-hyphens: none;
20
+ hyphens: none;
21
+ }
22
+ h2 {
23
+ font-family: "moderno-fb-condensed";
24
+ font-weight: 700;
25
+ }
26
+
27
+
28
+ /* Hide Navigation Stuff */
29
+
30
+ #nav-nav,
31
+ #full-nav {
32
+ display: none;
31
33
  }
@@ -0,0 +1,22 @@
1
+ ---
2
+ ---
3
+ @import url("{{site.course.css.font-url}}");
4
+
5
+ /* http://meyerweb.com/eric/tools/css/reset/
6
+ v2.0 | 20110126
7
+ License: none (public domain)
8
+ */
9
+ /* stylelint-disable */
10
+ /* Keep reset CSS to one minified line */
11
+ a,abbr,acronym,address,applet,article,aside,audio,b,big,blockquote,body,canvas,caption,center,cite,code,dd,del,details,dfn,div,dl,dt,em,embed,fieldset,figcaption,figure,footer,form,h1,h2,h3,h4,h5,h6,header,hgroup,html,i,iframe,img,ins,kbd,label,legend,li,mark,menu,nav,object,ol,output,p,pre,q,ruby,s,samp,section,small,span,strike,strong,sub,summary,sup,table,tbody,td,tfoot,th,thead,time,tr,tt,u,ul,var,video{margin:0;padding:0;border:0;font:inherit;vertical-align:baseline}article,aside,details,figcaption,figure,footer,header,hgroup,menu,nav,section{display:block}html,.reset{line-height:1}ol,ul{list-style:none}blockquote,q{quotes:none}blockquote:after,blockquote:before,q:after,q:before{content:'';content:none}table{border-collapse:collapse;border-spacing:0}
12
+ /* stylelint-enable */
13
+
14
+ html.loading,
15
+ html.loading * {
16
+ transition: none !important;
17
+ }
18
+
19
+ @import "fonts";
20
+ @import "colors";
21
+ @import "typography";
22
+ @import "base";
@@ -0,0 +1,289 @@
1
+ // Add a .js utility class to <html>
2
+ var html = document.querySelector('html');
3
+ html.classList.add('js');
4
+
5
+ // Register service worker, if browser supports them
6
+ if ('serviceWorker' in navigator) {
7
+ var scope = detectSiteScope(location.href);
8
+ // console.log(scope.path);
9
+ navigator.serviceWorker.register(scope.path + 'assets/js/sw.js', { scope: scope.path })
10
+ .then(function(registration) {
11
+ console.log('Registered service worker scoped to', registration.scope);
12
+ })
13
+ .catch(function(error) {
14
+ console.error('Failed to register service worker', error)
15
+ });
16
+
17
+ function detectSiteScope(url) {
18
+ var scope = {};
19
+ scope.id = url.split('/')[3];
20
+ scope.path = '/';
21
+ if (scope.id.length > 0) {
22
+ scope.path = '/' + scope.id + '/';
23
+ }
24
+ return scope;
25
+ }
26
+ }
27
+
28
+ // Load up the theme switcher if @supports & custom properties available
29
+ if ('supports' in CSS && CSS.supports("(--foo: bar)")) {
30
+
31
+ function ThemeSwitch() {
32
+ var html = document.querySelector('html');
33
+ var icons = {
34
+ light: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M12 18a6 6 0 1 1 0-12 6 6 0 0 1 0 12zm0-2a4 4 0 1 0 0-8 4 4 0 0 0 0 8zM11 1h2v3h-2V1zm0 19h2v3h-2v-3zM3.515 4.929l1.414-1.414L7.05 5.636 5.636 7.05 3.515 4.93zM16.95 18.364l1.414-1.414 2.121 2.121-1.414 1.414-2.121-2.121zm2.121-14.85l1.414 1.415-2.121 2.121-1.414-1.414 2.121-2.121zM5.636 16.95l1.414 1.414-2.121 2.121-1.414-1.414 2.121-2.121zM23 11v2h-3v-2h3zM4 11v2H1v-2h3z"/></svg>',
35
+ dark: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M11.38 2.019a7.5 7.5 0 1 0 10.6 10.6C21.662 17.854 17.316 22 12.001 22 6.477 22 2 17.523 2 12c0-5.315 4.146-9.661 9.38-9.981z"/></svg>',
36
+ system: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm0-2V4a8 8 0 1 0 0 16z"/></svg>'
37
+ // system: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M12 1l9.5 5.5v11L12 23l-9.5-5.5v-11L12 1zm0 2.311L4.5 7.653v8.694l7.5 4.342 7.5-4.342V7.653L12 3.311zM12 16a4 4 0 1 1 0-8 4 4 0 0 1 0 8zm0-2a2 2 0 1 0 0-4 2 2 0 0 0 0 4z"/></svg>'
38
+ }
39
+ var mode = 'system';
40
+ var modes = ['dark','light'];
41
+ var button = document.createElement('a');
42
+
43
+ var cleanupOldPreferences = function() {
44
+ // Clean up earlier `modes` item
45
+ if (storageAvailable('localStorage')) {
46
+ localStorage.removeItem('modes');
47
+ }
48
+ }
49
+
50
+ var loadPreference = function() {
51
+ // read from local storage
52
+ if (storageAvailable('localStorage')) {
53
+ if (localStorage.getItem('mode')) {
54
+ mode = localStorage.getItem('mode');
55
+ } else {
56
+ mode = 'system';
57
+ }
58
+ }
59
+ }
60
+
61
+ var setModeOrder = function() {
62
+ // reverse the modes if dark mode is preferred,
63
+ // or if dark is set in preferences
64
+ if ('matchMedia' in window) {
65
+ if (window.matchMedia('(prefers-color-scheme: dark)').matches && (mode !== 'light')) {
66
+ modes.reverse(); // ['light','dark']
67
+ }
68
+ }
69
+ // system gets tacked onto the end as the last option, always
70
+ modes.push('system');
71
+ }
72
+
73
+ var storePreference = function() {
74
+ // store current mode (default or selected) in local storage
75
+ if (storageAvailable('localStorage')) {
76
+ localStorage.setItem('mode', mode);
77
+ }
78
+ }
79
+
80
+ var setHTMLClass = function() {
81
+ html.classList.replace(mode,modes[0]);
82
+ }
83
+
84
+ var cycleModes = function() {
85
+ mode = modes.shift(); // grab the current mode from the front of the array...
86
+ modes.push(mode); // ...and push it to the end of the array
87
+ }
88
+
89
+ this.switcherButton = function() {
90
+ button.id = 'theme-button';
91
+ button.href = '#null';
92
+ button.title = 'Switch to ' + modes[0] + ' theme';
93
+ button.innerHTML = icons[modes[0]];
94
+ button.addEventListener('click', function(e) {
95
+ e.preventDefault();
96
+ // fix the class list on <html>
97
+ setHTMLClass();
98
+ cycleModes();
99
+ storePreference();
100
+ button.title = 'Switch to ' + modes[0] + ' theme';
101
+ button.innerHTML = icons[modes[0]];
102
+ });
103
+ return button;
104
+ }
105
+
106
+ // Do things on construction
107
+ cleanupOldPreferences();
108
+ loadPreference();
109
+ setModeOrder();
110
+ html.classList.add(mode);
111
+ }
112
+
113
+ var ts = new ThemeSwitch();
114
+ var ts_li = document.createElement('li');
115
+ ts_li.id = 'nav-thm';
116
+ ts_li.appendChild(ts.switcherButton());
117
+ document.querySelector('#quick-nav .nav').appendChild(ts_li);
118
+ }
119
+
120
+
121
+ function storageAvailable(type) {
122
+ try {
123
+ var storage = window[type];
124
+ var x = '__storage_test__';
125
+ storage.setItem(x, x);
126
+ storage.removeItem(x);
127
+ return true;
128
+ }
129
+ catch(e) {
130
+ return false;
131
+ }
132
+ }
133
+
134
+ // Move the nav to the header when there is room
135
+ // Responsive detection
136
+ function responsiveFeature(feature) {
137
+ var size = window
138
+ .getComputedStyle(document.body, ':after')
139
+ .getPropertyValue('content');
140
+ var has_feature = true;
141
+ if(size.indexOf(feature) === -1) {
142
+ has_feature = false;
143
+ }
144
+ return has_feature;
145
+ }
146
+
147
+ function ToggledNav() {
148
+ var nav = document.querySelector('#full-nav .nav');
149
+ var quick_nav = document.querySelector('#quick-nav .nav');
150
+ var full_nav = document.querySelector('#full-nav');
151
+ var thm_btn = document.querySelector('#theme-button')
152
+ var nav_thm;
153
+ var nav_nav;
154
+ var nav_items = [];
155
+ this.toggle = function() {
156
+ if (responsiveFeature('navbar') && !html.classList.contains('navbar')) {
157
+ if (thm_btn) {
158
+ nav_thm = quick_nav.removeChild(document.getElementById('nav-thm'));
159
+ }
160
+ while (nav.firstChild) {
161
+ if (nav.firstChild.tagName) {
162
+ nav_items.push(nav.removeChild(nav.firstChild));
163
+ } else {
164
+ nav.removeChild(nav.firstChild); // remove text nodes
165
+ }
166
+ }
167
+ for (var i = 0; i < nav_items.length; i++) {
168
+ quick_nav.appendChild(nav_items[i]);
169
+ }
170
+ if (thm_btn) {
171
+ quick_nav.appendChild(nav_thm);
172
+ }
173
+ nav_nav = quick_nav.removeChild(document.getElementById('nav-nav'));
174
+ html.classList.add('navbar');
175
+ full_nav.classList.add('hidden');
176
+ }
177
+ if (!responsiveFeature('navbar') && html.classList.contains('navbar')) {
178
+ if (thm_btn) {
179
+ nav_thm = quick_nav.removeChild(document.getElementById('nav-thm'));
180
+ }
181
+ quick_nav.appendChild(nav_nav);
182
+ for (var i = 0; i < nav_items.length; i++) {
183
+ nav.appendChild(nav_items[i]);
184
+ }
185
+ if (thm_btn) {
186
+ quick_nav.appendChild(nav_thm);
187
+ }
188
+ full_nav.classList.remove('hidden');
189
+ html.classList.remove('navbar');
190
+ }
191
+ }
192
+ }
193
+
194
+ var tn = new ToggledNav();
195
+ tn.toggle();
196
+
197
+ window.addEventListener('resize', function() {
198
+ tn.toggle();
199
+ });
200
+
201
+
202
+ // Capture and replicate the current week at the top of the calendar
203
+ if ((document.querySelector('#calendar')) && (document.querySelector('#this-week'))) {
204
+ var this_week = document.querySelector('#this-week').closest('article'); // grab this week's <article>
205
+ var current_week = this_week.cloneNode(true); // make a copy of it,
206
+ this_week.querySelector('#this-week').id = ''; // remove the original #this-week id
207
+ current_week.classList.add('current'); // add a class of current to this week's article copy
208
+ current_week.classList.remove('past'); // remove the past class
209
+ current_week.querySelector('#this-week small').innerText = "This Week";
210
+ document.querySelector('#content').prepend(current_week); // insert the copy at the top of the calendar
211
+ // if (location.hash === '') {
212
+ // location.hash = '#this-week'; // point at the new hash position; viewport should show this one
213
+ // }
214
+ var btn_show_calendar = document.createElement('a');
215
+ btn_show_calendar.id = "btn-show-calendar";
216
+ btn_show_calendar.href = "#null";
217
+ btn_show_calendar.text = "Show Previous Weeks"
218
+ btn_show_calendar.addEventListener('click', function(e) {
219
+ var past_weeks = document.querySelectorAll('article.past');
220
+ for (var week of past_weeks) {
221
+ week.classList.remove('past');
222
+ }
223
+ btn_show_calendar.remove();
224
+ e.preventDefault();
225
+ }
226
+ );
227
+ current_week.insertAdjacentElement('afterend', btn_show_calendar);
228
+ }
229
+
230
+ if ('fetch' in window) {
231
+ var namedDays = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
232
+ var namedMonths = [ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' ];
233
+
234
+ var github_url = (function() {
235
+ var url = document.querySelector('#github').getAttribute('href'); // grab the href value of the repo link
236
+ if (typeof(url) !== 'undefined') {
237
+ var fragment = url.substring(url.indexOf('.com/') + 5); // find the tail end (5 = .com/)
238
+ var branch_regex = /tree/gi;
239
+ var branch_fragment = fragment.replace(branch_regex,'branches'); // replace 'tree' with 'branches'
240
+ // If not dealing with an archival branch (no replacement), append the `main` branch
241
+ if (fragment === branch_fragment) {
242
+ branch_fragment = fragment + '/branches/main';
243
+ }
244
+ return 'https://api.github.com/repos/' + branch_fragment; // return the branch API url
245
+ }
246
+ })();
247
+
248
+ function escapeHTML(str) {
249
+ var div = document.createElement('div');
250
+ div.appendChild(document.createTextNode(str));
251
+ return div.innerHTML;
252
+ }
253
+
254
+ if(typeof(github_url) !== "undefined") {
255
+ fetch(github_url)
256
+ .then(function(response) {
257
+ return response.json();
258
+ })
259
+ .then(function(data) {
260
+ var c = data.commit; // Work only with the commit property of the branch API response
261
+ var commit = {};
262
+ // Lowercase commit message's first word to run in `...to XYZ` copy:
263
+ commit.message = c.commit.message.charAt(0).toLowerCase() + c.commit.message.slice(1);
264
+ // Grab only the first line of a multiline message
265
+ commit.message = commit.message.split("\n\n")[0];
266
+ commit.url = c.html_url;
267
+ commit.stamp = c.commit.author.date;
268
+ commit.date = new Date(commit.stamp);
269
+ // Put the date in Day, Month 31 at <Local Time String> format
270
+ commit.time_string = namedDays[commit.date.getDay()] + ', ' +
271
+ namedMonths[commit.date.getMonth()] + ' ' +
272
+ commit.date.getDate() + ' at ' + commit.date.toLocaleTimeString();
273
+ // Append to footer on calendar
274
+ document.querySelector('#footer p').innerHTML +=
275
+ ' Course last updated on <time datetime="' + commit.stamp + '">' + commit.time_string +
276
+ '</time> to <a id="commit-message" href="' + commit.url + '">' + escapeHTML(commit.message) + '</a>.';
277
+ });
278
+ }
279
+ }
280
+
281
+ window.addEventListener('keyup', function(e) {
282
+ // console.log(e.keyCode);
283
+ // Toggle the visibility of gridlines when `g` is pressed
284
+ if (e.keyCode === 71) {
285
+ document.querySelector('html').classList.toggle('g');
286
+ }
287
+ });
288
+
289
+ html.classList.remove('loading');
@@ -0,0 +1,220 @@
1
+ var VERSION;
2
+ const version = VERSION ? VERSION : mockTenMinuteVersion();
3
+
4
+ // Update with all essential and supporting assets
5
+ // (supporting assets might be things URLs to externally hosted web fonts)
6
+ // There can be NO errors here, or nothing will be cached
7
+ const site_scope = detectSiteScope(location.href);
8
+ const site_offline_path = 'offline/';
9
+ const site_preloaded_assets = {
10
+ essential: [
11
+ 'assets/css/screen.css',
12
+ 'assets/css/print.css',
13
+ 'assets/js/site.js',
14
+ site_offline_path
15
+ ].map(function(asset){
16
+ return site_scope.path + asset;
17
+ }),
18
+ supporting: [].map(function(asset){
19
+ return site_scope.path + asset;
20
+ })
21
+ };
22
+ // Also preload the home page into the cache
23
+ site_preloaded_assets.essential.push(site_scope.path);
24
+
25
+ const site_cache_of = {
26
+ assets: `assets.${version}`,
27
+ pages: `pages`,
28
+ requests: `requests`
29
+ };
30
+
31
+ // prefix scoped values
32
+ if (site_scope.id.length > 0) {
33
+ for (c in site_cache_of) {
34
+ site_cache_of[c] = site_scope.id + '.' + site_cache_of[c];
35
+ }
36
+ }
37
+
38
+
39
+ // For convenience in the activation event, programmatically build a simple array
40
+ // of the names of all the caches listed in the site_caches object literal
41
+ const site_cache_list = [];
42
+ for (let c in site_cache_of) {
43
+ site_cache_list.push(site_cache_of[c]);
44
+ }
45
+
46
+
47
+ // The first step in a ServiceWorker's life cycle is to install it...
48
+ addEventListener('install', function(e) {
49
+ console.log('Preparing to install the service worker...');
50
+ // shorthand for ServiceWorkerGlobalScope.self
51
+ // see https://developer.mozilla.org/en-US/docs/Web/API/WorkerGlobalScope/self
52
+ // documentation for skipWaiting() at
53
+ // https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerGlobalScope/skipWaiting
54
+ self.skipWaiting();
55
+ e.waitUntil(
56
+ caches.open(site_cache_of.assets)
57
+ .then(function(c) {
58
+ // non-essential/nice-to-have assets are added asynchronously
59
+ c.addAll(site_preloaded_assets.supporting);
60
+ // *synchronously* add only for essential assets and fallbacks
61
+ return c.addAll(site_preloaded_assets.essential);
62
+ })
63
+ .catch(function(e) {
64
+ console.error('Caches error:', e);
65
+ })
66
+ );
67
+ // end install event listener
68
+ });
69
+
70
+ // Once the ServiceWorker has been installed, it must be activated. Ordinarily, that will only
71
+ // happen if all tabs and windows open to your site on a user's computer are closed. But the
72
+ // call to skipWaiting() above is more aggressive, and activates the ServiceWorker immediately.
73
+ // The primary tasks of the activate event function is to check all existing caches and delete
74
+ // any that aren't listed in the site_cache_list created above
75
+ addEventListener('activate', function(e) {
76
+ console.log('The service worker is activated!');
77
+ e.waitUntil(
78
+ caches.keys()
79
+ .then(function(existing_caches) {
80
+ // Only work with relevant caches, e.g., those in the same site_scope
81
+ let relevant_caches = existing_caches.filter(function(c) {
82
+ return c.includes(site_scope.id);
83
+ });
84
+ return Promise.all(
85
+ relevant_caches.map(function(relevant_cache) {
86
+ if (!site_cache_list.includes(relevant_cache)) {
87
+ return caches.delete(relevant_cache);
88
+ }
89
+ })
90
+ );
91
+ })
92
+ .then(function(){
93
+ // see https://developer.mozilla.org/en-US/docs/Web/API/Clients/claim
94
+ return clients.claim();
95
+ })
96
+ // end waitUntil
97
+ );
98
+ // end activate event listener
99
+ });
100
+
101
+ // Now we get to the main monkey business: intercepting fetch events and instructing the browser
102
+ // whether to read from a cache, try the network, and so on.
103
+
104
+ addEventListener('fetch', function(fe) {
105
+ // hold onto a copy of the original request
106
+ const request = fe.request;
107
+ // Pages: Try the network first; if it's available, cache and return the page; otherwise, serve
108
+ // from the cache if it exists, or respond with the offline HTML
109
+ if (request.headers.get('Accept').includes('text/html')) {
110
+ fe.respondWith(
111
+ fetch(request)
112
+ .then(function(fetch_response) {
113
+ const copy = fetch_response.clone();
114
+ fe.waitUntil(
115
+ caches.open(site_cache_of.pages)
116
+ .then(function(this_cache) {
117
+ this_cache.put(request,copy);
118
+ })
119
+ );
120
+ return fetch_response;
121
+ })
122
+ .catch(function(error) {
123
+ return caches.match(request)
124
+ .then(function(cached_response) {
125
+ if (cached_response) {
126
+ return cached_response;
127
+ }
128
+ return caches.match(site_scope.path + site_offline_path);
129
+ });
130
+
131
+ })
132
+ // end respondWith
133
+ );
134
+ return;
135
+ }
136
+
137
+ // External requests: Try the cache first; update the cache from the network
138
+ if (!request.url.includes(location.hostname)) {
139
+ fe.respondWith(
140
+ caches.match(request)
141
+ .then(function(cached_response) {
142
+ if (cached_response) {
143
+ fe.waitUntil(
144
+ fetch(request)
145
+ .then(function(fetch_response){
146
+ caches.open(site_cache_of.requests)
147
+ .then(function(this_cache){
148
+ return this_cache.put(request, fetch_response);
149
+ });
150
+ })
151
+ );
152
+ return cached_response;
153
+ }
154
+ return fetch(request)
155
+ .then(function(fetch_response) {
156
+ const copy = fetch_response.clone();
157
+ fe.waitUntil(
158
+ caches.open(site_cache_of.requests)
159
+ .then(function(this_cache) {
160
+ this_cache.put(request, copy);
161
+ })
162
+ );
163
+ return fetch_response;
164
+ });
165
+ })
166
+ // end respondWith
167
+ );
168
+ return;
169
+ }
170
+
171
+ // Everything else: try the cache first, then the network; if the network fails, respond with the
172
+ // offline site path
173
+ fe.respondWith(
174
+ caches.match(request)
175
+ .then(function(cached_response) {
176
+ if (cached_response) {
177
+ return cached_response;
178
+ }
179
+ return fetch(request)
180
+ .catch(function(error) {
181
+ return caches.match(site_scope.path + site_offline_path);
182
+ })
183
+ }
184
+ )
185
+ );
186
+
187
+ // end fetch event listener
188
+ });
189
+
190
+
191
+ // Helper and utility functions
192
+
193
+ function mockTenMinuteVersion() {
194
+ var d = new Date();
195
+ var year = d.getFullYear().toString();
196
+ var month = (d.getMonth() + 1).toString();
197
+ var date = d.getDate().toString();
198
+ var mins = new Date().getMinutes();
199
+ mins = Math.floor(mins/10).toString();
200
+ return year + '.' + zeroPad(month) + '.' + zeroPad(date) + '-' + zeroPad(mins);
201
+ function zeroPad(num,n = 2) {
202
+ num = num.toString();
203
+ while (num.length < n) {
204
+ num = '0' + num;
205
+ }
206
+ return num;
207
+ }
208
+ }
209
+
210
+ function detectSiteScope(url) {
211
+ var scope = {};
212
+ scope.id = url.split('/')[3];
213
+ scope.path = '/';
214
+ if (scope.id.length > 0) {
215
+ scope.path = '/' + scope.id + '/';
216
+ }
217
+ return scope;
218
+ }
219
+
220
+ // console.log(version);