jekyll-theme-open-course 1.0.1 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ee7859ed93d15688c53305eeace80e49bdd6ce3b9228915f87e6ebdf5c97ffb7
4
- data.tar.gz: cffa0b043adf2bace48b4f1000bfd08445904f0435e0127a9364fd3db6a72172
3
+ metadata.gz: 7c44228f16269009a36ed91cb93d45493edda39485bce1bcb768a07a22945fb2
4
+ data.tar.gz: 74ee9d5d5fe34e59b200b6d865c764faa1c55cea6b2f940661ecc8ee7cf566f5
5
5
  SHA512:
6
- metadata.gz: 5b6c5e2d47909a8303b7877b2eca4538c33d470737561b1ee872b8bbe26a602637a5bc8acdc2a345e6e87599bfcea01ede52b250fb49410739fbc96486818a93
7
- data.tar.gz: f020b4d1c8ceac52fd6dba769f163f2e1062bc03a90d69bd43b569eeb9ac38ed06ef2aae98caacd389aa530f40988712777e3dedbaeb125a7ba40daab9b92e90
6
+ metadata.gz: 7aebe324212b4fc8513f218d6298c05a8ea3f0a67f8941a58af242d81aa5169803668f0749d7bd254c6ac18e3828a4e91ccd227f24bbc297c8425046b126c48a
7
+ data.tar.gz: 29acc9e346bd4d78206878b98394d63b7474c12c1302f2d38da50559bc1e67d2cf02d5092e808db984df4e88863eda618285c53c8d50ac2aea2638b41463a637
data/_config.yml CHANGED
@@ -39,6 +39,8 @@ course:
39
39
  - label: Policies
40
40
  href: "policies/"
41
41
  id: pol
42
+ css:
43
+ font-url: "https://use.typekit.net/pig5ein.css"
42
44
 
43
45
  collections_dir: syllabus
44
46
 
@@ -26,6 +26,7 @@ layout: default
26
26
  </h2>
27
27
  <small>Week&nbsp;{{ util.spelled_nums[week_int] }}</small>
28
28
  </header>
29
+ <div class="agendas">
29
30
  {% for meeting in week.meetings %}
30
31
  <section class="agenda">
31
32
  {% if site.course.days.size > 0 %}
@@ -43,7 +44,25 @@ layout: default
43
44
  </ol>
44
45
  </section>
45
46
  {% endfor %}
46
- {% if week.readings or week.tasks %}
47
+ </div>
48
+ {% assign be = entry.begins | date: "%s" %}
49
+ {% assign ex = entry.expires | date: "%s" %}
50
+ {% assign deliverables_li = "" %}
51
+
52
+ {%- capture deliverables_li -%}
53
+ {%- for project in projects -%}
54
+ {%- assign proj_int = forloop.index | times: 1 -%}
55
+ {%- capture proj_num -%}{%- if proj_int < 10 -%}0{%- endif -%}{{ proj_int }}{%- endcapture -%}
56
+ {%- for deliverable in project.deliverables -%}
57
+ {%- assign dl = deliverable.deadline | date: "%s" -%}
58
+ {%- if dl >= be and dl <= ex -%}
59
+ <li class="deadline">Project {{ util.spelled_nums[proj_int] }}: {{ deliverable.action | markdownify | remove: "<p>" | remove: "</p>" | strip_newlines }} (Due by {{ deliverable.deadline | date: "%A, %B %-e" }})</li>
60
+ {%- endif -%}
61
+ {%- endfor -%}
62
+ {%- endfor -%}
63
+ {%- endcapture -%}
64
+
65
+ {% if week.readings or week.tasks or deliverables_li.size > 1%}
47
66
  <aside class="assigned">
48
67
  <header>
49
68
  <h3>Assigned Work</h3>
@@ -70,23 +89,6 @@ layout: default
70
89
  </section>
71
90
  {% endif %}
72
91
 
73
- {% assign be = entry.begins | date: "%s" %}
74
- {% assign ex = entry.expires | date: "%s" %}
75
- {% assign deliverables_li = "" %}
76
-
77
- {%- capture deliverables_li -%}
78
- {%- for project in projects -%}
79
- {%- assign proj_int = forloop.index | times: 1 -%}
80
- {%- capture proj_num -%}{%- if proj_int < 10 -%}0{%- endif -%}{{ proj_int }}{%- endcapture -%}
81
- {%- for deliverable in project.deliverables -%}
82
- {%- assign dl = deliverable.deadline | date: "%s" -%}
83
- {%- if dl >= be and dl <= ex -%}
84
- <li class="deadline">Project {{ util.spelled_nums[proj_int] }}: {{ deliverable.action }} (Due by {{ deliverable.deadline | date: "%A, %B %-e" }})</li>
85
- {%- endif -%}
86
- {%- endfor -%}
87
- {%- endfor -%}
88
- {%- endcapture -%}
89
-
90
92
  {% if deliverables_li.size > 1 %}
