jekyll-theme-open-course 1.0.1 → 2.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,7 @@
1
+ html {
2
+ font-size: 11pt;
3
+ }
4
+
1
5
  #header,
2
6
  #footer {
3
7
  color: inherit;
@@ -19,14 +23,10 @@ p,li {
19
23
  -webkit-hyphens: none;
20
24
  hyphens: none;
21
25
  }
22
- h2 {
23
- font-family: "moderno-fb-condensed";
24
- font-weight: 700;
25
- }
26
-
27
26
 
28
27
  /* Hide Navigation Stuff */
29
28
 
29
+ #quick-nav,
30
30
  #nav-nav,
31
31
  #full-nav {
32
32
  display: none;
@@ -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";
@@ -1,8 +1,206 @@
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
+ // 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
+
3
201
 
4
202
  // Capture and replicate the current week at the top of the calendar
5
- if (document.querySelector('#calendar')) {
203
+ if ((document.querySelector('#calendar')) && (document.querySelector('#this-week'))) {
6
204
  var this_week = document.querySelector('#this-week').closest('article'); // grab this week's <article>
7
205
  var current_week = this_week.cloneNode(true); // make a copy of it,
8
206
  this_week.querySelector('#this-week').id = ''; // remove the original #this-week id
@@ -10,15 +208,15 @@ if (document.querySelector('#calendar')) {
10
208
  current_week.classList.remove('past'); // remove the past class
11
209
  current_week.querySelector('#this-week small').innerText = "This Week";
12
210
  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
- }
211
+ // if (location.hash === '') {
212
+ // location.hash = '#this-week'; // point at the new hash position; viewport should show this one
213
+ // }
16
214
  var btn_show_calendar = document.createElement('a');
215
+ var past_weeks = document.querySelectorAll('article.past');
17
216
  btn_show_calendar.id = "btn-show-calendar";
18
217
  btn_show_calendar.href = "#null";
19
218
  btn_show_calendar.text = "Show Previous Weeks"
20
219
  btn_show_calendar.addEventListener('click', function(e) {
21
- var past_weeks = document.querySelectorAll('article.past');
22
220
  for (var week of past_weeks) {
23
221
  week.classList.remove('past');
24
222
  }
@@ -26,13 +224,84 @@ if (document.querySelector('#calendar')) {
26
224
  e.preventDefault();
27
225
  }
28
226
  );
29
- current_week.insertAdjacentElement('afterend', btn_show_calendar);
227
+ if (past_weeks.length > 1) {
228
+ current_week.insertAdjacentElement('afterend', btn_show_calendar);
229
+ }
230
+ }
231
+
232
+ if ('fetch' in window) {
233
+ var namedDays = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
234
+ var namedMonths = [ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' ];
235
+
236
+ var github_url = (function() {
237
+ var url = document.querySelector('#github').getAttribute('href'); // grab the href value of the repo link
238
+ if (typeof(url) !== 'undefined') {
239
+ var fragment = url.substring(url.indexOf('.com/') + 5); // find the tail end (5 = .com/)
240
+ return 'https://api.github.com/repos/' + fragment + '/commits?per_page=1'; // return the API url
241
+ }
242
+ })();
243
+
244
+ function escapeHTML(str) {
245
+ var div = document.createElement('div');
246
+ div.appendChild(document.createTextNode(str));
247
+ return div.innerHTML;
248
+ }
249
+
250
+ if(typeof(github_url) !== "undefined") {
251
+ fetch(github_url)
252
+ .then(function(response) {
253
+ return response.json();
254
+ })
255
+ .then(function(data) {
256
+ var commit = {};
257
+ data = data[0]; // only need most recent commit
258
+ // Lowercase commit message's first word to run in `...to XYZ` copy:
259
+ commit.message = data.commit.message.charAt(0).toLowerCase() + data.commit.message.slice(1);
260
+ // Grab only the first line of a multiline message
261
+ commit.message = commit.message.split("\n\n")[0];
262
+ commit.url = data.html_url;
263
+ commit.stamp = data.commit.author.date;
264
+ commit.date = new Date(commit.stamp);
265
+ // Put the date in Day, Month 31 at <Local Time String> format
266
+ commit.time_string = namedDays[commit.date.getDay()] + ', ' +
267
+ namedMonths[commit.date.getMonth()] + ' ' +
268
+ commit.date.getDate() + ' at ' + commit.date.toLocaleTimeString();
269
+ // Append to footer on calendar
270
+ document.querySelector('#footer p').innerHTML +=
271
+ ' Course last updated on <time datetime="' + commit.stamp + '">' + commit.time_string +
272
+ '</time> to <a id="commit-message" href="' + commit.url + '">' + escapeHTML(commit.message) + '</a>.';
273
+ });
274
+ }
30
275
  }
31
276
 
277
+ // This is 10,000 kinds of ugly and bad, but it gets the job done...for now.
278
+ var course_levels = ['grad','ugrad','all'];
279
+ var title = document.querySelector('title');
280
+ var title_components = {};
281
+ title_components.original = title.innerText;
282
+ title_components.name = title_components.original.split(':')[1] // Web Real-Time Communications
283
+ title_components.full = title_components.original.split(':')[0]; // ITMD 469/545
284
+ title_components.code = title_components.full.split(' ')[0]; // ITMD
285
+ title_components.all = title_components.full.split(' ')[1]; // 469/545
286
+ title_components.ugrad = title_components.all.split('/')[0]; // 469
287
+ title_components.grad = title_components.all.split('/')[1]; // 545
288
+
289
+ document.querySelector('#footer').addEventListener('dblclick', function(e) {
290
+ // Adjust the <title> contents
291
+ title.innerText = title_components.code + ' ' + title_components[course_levels[0]] + ': ' + title_components.name;
292
+ // Switch up the ancestor level class
293
+ html.classList.remove(course_levels[course_levels.length - 1]);
294
+ html.classList.add(course_levels[0]);
295
+ // Push the current level to the end of the array
296
+ course_levels.push(course_levels.shift());
297
+ });
298
+
32
299
  window.addEventListener('keyup', function(e) {
33
300
  // console.log(e.keyCode);
34
301
  // Toggle the visibility of gridlines when `g` is pressed
35
302
  if (e.keyCode === 71) {
36
303
  document.querySelector('html').classList.toggle('g');
37
304
  }
38
- })
305
+ });
306
+
307
+ 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);