jekyll-theme-open-course 0.0.1 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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);