91
93
  <!-- {{ deliverables_li.size }} -->
92
94
  <section class="deadlines">
@@ -1,5 +1,5 @@
1
1
  <!DOCTYPE html>
2
- <html lang="en" id="{{page.id}}" prefix="og: http://ogp.me/ns#">
2
+ <html lang="en" id="{{page.id}}" prefix="og: http://ogp.me/ns#" class="loading">
3
3
  <head>{% assign course = site.course %}{% assign instructor = site.course.instructor %}
4
4
  <meta charset="utf-8" />
5
5
  <title>{{ course.number }}: {% if page.title %}{{ page.title}}{% else %}{{ course.title }}{% endif %}</title>
@@ -25,8 +25,8 @@
25
25
  {{ course.term }}.
26
26
  </p>
27
27
  <nav id="quick-nav">
28
+ <h3 id="nav-con"><a href="#content"><span class="access">Jump to</span> {{ page.title }}</a></h3>
28
29
  <ul class="nav">
29
- <li id="nav-con"><a href="#content"><span class="access">Jump to</span> {{ page.title }}</a></li>
30
30
  <li id="nav-nav"><a href="#full-nav"><span class="access">Jump to</span> Menu</a></li>
31
31
  </ul>
32
32
  </nav>
@@ -35,28 +35,26 @@
35
35
  {{ content }}
36
36
  </main>
37
37
 
38
+ <aside id="instructor">
39
+ <header>
40
+ <h3>Instructor</h3>
41
+ </header>
42
+ <ul>
43
+ <li>
44
+ <a href="{{ instructor.url }}">{{ instructor.honorific }} {{ instructor.name }}</a>,
45
+ {{ site.course.instructor.full_title | escape }}
46
+ </li>
47
+ <li><a href="mailto:{{ instructor.email }}">{{ instructor.email }}</a></li>
48
+ <li id="office-hours">
49
+ {{ instructor.office_hours }}
50
+ </li>
51
+ </ul>
52
+ </aside>
38
53
 
39
54
 
40
55
  <footer id="footer">
41
- <section id="instructor">
42
- <header>
43
- <h3>Instructor</h3>
44
- </header>
45
- <ul>
46
- <li>
47
- <a href="{{ instructor.url }}">{{ instructor.honorific }} {{ instructor.name }}</a>,
48
- {{ site.course.instructor.full_title | escape }}
49
- </li>
50
- <li><a href="mailto:{{ instructor.email }}">{{ instructor.email }}</a></li>
51
- <li id="office-hours">
52
- {{ instructor.office_hours }}
53
- </li>
54
- </ul>
55
- </section>
56
56
  <nav id="full-nav">
57
- <header>
58
- <h3>Navigation</h3>
59
- </header>
57
+ <h3>Navigation</h3>
60
58
  <ul class="nav">
61
59
  {% for nav in course.navigation %}
62
60
  {% assign protocol = nav.href | slice: 0, 4 %}
@@ -70,9 +68,7 @@
70
68
  </ul>
71
69
  </nav>
72
70
  <section id="colophon">
73
- <header>
74
- <h3>Colophon</h3>
75
- </header>
71
+ <h3>Colophon</h3>
76
72
  <p class="fineprint">
77
73
  Course syllabus by <a href="{{ instructor.url }}">{{ instructor.name }}</a>.
78
74
  Licensed under
@@ -0,0 +1,10 @@
1
+ ---
2
+ layout: default
3
+ ---
4
+
5
+ <article class="error">
6
+ <header>
7
+ <h2>{{ page.title }}</h2>
8
+ </header>
9
+ {{ content }}
10
+ </article>
@@ -19,22 +19,12 @@ layout: default
19
19
  <small>Due by {{ project.due_date | date: "%A, %B %-e, %Y"}}</small>
20
20
  </header>
21
21
 
22
- <section class="description">
22
+ <section class="description {% if project.preview %}preview{% endif %}">
23
23
  <h3>Project Description</h3>
24
24
  {{ project.content }}
25
25
  </section>
26
26
 
27
27
  {% unless project.preview %}
28
- {% if project.goals %}
29
- <section class="goals">
30
- <h3>Project Goals</h3>
31
- <ul>
32
- {% for goal in project.goals %}
33
- <li>{{ goal | markdownify | remove: "<p>" | remove: "</p>" | strip_newlines }}</li>
34
- {% endfor %}
35
- </ul>
36
- </section>
37
- {% endif %}
38
28
  {% if project.deliverables %}
39
29
  <section class="deliverables">
40
30
  <h3>Deliverables and Deadlines</h3>
@@ -66,6 +56,16 @@ layout: default
66
56
  </ol>
67
57
  </section>
68
58
  {% endif %}
59
+ {% if project.goals %}
60
+ <section class="goals">
61
+ <h3>Project Goals</h3>
62
+ <ul>
63
+ {% for goal in project.goals %}
64
+ <li>{{ goal | markdownify | remove: "<p>" | remove: "</p>" | strip_newlines }}</li>
65
+ {% endfor %}
66
+ </ul>
67
+ </section>
68
+ {% endif %}
69
69
  {% endunless %}
70
70
  </article>
71
71
  {% endfor %}
@@ -1,5 +1,7 @@
1
1
  ---
2
2
  ---
3
+ @import url("{{site.course.css.font-url}}");
4
+
3
5
  /* http://meyerweb.com/eric/tools/css/reset/
4
6
  v2.0 | 20110126
5
7
  License: none (public domain)
@@ -9,4 +11,12 @@
9
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}
10
12
  /* stylelint-enable */
11
13
 
14
+ html.loading,
15
+ html.loading * {
16
+ transition: none !important;
17
+ }
18
+
19
+ @import "fonts";
20
+ @import "colors";
12
21
  @import "typography";
22
+ @import "base";
data/assets/js/site.js CHANGED
@@ -1,5 +1,152 @@
1
1
  // Add a .js utility class to <html>
2
- document.querySelector('html').classList.add('js');
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
+ function themeSwitcher() {
29
+ // Exit fast if no CSS properties support
30
+ if (!('supports' in CSS && CSS.supports("(--foo: bar)"))) {
31
+ return;
32
+ }
33
+
34
+ var header = document.querySelector('#header h1');
35
+ var toggle = document.createElement('a');
36
+ var html = document.querySelector('html');
37
+ // Icons from https://remixicon.com/
38
+ var icons = {
39
+ 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>',
40
+ 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>'
41
+ }
42
+
43
+ var dark_mode = false;
44
+ var modes = ['light','dark'];
45
+
46
+ if ('matchMedia' in window) {
47
+ dark_mode = window.matchMedia('(prefers-color-scheme: dark)').matches;
48
+ if (dark_mode) {
49
+ modes.reverse(); // ['dark','light']
50
+ }
51
+ }
52
+
53
+ if (storageAvailable('localStorage')) {
54
+ if (!localStorage.getItem('modes')) {
55
+ localStorage.setItem('modes',modes.join(','));
56
+ } else {
57
+ modes = localStorage.getItem('modes').split(',');
58
+ }
59
+ }
60
+
61
+ html.classList.add(modes[0]);
62
+
63
+ toggle.id = 'theme-toggle';
64
+ toggle.href = '#null';
65
+ toggle.title = 'Switch to ' + modes[1] + ' theme';
66
+ toggle.innerHTML = icons[modes[1]];
67
+ header.appendChild(toggle);
68
+
69
+ toggle.addEventListener('click', function(e) {
70
+ e.preventDefault();
71
+ html.classList.replace(modes[0],modes[1]);
72
+ modes.reverse();
73
+ if (storageAvailable('localStorage')) {
74
+ localStorage.setItem('modes',modes.join(','));
75
+ }
76
+ toggle.title = 'Switch to ' + modes[1] + ' theme';
77
+ toggle.innerHTML = icons[modes[1]];
78
+ });
79
+
80
+ function storageAvailable(type) {
81
+ try {
82
+ var storage = window[type];
83
+ var x = '__storage_test__';
84
+ storage.setItem(x, x);
85
+ storage.removeItem(x);
86
+ return true;
87
+ }
88
+ catch(e) {
89
+ return false;
90
+ }
91
+ }
92
+ }
93
+
94
+ themeSwitcher();
95
+
96
+ // Move the nav to the header when there is room
97
+ // Responsive detection
98
+ function responsiveFeature(feature) {
99
+ var size = window
100
+ .getComputedStyle(document.body, ':after')
101
+ .getPropertyValue('content');
102
+ var has_feature = true;
103
+ if(size.indexOf(feature) === -1) {
104
+ has_feature = false;
105
+ }
106
+ return has_feature;
107
+ }
108
+
109
+ function ToggledNav() {
110
+ this.nav = document.querySelector('#full-nav .nav');
111
+ this.quick_nav = document.querySelector('#quick-nav .nav');
112
+ this.full_nav = document.querySelector('#full-nav');
113
+ this.nav_nav;
114
+ this.nav_items = [];
115
+ this.toggle = function() {
116
+ if (responsiveFeature('navbar') && !html.classList.contains('navbar')) {
117
+ while (this.nav.firstChild) {
118
+ if (this.nav.firstChild.tagName) {
119
+ this.nav_items.push(this.nav.removeChild(this.nav.firstChild));
120
+ } else {
121
+ this.nav.removeChild(this.nav.firstChild); // remove text nodes
122
+ }
123
+ }
124
+ for (var i = 0; i < this.nav_items.length; i++) {
125
+ this.quick_nav.appendChild(this.nav_items[i]);
126
+ }
127
+ this.nav_nav = this.quick_nav.removeChild(document.getElementById('nav-nav'));
128
+ html.classList.add('navbar');
129
+ this.full_nav.classList.add('hidden');
130
+ }
131
+ if (!responsiveFeature('navbar') && html.classList.contains('navbar')) {
132
+ console.log('nav-nav node name:', this.nav_nav.nodeName);
133
+ this.quick_nav.appendChild(this.nav_nav);
134
+ for (var i = 0; i < this.nav_items.length; i++) {
135
+ this.nav.appendChild(this.nav_items[i]);
136
+ }
137
+ this.full_nav.classList.remove('hidden');
138
+ html.classList.remove('navbar');
139
+ }
140
+ }
141
+ }
142
+
143
+ var tn = new ToggledNav();
144
+ tn.toggle();
145
+
146
+ window.addEventListener('resize', function() {
147
+ tn.toggle();
148
+ });
149
+
3
150
 
4
151
  // Capture and replicate the current week at the top of the calendar
5
152
  if (document.querySelector('#calendar')) {
@@ -10,9 +157,9 @@ if (document.querySelector('#calendar')) {
10
157
  current_week.classList.remove('past'); // remove the past class
11
158
  current_week.querySelector('#this-week small').innerText = "This Week";
12
159
  document.querySelector('#content').prepend(current_week); // insert the copy at the top of the calendar
13
- if (location.hash === '') {
14
- location.hash = '#this-week'; // point at the new hash position; viewport should show this one
15
- }
160
+ // if (location.hash === '') {
161
+ // location.hash = '#this-week'; // point at the new hash position; viewport should show this one
162
+ // }
16
163
  var btn_show_calendar = document.createElement('a');
17
164
  btn_show_calendar.id = "btn-show-calendar";
18
165
  btn_show_calendar.href = "#null";
@@ -29,10 +176,55 @@ if (document.querySelector('#calendar')) {
29
176
  current_week.insertAdjacentElement('afterend', btn_show_calendar);
30
177
  }
31
178
 
179
+ if ('fetch' in window) {
180
+ var namedDays = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
181
+ var namedMonths = [ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' ];
182
+
183
+ var github_url = (function() {
184
+ var url = document.querySelector('#github').getAttribute('href'); // grab the href value of the repo link
185
+ if (typeof(url) !== 'undefined') {
186
+ var fragment = url.substring(url.indexOf('.com/') + 5); // find the tail end (5 = .com/)
187
+ return 'https://api.github.com/repos/' + fragment + '/commits?per_page=1'; // return the API url
188
+ }
189
+ })();
190
+
191
+ function escapeHTML(str) {
192
+ var div = document.createElement('div');
193
+ div.appendChild(document.createTextNode(str));
194
+ return div.innerHTML;
195
+ }
196
+
197
+ if(typeof(github_url) !== "undefined") {
198
+ fetch(github_url)
199
+ .then(function(response) {
200
+ return response.json();
201
+ })
202
+ .then(function(data) {
203
+ var commit = {};
204
+ data = data[0]; // only need most recent commit
205
+ // Lowercase commit message's first word to run in `...to XYZ` copy:
206
+ commit.message = data.commit.message.charAt(0).toLowerCase() + data.commit.message.slice(1);
207
+ commit.url = data.html_url;
208
+ commit.stamp = data.commit.author.date;
209
+ commit.date = new Date(commit.stamp);
210
+ // Put the date in Day, Month 31 at <Local Time String> format
211
+ commit.time_string = namedDays[commit.date.getDay()] + ', ' +
212
+ namedMonths[commit.date.getMonth()] + ' ' +
213
+ commit.date.getDate() + ' at ' + commit.date.toLocaleTimeString();
214
+ // Append to footer on calendar
215
+ document.querySelector('#footer p').innerHTML +=
216
+ ' Course last updated on <time datetime="' + commit.stamp + '">' + commit.time_string +
217
+ '</time> to <a id="commit-message" href="' + commit.url + '">' + escapeHTML(commit.message) + '</a>.';
218
+ });
219
+ }
220
+ }
221
+
32
222
  window.addEventListener('keyup', function(e) {
33
223
  // console.log(e.keyCode);
34
224
  // Toggle the visibility of gridlines when `g` is pressed
35
225
  if (e.keyCode === 71) {
36
226
  document.querySelector('html').classList.toggle('g');
37
227
  }
38
- })
228
+ });
229
+
230
+ html.classList.remove('loading');
data/assets/js/sw.js ADDED
@@ -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);
data/lib/jtoc.rb CHANGED
@@ -3,7 +3,7 @@ module JTOpenCourse
3
3
  require 'erb'
4
4
  require 'date'
5
5
 
6
- VERSION = "1.0.1"
6
+ VERSION = "1.1.0"
7
7
 
8
8
  SPELLED_NUMS = %w(
9
9
  Zero One Two Three Four Five Six Seven Eight Nine Ten Eleven Twelve Thirteen Fourteen Fifteen
@@ -39,6 +39,8 @@ course:
39
39
  - label: Policies
40
40
  href: "policies/"
41
41
  id: pol
42
+ css:
43
+ font-url: "https://use.typekit.net/pig5ein.css"
42
44
 
43
45
  collections_dir: syllabus
44
46
 
@@ -0,0 +1,7 @@
1
+ ---
2
+ title: Site Offline
3
+ layout: error
4
+ ---
5
+
6
+ Well this is embarrassing. Either there’s something the matter with your internet connection, or the
7
+ server where this course site is hosted. Best to just try again later.
@@ -2,12 +2,16 @@
2
2
  title: Books
3
3
  ---
4
4
 
5
- <h4>Required</h4>
5
+ <section class="required" markdown="1">
6
+ <h3>Required</h3>
6
7
 
7
8
  - Required book one
8
9
  - Required book two
10
+ </section>
9
11
 
10
- <h4>Recommended</h4>
12
+ <section class="recommended" markdown="1">
13
+ <h3>Recommended</h3>
11
14
 
12
15
  - Recommended book one
13
16
  - Recommended book two
17
+ </section>
@@ -2,12 +2,16 @@
2
2
  title: Materials
3
3
  ---
4
4
 
5
- <h4>Required</h4>
5
+ <section class="required" markdown="1">
6
+ <h3>Required</h3>
6
7
 
7
8
  - Required material one
8
9
  - Required material two
10
+ </section>
9
11
 
10
- <h4>Recommended</h4>
12
+ <section class="recommended" markdown="1">
13
+ <h3>Recommended</h3>
11
14
 
12
15
  - Recommended material one
13
16
  - Recommended material two
17
+ </section>
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jekyll-theme-open-course
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Karl Stolley
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-03-06 00:00:00.000000000 Z
11
+ date: 2020-04-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: jekyll
@@ -67,12 +67,14 @@ files:
67
67
  - _data/utility.yml
68
68
  - _layouts/calendar.html
69
69
  - _layouts/default.html
70
+ - _layouts/error.html
70
71
  - _layouts/policies.html
71
72
  - _layouts/projects.html
72
73
  - assets/css/print.css
73
74
  - assets/css/screen.scss
74
75
  - assets/img/te.png
75
76
  - assets/js/site.js
77
+ - assets/js/sw.js
76
78
  - exe/jtoc
77
79
  - index.md
78
80
  - lib/jtoc.rb
@@ -80,6 +82,7 @@ files:
80
82
  - lib/starter_files/_data/calendar.yml.erb
81
83
  - lib/starter_files/_data/utility.yml
82
84
  - lib/starter_files/index.md.erb
85
+ - lib/starter_files/offline/index.md
83
86
  - lib/starter_files/syllabus/_policies/academic-integrity.md
84
87
  - lib/starter_files/syllabus/_policies/assignment-submission.md
85
88
  - lib/starter_files/syllabus/_policies/books.md