├── .gitignore
├── .gitpod.yml
├── LICENSE
├── README.md
├── babel.config.js
├── netlify.toml
├── package-lock.json
├── package.json
├── public
├── favicon.ico
├── img
│ └── icons
│ │ ├── android-chrome-192x192.png
│ │ ├── android-chrome-512x512.png
│ │ ├── apple-touch-icon-152x152.png
│ │ ├── apple-touch-icon.png
│ │ ├── favicon-16x16.png
│ │ ├── favicon-32x32.png
│ │ ├── msapplication-icon-144x144.png
│ │ ├── mstile-150x150.png
│ │ └── safari-pinned-tab.svg
├── index.html
├── manifest.json
├── robots.txt
└── tones
│ ├── end.mp3
│ └── start.mp3
├── src
├── App.vue
├── components
│ ├── LinkSetting.vue
│ ├── PeriodSetting.vue
│ ├── ScheduleDay.vue
│ └── SchedulePeriod.vue
├── main.js
├── material-icons.css
├── plugins
│ └── vuetify.js
├── registerServiceWorker.js
├── router.js
├── sass
│ └── main.scss
├── scripts
│ └── sun.js
└── views
│ └── Home.vue
├── vue.config.js
└── yarn.lock
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | /dist
4 | .netlify
5 | .quasar
6 |
7 | # local env files
8 | .env.local
9 | .env.*.local
10 |
11 | # Log files
12 | npm-debug.log*
13 | yarn-debug.log*
14 | yarn-error.log*
15 |
16 | # Editor directories and files
17 | .idea
18 | .vscode
19 | *.suo
20 | *.ntvs*
21 | *.njsproj
22 | *.sln
23 | *.sw?
24 | .sentryclirc
25 |
26 | start_dev.sh
--------------------------------------------------------------------------------
/.gitpod.yml:
--------------------------------------------------------------------------------
1 | tasks:
2 | - init: npm install
3 | command: npm run serve
4 | vscode:
5 | extensions:
6 | - octref.vetur@0.24.0:FtNScOJHzSTxcNY6NB60Cg==
7 | - christian-kohler.npm-intellisense@1.2.1:vghMIRDy8/ygZPJWq/9oEQ==
8 | - dbaeumer.vscode-eslint@2.1.8:02aHhbJ0Q4aGdjHXlTdVKg==
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Bowen Yin
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Harker Bell Schedule
2 | 
3 | [](https://app.netlify.com/sites/harker-bell/deploys)
4 | [](https://app.fossa.com/projects/git%2Bgithub.com%2FHarkerDev%2Fharker-bell?ref=badge_small)
5 |
6 | #### [View Live Site](https://bell.harker.org)
7 | #### [View Changelog](https://github.com/HarkerDev/harker-bell/releases)
8 | #### [View Documentation](https://bell.harker.org/docs)
9 |
10 |
11 |
12 |
13 |
14 | ### License
15 | [](https://app.fossa.com/projects/git%2Bgithub.com%2FHarkerDev%2Fharker-bell?ref=badge_large)
16 | 
17 |
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | "@vue/cli-plugin-babel/preset"
4 | ]
5 | };
--------------------------------------------------------------------------------
/netlify.toml:
--------------------------------------------------------------------------------
1 | [[redirects]]
2 | from = "/p/*"
3 | to = ":splat"
4 | status = 200
5 | headers = {Access-Control-Allow-Origin = "https://bell.harker.org"}
6 | [[redirects]]
7 | from = "/pgtm/*"
8 | to = "https://www.googletagmanager.com/gtm.js:splat"
9 | status = 200
10 | headers = {Access-Control-Allow-Origin = "https://bell.harker.org"}
11 | [[redirects]]
12 | from = "/submitevent"
13 | to = "https://docs.google.com/forms/d/e/1FAIpQLSeWUzPcr079_ZhXHlRcwKwsoMjJL5O_ZsWVulzMjqVxHOb7Fg/viewform"
14 | status = 302
15 | [[redirects]]
16 | from = "/docs/*"
17 | to = "https://harker-bell-docs.netlify.com/:splat"
18 | status = 200
19 | [[redirects]]
20 | from = "/admin/*"
21 | to = "https://harker-bell-admin.netlify.com/:splat"
22 | status = 200
23 | [[redirects]]
24 | from = "/api/*"
25 | to = "https://bell.dev.harker.org/api/:splat"
26 | status = 200
27 | [[redirects]]
28 | from = "/*"
29 | to = "/index.html"
30 | status = 200
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "harker-bell",
3 | "version": "2.6.1",
4 | "private": true,
5 | "author": "HarkerDev",
6 | "scripts": {
7 | "serve": "vue-cli-service serve",
8 | "build": "vue-cli-service build --modern",
9 | "lint": "vue-cli-service lint"
10 | },
11 | "dependencies": {
12 | "@sentry/browser": "^5.22.3",
13 | "@sentry/integrations": "^5.22.3",
14 | "autotrack": "^2.4.1",
15 | "core-js": "^3.6.5",
16 | "idb": "^4.0.5",
17 | "register-service-worker": "^1.7.1",
18 | "semver": "^5.7.0",
19 | "socket.io-client": "^2.3.0",
20 | "vue": "^2.6.12",
21 | "vue-content-loader": "^0.2.3",
22 | "vue-router": "^3.4.3",
23 | "vuetify": "^2.2.34",
24 | "web-vitals": "^0.2.4"
25 | },
26 | "devDependencies": {
27 | "@vue/cli-plugin-babel": "^4.5.12",
28 | "@vue/cli-plugin-eslint": "^4.5.4",
29 | "@vue/cli-plugin-pwa": "^4.5.4",
30 | "@vue/cli-service": "^4.1.1",
31 | "babel-eslint": "^10.1.0",
32 | "deepmerge": "^3.3.0",
33 | "eslint": "^5.16.0",
34 | "eslint-plugin-vue": "^5.2.3",
35 | "fibers": "^4.0.3",
36 | "sass": "^1.26.10",
37 | "sass-loader": "^7.3.1",
38 | "vue-cli-plugin-vuetify": "^2.0.7",
39 | "vue-template-compiler": "^2.6.12",
40 | "vuetify-loader": "^1.6.0"
41 | },
42 | "eslintConfig": {
43 | "root": true,
44 | "env": {
45 | "node": true
46 | },
47 | "extends": [
48 | "plugin:vue/strongly-recommended",
49 | "eslint:recommended"
50 | ],
51 | "parserOptions": {
52 | "parser": "babel-eslint"
53 | },
54 | "rules": {
55 | "no-fallthrough": "off",
56 | "no-console": 1,
57 | "vue/attributes-order": 1,
58 | "vue/no-confusing-v-for-v-if": 1,
59 | "vue/no-v-html": 1,
60 | "vue/order-in-components": 1,
61 | "vue/this-in-template": 1,
62 | "vue/array-bracket-spacing": 1,
63 | "vue/arrow-spacing": 1,
64 | "vue/block-spacing": 1,
65 | "vue/brace-style": 1,
66 | "vue/camelcase": 1,
67 | "vue/comma-dangle": 1,
68 | "vue/component-name-in-template-casing": [
69 | 1,
70 | "kebab-case"
71 | ],
72 | "vue/key-spacing": 1,
73 | "vue/match-component-file-name": 1,
74 | "vue/no-boolean-default": 1,
75 | "vue/no-restricted-syntax": 1,
76 | "vue/object-curly-spacing": 1,
77 | "vue/require-direct-export": 1,
78 | "vue/script-indent": 1,
79 | "vue/space-unary-ops": 1,
80 | "vue/v-on-function-call": 1,
81 | "vue/max-attributes-per-line": 0,
82 | "vue/html-closing-bracket-spacing": [
83 | "error",
84 | {
85 | "selfClosingTag": 0
86 | }
87 | ],
88 | "vue/html-self-closing": [
89 | "error",
90 | {
91 | "html": {
92 | "normal": "never",
93 | "component": "never"
94 | }
95 | }
96 | ],
97 | "vue/singleline-html-element-content-newline": 0,
98 | "vue/mustache-interpolation-spacing": [
99 | "warning",
100 | "never"
101 | ]
102 | }
103 | },
104 | "postcss": {
105 | "plugins": {
106 | "autoprefixer": {}
107 | }
108 | },
109 | "browserslist": [
110 | "> 1% in US",
111 | "last 2 versions",
112 | "not dead",
113 | "not IE <= 11"
114 | ],
115 | "bugs": {
116 | "url": "https://github.com/HarkerDev/harker-bell/issues"
117 | },
118 | "homepage": "https://github.com/HarkerDev/harker-bell#readme",
119 | "license": "MIT",
120 | "repository": {
121 | "type": "git",
122 | "url": "git+https://github.com/HarkerDev/harker-bell"
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HarkerDev/harker-bell/06cd8a61e6126e32f4ea6f7a7cbd95d9bca4946d/public/favicon.ico
--------------------------------------------------------------------------------
/public/img/icons/android-chrome-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HarkerDev/harker-bell/06cd8a61e6126e32f4ea6f7a7cbd95d9bca4946d/public/img/icons/android-chrome-192x192.png
--------------------------------------------------------------------------------
/public/img/icons/android-chrome-512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HarkerDev/harker-bell/06cd8a61e6126e32f4ea6f7a7cbd95d9bca4946d/public/img/icons/android-chrome-512x512.png
--------------------------------------------------------------------------------
/public/img/icons/apple-touch-icon-152x152.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HarkerDev/harker-bell/06cd8a61e6126e32f4ea6f7a7cbd95d9bca4946d/public/img/icons/apple-touch-icon-152x152.png
--------------------------------------------------------------------------------
/public/img/icons/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HarkerDev/harker-bell/06cd8a61e6126e32f4ea6f7a7cbd95d9bca4946d/public/img/icons/apple-touch-icon.png
--------------------------------------------------------------------------------
/public/img/icons/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HarkerDev/harker-bell/06cd8a61e6126e32f4ea6f7a7cbd95d9bca4946d/public/img/icons/favicon-16x16.png
--------------------------------------------------------------------------------
/public/img/icons/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HarkerDev/harker-bell/06cd8a61e6126e32f4ea6f7a7cbd95d9bca4946d/public/img/icons/favicon-32x32.png
--------------------------------------------------------------------------------
/public/img/icons/msapplication-icon-144x144.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HarkerDev/harker-bell/06cd8a61e6126e32f4ea6f7a7cbd95d9bca4946d/public/img/icons/msapplication-icon-144x144.png
--------------------------------------------------------------------------------
/public/img/icons/mstile-150x150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HarkerDev/harker-bell/06cd8a61e6126e32f4ea6f7a7cbd95d9bca4946d/public/img/icons/mstile-150x150.png
--------------------------------------------------------------------------------
/public/img/icons/safari-pinned-tab.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | Harker Bell Schedule - HarkerDev
13 |
14 |
15 |
16 |
20 |
21 |
22 |
23 |
24 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Harker Bell Schedule",
3 | "short_name": "Harker Bell",
4 | "icons": [
5 | {
6 | "src": "./img/icons/android-chrome-192x192.png",
7 | "sizes": "192x192",
8 | "type": "image/png"
9 | },
10 | {
11 | "src": "./img/icons/android-chrome-512x512.png",
12 | "sizes": "512x512",
13 | "type": "image/png"
14 | }
15 | ],
16 | "start_url": "./?utm_source=a2hs&utm_medium=a2hs",
17 | "display": "standalone",
18 | "background_color": "#FFFFFF",
19 | "theme_color": "#005841"
20 | }
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | User-agent: *
2 | Disallow:
3 |
--------------------------------------------------------------------------------
/public/tones/end.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HarkerDev/harker-bell/06cd8a61e6126e32f4ea6f7a7cbd95d9bca4946d/public/tones/end.mp3
--------------------------------------------------------------------------------
/public/tones/start.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HarkerDev/harker-bell/06cd8a61e6126e32f4ea6f7a7cbd95d9bca4946d/public/tones/start.mp3
--------------------------------------------------------------------------------
/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
10 |
11 |
12 |
13 |
14 | event
15 |
16 |
17 |
20 |
21 | {}); datePicker = false;">
22 | Today
23 |
24 |
25 |
26 |
27 |
28 |
29 |
32 |
33 |
34 |
37 |
38 | {{ shortMonths[calendar.dates[0].getUTCMonth()] }} {{ calendar.dates[0].getUTCDate() }} – {{ shortMonths[calendar.dates[calendar.dates.length - 1].getUTCMonth()] }} {{ calendar.dates[calendar.dates.length - 1].getUTCDate() }}, {{ calendar.dates[calendar.dates.length - 1].getUTCFullYear() }}
39 | {{ longMonths[calendar.currentDate.getUTCMonth()] }} {{ calendar.currentDate.getUTCDate() }}, {{ calendar.currentDate.getUTCFullYear() }}
40 |
41 |
42 | {{ shortMonths[calendar.dates[0].getUTCMonth()] }} {{ calendar.dates[0].getUTCDate() }} - {{ shortMonths[calendar.dates[calendar.dates.length - 1].getUTCMonth()] }} {{ calendar.dates[calendar.dates.length - 1].getUTCDate() }}
43 | {{ shortMonths[calendar.currentDate.getUTCMonth()] }} {{ calendar.currentDate.getUTCDate() }}, {{ calendar.currentDate.getUTCFullYear() }}
44 |
45 |
46 |
49 | Harker Bell Schedule
50 |
51 |
52 |
53 |
54 |
57 | settings
58 |
59 |
60 |
61 |
62 |
63 | tune
64 |
65 |
66 | Settings
67 |
68 |
69 |
79 |
80 |
81 |
82 | Change view
83 |
84 |
85 | check
86 |
87 |
88 | Day
89 |
90 |
91 |
92 |
93 | check
94 |
95 |
96 | Week
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 | get_app
105 |
106 |
107 | Install app
108 |
109 |
110 |
111 |
112 | calendar_add_on
113 |
114 |
115 | Export to Calendar
116 |
117 |
118 |
119 |
120 | print
121 |
122 |
123 | Print
124 | Light theme recommended
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 | campaign
134 |
135 |
136 |
137 |
138 |
139 | ASB Announcements
140 |
141 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
159 |
160 |
161 |
162 |
163 |
164 |
170 |
171 |
172 | {{ shortMonths[calendar.dates[0].getUTCMonth()] }} {{ calendar.dates[0].getUTCDate() }} – {{ shortMonths[calendar.dates[calendar.dates.length - 1].getUTCMonth()] }} {{ calendar.dates[calendar.dates.length - 1].getUTCDate() }}, {{ calendar.dates[calendar.dates.length - 1].getUTCFullYear() }}
173 | {{ longMonths[calendar.currentDate.getUTCMonth()] }} {{ calendar.currentDate.getUTCDate() }}, {{ calendar.currentDate.getUTCFullYear() }}
174 |
175 |
178 |
179 |
183 |
184 |
185 |
186 | close
187 |
188 | Settings
189 |
190 | Version {{ env.VUE_APP_VERSION }}
191 |
192 |
193 |
194 | Use dark mode
195 |
196 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 | Automatic dark mode
207 | Turns on from sunset to sunrise
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 | Show period colors
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 | What do the event colors mean?
237 |
238 |
239 | warning
240 | Schedules are only guaranteed to be accurate up to the end of the current month.
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 | API
251 | Docs • GitHub • Help • Release Notes
254 |
255 |
256 | Made with
257 | code
258 | by HarkerDev
259 |
260 |
261 |
262 |
263 |
264 |
265 |
269 |
270 |
271 |
272 | close
273 |
274 | Export
275 |
276 | Version {{ env.VUE_APP_VERSION }}
277 |
278 |
279 |
280 |
281 |
282 |
283 |
298 |
299 |
305 | open_in_new
306 |
307 |
308 |
309 | Click to copy URL!
310 |
311 |
312 |
313 |
314 |
315 |
316 |
317 |
318 |
319 |
320 |
321 |
322 | Choose Periods
323 |
324 |
325 |
326 |
327 |
328 |
329 |
330 |
331 |
332 |
333 |
341 |
342 |
343 |
344 |
345 |
346 |
347 |
348 |
349 |
350 | Extra Periods
351 |
352 |
353 |
359 |
367 | {{ event.name }}
368 |
369 |
370 |
371 |
372 |
373 | Extra Events
374 |
375 |
376 |
382 |
390 | {{ event.name }}
391 |
392 |
393 |
394 |
395 |
396 |
397 |
398 |
399 |
400 |
401 |
402 |
403 |
404 |
405 | What do the event colors mean? • Use Google Calendar?
408 |
409 |
410 | warning
411 | Schedules are only guaranteed to be accurate up to the end of the current month.
412 |
413 |
414 |
415 |
416 |
417 |
418 |
419 |
420 |
421 |
422 | API
423 | Docs • GitHub • Help • Release Notes
426 |
427 |
428 | Made with
429 | code
430 | by HarkerDev
431 |
432 |
433 |
434 |
435 |
436 |
437 |
440 |
441 |
444 |
445 |
446 | Location |
447 | Menu Item |
448 |
449 |
450 |
451 |
452 | |
453 |
454 | warning
455 |
456 | The lunch menu may not always be completely accurate.
457 |
458 | |
459 | |
460 |
461 |
462 | {{ item.place }} |
463 | {{ item.food }} |
464 |
465 |
466 |
467 | No lunch menu available
for this day.
468 |
469 |
470 |
471 |
472 | Last connected:
473 | {{ io.connected ? "Connected" : formattedLastConnected || "Connecting..." }}
476 |
477 |
478 |
479 | Last update:
480 | {{ formattedLastUpdated || "Updating..." }}
481 |
482 |
483 |
484 | {{ features.beforeInstallPrompt ? 'Install this app in one click for quick and easy access.' : 'Install this app onto your home screen for quick and easy access.' }}
485 | Install
486 |
489 | Learn More
490 |
491 |
492 |
493 | Updating to the latest version...
494 |
495 |
496 | Permission denied
497 | sentiment_dissatisfied
498 |
499 |
500 |
501 |
502 |
1205 |
1206 |
1378 |
--------------------------------------------------------------------------------
/src/components/LinkSetting.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {{name}}
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/components/PeriodSetting.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | P{{num}}
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | {{colorNames[item]}}
17 |
18 |
19 |
20 |
21 | {{colorNames[item]}}
22 |
23 |
24 |
25 |
26 | arrow_drop_down
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
100 |
--------------------------------------------------------------------------------
/src/components/ScheduleDay.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Mon
8 | 24
9 |
10 |
11 |
12 |
13 | Adjusted
14 | B
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 | {{period.start|formatTime}}–{{period.end|formatTime}}
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 | {{period.start|formatTime}}–{{period.end|formatTime}}
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
160 |
161 |
196 |
--------------------------------------------------------------------------------
/src/components/SchedulePeriod.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | {{period.start|formatTime}}–{{period.end|formatTime}}
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | {{period.start|formatTime}}–{{period.end|formatTime}}
16 |
17 |
18 |
19 |
20 |
70 |
71 |
80 |
--------------------------------------------------------------------------------
/src/main.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-undef */
2 | import Vue from "vue";
3 | import App from "./App.vue";
4 | import router from "./router";
5 | import "./registerServiceWorker";
6 | import vuetify from "./plugins/vuetify";
7 | import {openDB} from "idb";
8 | import * as Sentry from "@sentry/browser";
9 | import * as Integrations from "@sentry/integrations";
10 | import "autotrack/lib/plugins/event-tracker";
11 | import "autotrack/lib/plugins/outbound-link-tracker";
12 | import "autotrack/lib/plugins/page-visibility-tracker";
13 | import "autotrack/lib/plugins/url-change-tracker";
14 | import {getFCP, getLCP, getFID, getCLS, getTTFB} from "web-vitals";
15 | import "./scripts/sun";
16 |
17 | Sentry.init({
18 | dsn: "https://74f7b85b2e13edde2c1935d20e3623e1@o4508111680176128.ingest.us.sentry.io/4509268934918144",
19 | integrations: [new Integrations.Vue({Vue, attachProps: true, logErrors: true})],
20 | release: "harker-bell@"+process.env.VUE_APP_VERSION,
21 | });
22 | window.onload = () => {
23 | setTimeout(() => Sentry.setTag("nodes", document.getElementsByTagName("*").length), 2000);
24 | };
25 |
26 | function saveWebVitals({name, delta, id}) {
27 | if (window.gtag) gtag('event', name, {
28 | event_category: 'Web Vitals',
29 | event_label: id,
30 | value: Math.round(name === 'CLS' ? delta * 1000 : delta),
31 | non_interaction: true
32 | });
33 | }
34 | getFCP(saveWebVitals);
35 | getLCP(saveWebVitals);
36 | getFID(saveWebVitals);
37 | getCLS(saveWebVitals);
38 | getTTFB(saveWebVitals);
39 |
40 | const MS_PER_DAY = 24*60*60*1000;
41 | /**
42 | * Gets the next sunrise or sunset for a given date.
43 | * @param {Date} date a date
44 | * @return {Object} object containing a `time` and an `isSunrise` field
45 | */
46 | window.getNextSunRiseSet = (date) => {
47 | let sunrise = date.sunrise(37.318, -121.971); // coordinates of Harker
48 | let sunset = date.sunset(37.318, -121.971);
49 | if (sunrise < date) sunrise = new Date(sunrise.getTime()+MS_PER_DAY);
50 | if (sunset < date) sunset = new Date(sunset.getTime()+MS_PER_DAY);
51 | if (sunrise < sunset) return {time: sunrise, isSunrise: true};
52 | else return {time: sunset, isSunrise: false};
53 | }
54 | /** Initializes auto dark mode and updates the current setting as necessary. */
55 | window.initializeAutoDark = () => {
56 | window.nextSunRiseSet = window.getNextSunRiseSet(new Date());
57 | if (nextSunRiseSet.isSunrise) localStorage.setItem("darkTheme", true);
58 | else localStorage.setItem("darkTheme", false);
59 | };
60 | if (localStorage.getItem("autoDark") == "true") window.initializeAutoDark();
61 |
62 | Vue.config.productionTip = false;
63 |
64 | //let timestamp = new Date();
65 | if (window.indexedDB)
66 | openDB("harker-bell-db", 1, {
67 | upgrade(db) {
68 | db.createObjectStore("schedules", {keyPath: "date"});
69 | },
70 | }).then(db => {
71 | //console.log("==> DB: ", new Date()-timestamp);
72 | window.db = db;
73 | initVue();
74 | //console.log("==> VUE: ", new Date()-timestamp);
75 | }).catch(err => {
76 | Sentry.captureException(err);
77 | window.db = null;
78 | initVue();
79 | });
80 | else initVue();
81 | function initVue() {
82 | new Vue({
83 | router,
84 | vuetify,
85 | methods: {
86 | /** Sends a basic event to GA */
87 | sendAnalyticsHit: function(actionValue, type, category, additionalData) {
88 | // console.log(actionValue, type, category)
89 |
90 | if (window.gtag) window.gtag('event', category, {
91 | 'value': actionValue,
92 | 'hit_type': type,
93 | 'hit_time': new Date().getTime(),
94 | 'timestamp_minutes': Math.floor(new Date().getTime() / 60000) * 60000,
95 | 'clock': new Intl.DateTimeFormat('en-GB', {
96 | hour: '2-digit',
97 | minute: '2-digit',
98 | second: '2-digit',
99 | hour12: false
100 | }).format(new Date()),
101 | 'branch': window.nf_branch,
102 |
103 | ...additionalData
104 | });
105 | }
106 | },
107 | render: h => h(App),
108 | }).$mount("#app");
109 | }
110 | localStorage.setItem("appVersion", process.env.VUE_APP_VERSION);
111 |
112 | gtag('set', 'user_properties', { 'appVersion': process.env.VUE_APP_VERSION || "not set" }); // dimension5
113 | if (window.GA_MEASUREMENT_ID) gtag('get', window.GA_MEASUREMENT_ID, 'client_id', function(clientId) {
114 | Sentry.setTag("clientId", clientId)
115 | });
116 |
117 | ga("require", "eventTracker", {events: ["click", "contextmenu", "focus"]});
118 | ga("require", "outboundLinkTracker", {
119 | events: ["click", "contextmenu", "auxclick"],
120 | shouldTrackOutboundLink: () => true});
121 | ga("require", "pageVisibilityTracker", {visibleThreshold: 500, visibleMetricIndex: 1});
122 | ga("require", "urlChangeTracker");
--------------------------------------------------------------------------------
/src/material-icons.css:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: "Material Icons Outlined";
3 | font-style: normal;
4 | font-display: block;
5 | font-weight: 400;
6 | src: local("Material Icons Outlined"),
7 | local("MaterialIcons-Outlined"),
8 | url(assets/fonts/MaterialIcons-Outlined.woff2) format("woff2"), /* Modern browsers */
9 | url(assets/fonts/MaterialIcons-Outlined.woff) format("woff"); /* IE11 */
10 | }
11 | .material-icons {
12 | font-family: "Material Icons Outlined";
13 | font-weight: normal;
14 | font-style: normal;
15 | font-size: 24px; /* Preferred icon size */
16 | display: inline-block;
17 | line-height: 1;
18 | text-transform: none;
19 | letter-spacing: normal;
20 | word-wrap: normal;
21 | white-space: nowrap;
22 | direction: ltr;
23 |
24 | /* Support for all WebKit browsers. */
25 | -webkit-font-smoothing: antialiased;
26 | /* Support for Safari and Chrome. */
27 | text-rendering: optimizeLegibility;
28 |
29 | /* Support for Firefox. */
30 | -moz-osx-font-smoothing: grayscale;
31 |
32 | /* Support for IE. */
33 | font-feature-settings: "liga";
34 | }
--------------------------------------------------------------------------------
/src/plugins/vuetify.js:
--------------------------------------------------------------------------------
1 | import Vue from "vue";
2 | import Vuetify from "vuetify/lib";
3 |
4 | Vue.use(Vuetify);
5 |
6 | export default new Vuetify({
7 | theme: {
8 | themes: {
9 | light: {
10 | primary: "#FFFFFF",
11 | secondary: "#BDC1C6",
12 | accent: "#005841",
13 | error: "#D93025",
14 | warning: "#EA8600",
15 | success: "#188038",
16 | info: "#1A73E8",
17 | anchor: "#1A73E8",
18 | red2: {base: "#EA4335", lighten5: "#FAEAEE", darken4: "#CF2035"},
19 | deeporange2: {lighten5: "#FEEDE8", darken4: "#FA7B17"},
20 | orange2: {base: "#FA7B17", lighten5: "#FEEFE3", darken4: "#B36600"},
21 | yellow2: {base: "#FBBC04", lighten5: "#FEF7E0", darken4: "#B06000"},
22 | lightgreen2: {lighten5: "#E9FAF7", darken4: "#027E5A"},
23 | green2: {base: "#0F9D58", lighten5: "#E6F4EA", darken4: "#137333"},
24 | teal2: {base: "#00C3A6", lighten5: "#E4F7FB", darken4: "#007B86"},
25 | lightblue2: {lighten5: "#F6FAFE", darken4: "#047CBD"},
26 | blue2: {base: "#1A73E8", lighten4: "#E8F0FE", lighten5: "#E9EEFF", darken3: "#1A73E8", darken4: "#2A56C6"},
27 | indigo2: {lighten5: "#F1F5FA", darken4: "#1E267D"},
28 | purple2: {base: "#A142F4", lighten5: "#F4EAFF", darken4: "#9345BD"},
29 | pink2: {base: "#F538A0", lighten5: "#FDE7F3", darken4: "#B80672"},
30 | bluegrey2: {base: "#9AA0A6", lighten5: "#E8EAED", darken4: "#2C323D"},
31 | grey2: {lighten5: "#FFFFFF", darken4: "#202124"},
32 | },
33 | dark: {
34 | primary: "#202124",
35 | secondary: "#9AA0A6",
36 | accent: "#098060",
37 | error: "#EA4335",
38 | warning: "#FBBC04",
39 | success: "#34A853",
40 | info: "#8AB4F8",
41 | anchor: "#8AB4F8",
42 | red2: {base: "#EA4335", lighten5: "#534343", darken4: "#F6AEA9"},
43 | deeporange2: {lighten5: "#634947", darken4: "#F5BBA9"},
44 | orange2: {base: "#FA7B17", lighten5: "#635247", darken4: "#F5CBA9"},
45 | yellow2: {base: "#FBBC04", lighten5: "#554C33", darken4: "#FDD663"},
46 | lightgreen2: {lighten5: "#374A41", darken4: "#9DEDB4"},
47 | green2: {base: "#34A853", lighten5: "#37493F", darken4: "#7FD095"},
48 | teal2: {base: "#00C3A6", lighten5: "#31504C", darken4: "#00DCB0"},
49 | lightblue2: {lighten5: "#474D58", darken4: "#A6C5F9"},
50 | blue2: {base: "#4285F4", lighten4: "#394456", lighten5: "#394456", darken3: "#8AB4F8", darken4: "#8AB4F8"},
51 | indigo2: {lighten5: "#223957", darken4: "#87A3FF"},
52 | purple2: {base: "#A142F4", lighten5: "#473A57", darken4: "#BBB3FF"},
53 | pink2: {base: "#FF63B8", lighten5: "#4F2F43", darken4: "#FDB0C9"},
54 | bluegrey2: {base: "#9AA0A6", lighten5: "#3C4043", darken4: "#E8EAED"},
55 | grey2: {lighten5: "#202124", darken4: "#FFFFFF"},
56 | },
57 | },
58 | options: {
59 | customProperties: true
60 | },
61 | },
62 | icons: {
63 | iconfont: "mdiSvg"
64 | },
65 | });
66 |
--------------------------------------------------------------------------------
/src/registerServiceWorker.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-console */
2 |
3 | import { register } from 'register-service-worker'
4 |
5 | if (process.env.NODE_ENV === 'production') {
6 | register(`${process.env.BASE_URL}service-worker.js`, {
7 | registrationOptions: {
8 | scope: '/'
9 | },
10 | ready () {
11 | console.log(
12 | 'App is being served from cache by a service worker.\n' +
13 | 'For more details, visit https://goo.gl/AFskqB'
14 | )
15 | },
16 | registered () {
17 | console.log('Service worker has been registered.')
18 | },
19 | cached () {
20 | console.log('Content has been cached for offline use.')
21 | window.dispatchEvent(new Event("pwaOfflineReady"));
22 | },
23 | updatefound () {
24 | console.log('New content is downloading.')
25 | },
26 | updated () {
27 | console.log('New content is available; please refresh.')
28 | window.dispatchEvent(new Event("pwaUpdated"));
29 | },
30 | offline () {
31 | console.log('No internet connection found. App is running in offline mode.')
32 | },
33 | error (error) {
34 | console.error('Error during service worker registration:', error)
35 | }
36 | })
37 | }
38 |
--------------------------------------------------------------------------------
/src/router.js:
--------------------------------------------------------------------------------
1 | import Vue from "vue";
2 | import Router from "vue-router";
3 | import Home from "./views/Home.vue";
4 |
5 | Vue.use(Router);
6 |
7 | export default new Router({
8 | mode: "history",
9 | base: process.env.BASE_URL,
10 | routes: [
11 | {
12 | path: "/",
13 | name: "home",
14 | component: Home
15 | },
16 | {
17 | path: "/index.html",
18 | name: "index",
19 | component: Home
20 | },
21 | {
22 | path: "/settings",
23 | name: "settings",
24 | component: Home
25 | },
26 | {
27 | path: "/export",
28 | name: "export",
29 | component: Home
30 | },
31 | {
32 | path: "/:year/:month/:day",
33 | name: "day",
34 | component: Home
35 | },
36 | ]
37 | });
--------------------------------------------------------------------------------
/src/sass/main.scss:
--------------------------------------------------------------------------------
1 | $spacer: 2px;
2 |
3 | @import "~vuetify/src/styles/styles.sass";
4 |
5 | $headings: map-merge($headings, (
6 | h5: map-merge(
7 | map-get($headings, "h5"),
8 | ("letter-spacing": -.02rem)
9 | ),
10 | h6: map-merge(
11 | map-get($headings, "h6"),
12 | ("letter-spacing": -.02rem)
13 | ),
14 | "body-1": map-merge(
15 | map-get($headings, "body-1"),
16 | ("size": .875rem)
17 | ),
18 | "body-2": map-merge(
19 | map-get($headings, "body-2"),
20 | ("size": .8125rem)
21 | ),
22 | caption: map-merge(
23 | map-get($headings, "caption"),
24 | ("letter-spacing": 0.01rem)
25 | )
26 | ));
27 | $material-light: map-merge($material-light, (
28 | "background": map-get($shades, "white"),
29 | ));
30 | $material-dark: map-merge($material-dark, (
31 | "background": #202124,
32 | "cards": #202124,
33 | ));
34 | $border-radius-root: 2px;
35 | $btn-outline-border-width: 1px;
36 | $tooltip-font-size: 12px;
37 | $icon-outlined-border-width: 1px;
38 | $list-item-min-height: 36px;
39 | $timeline-dot-small-size: 7px;
40 | $timeline-item-padding: 4px;
41 | $timeline-divider-width: 17px;
42 | $timeline-line-width: 1px;
--------------------------------------------------------------------------------
/src/scripts/sun.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Sunrise/sunset script. By Matt Kane.
3 | *
4 | * Based loosely and indirectly on Kevin Boone's SunTimes Java implementation
5 | * of the US Naval Observatory's algorithm.
6 | *
7 | * Copyright © 2012 Triggertrap Ltd. All rights reserved.
8 | *
9 | * This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General
10 | * Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option)
11 | * any later version.
12 | *
13 | * This library is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied
14 | * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
15 | * details.
16 | * You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to
17 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA,
18 | * or connect to: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
19 | */
20 |
21 |
22 | Date.prototype.sunrise = function(latitude, longitude, zenith) {
23 | return this.sunriseSet(latitude, longitude, true, zenith);
24 | }
25 |
26 | Date.prototype.sunset = function(latitude, longitude, zenith) {
27 | return this.sunriseSet(latitude, longitude, false, zenith);
28 | }
29 |
30 | Date.prototype.sunriseSet = function(latitude, longitude, sunrise, zenith) {
31 | if(!zenith) {
32 | zenith = 90.8333;
33 | }
34 |
35 |
36 | var hoursFromMeridian = longitude / Date.DEGREES_PER_HOUR,
37 | dayOfYear = this.getDayOfYear(),
38 | approxTimeOfEventInDays,
39 | sunMeanAnomaly,
40 | sunTrueLongitude,
41 | ascension,
42 | rightAscension,
43 | lQuadrant,
44 | raQuadrant,
45 | sinDec,
46 | cosDec,
47 | cosLocalHourAngle,
48 | localHourAngle,
49 | localHour,
50 | localMeanTime,
51 | time;
52 |
53 | if (sunrise) {
54 | approxTimeOfEventInDays = dayOfYear + ((6 - hoursFromMeridian) / 24);
55 | } else {
56 | approxTimeOfEventInDays = dayOfYear + ((18.0 - hoursFromMeridian) / 24);
57 | }
58 |
59 | sunMeanAnomaly = (0.9856 * approxTimeOfEventInDays) - 3.289;
60 |
61 | sunTrueLongitude = sunMeanAnomaly + (1.916 * Math.sinDeg(sunMeanAnomaly)) + (0.020 * Math.sinDeg(2 * sunMeanAnomaly)) + 282.634;
62 | sunTrueLongitude = Math.mod(sunTrueLongitude, 360);
63 |
64 | ascension = 0.91764 * Math.tanDeg(sunTrueLongitude);
65 | rightAscension = 360 / (2 * Math.PI) * Math.atan(ascension);
66 | rightAscension = Math.mod(rightAscension, 360);
67 |
68 | lQuadrant = Math.floor(sunTrueLongitude / 90) * 90;
69 | raQuadrant = Math.floor(rightAscension / 90) * 90;
70 | rightAscension = rightAscension + (lQuadrant - raQuadrant);
71 | rightAscension /= Date.DEGREES_PER_HOUR;
72 |
73 | sinDec = 0.39782 * Math.sinDeg(sunTrueLongitude);
74 | cosDec = Math.cosDeg(Math.asinDeg(sinDec));
75 | cosLocalHourAngle = ((Math.cosDeg(zenith)) - (sinDec * (Math.sinDeg(latitude)))) / (cosDec * (Math.cosDeg(latitude)));
76 |
77 | localHourAngle = Math.acosDeg(cosLocalHourAngle)
78 |
79 | if (sunrise) {
80 | localHourAngle = 360 - localHourAngle;
81 | }
82 |
83 | localHour = localHourAngle / Date.DEGREES_PER_HOUR;
84 |
85 | localMeanTime = localHour + rightAscension - (0.06571 * approxTimeOfEventInDays) - 6.622;
86 |
87 | time = localMeanTime - (longitude / Date.DEGREES_PER_HOUR);
88 | time = Math.mod(time, 24);
89 |
90 | var midnight = new Date(0);
91 | midnight.setUTCFullYear(this.getUTCFullYear());
92 | midnight.setUTCMonth(this.getUTCMonth());
93 | midnight.setUTCDate(this.getUTCDate());
94 |
95 |
96 |
97 | var milli = midnight.getTime() + (time * 60 *60 * 1000);
98 |
99 |
100 | return new Date(milli);
101 | }
102 |
103 | Date.DEGREES_PER_HOUR = 360 / 24;
104 |
105 |
106 | // Utility functions
107 |
108 | Date.prototype.getDayOfYear = function() {
109 | var onejan = new Date(this.getFullYear(),0,1);
110 | return Math.ceil((this - onejan) / 86400000);
111 | }
112 |
113 | Math.degToRad = function(num) {
114 | return num * Math.PI / 180;
115 | }
116 |
117 | Math.radToDeg = function(radians){
118 | return radians * 180.0 / Math.PI;
119 | }
120 |
121 | Math.sinDeg = function(deg) {
122 | return Math.sin(deg * 2.0 * Math.PI / 360.0);
123 | }
124 |
125 |
126 | Math.acosDeg = function(x) {
127 | return Math.acos(x) * 360.0 / (2 * Math.PI);
128 | }
129 |
130 | Math.asinDeg = function(x) {
131 | return Math.asin(x) * 360.0 / (2 * Math.PI);
132 | }
133 |
134 |
135 | Math.tanDeg = function(deg) {
136 | return Math.tan(deg * 2.0 * Math.PI / 360.0);
137 | }
138 |
139 | Math.cosDeg = function(deg) {
140 | return Math.cos(deg * 2.0 * Math.PI / 360.0);
141 | }
142 |
143 | Math.mod = function(a, b) {
144 | var result = a % b;
145 | if(result < 0) {
146 | result += b;
147 | }
148 | return result;
149 | }
150 |
--------------------------------------------------------------------------------
/src/views/Home.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 | {{period.start|formatTime}}–{{period.end|formatTime}}
69 | {{Math.ceil((period.end-time.utcNow)/$MS_PER_MIN)}} min. left
70 | View menu
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 | {{period.start|formatTime}}–{{period.end|formatTime}}
81 |
82 |
83 | {{period.start|formatTime}}–{{period.end|formatTime}}
84 | {{Math.ceil((period.end-time.utcNow)/$MS_PER_MIN)}} min. left
85 |
86 | Open link launch
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 | {{period.start|formatTime}}–{{period.end|formatTime}}
96 |
97 |
98 | {{period.start|formatTime}}–{{period.end|formatTime}}
99 | {{Math.ceil((period.end-time.utcNow)/$MS_PER_MIN)}} min. left
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 | {{schedules[date.toISOString()].name}}
109 |
110 |
111 |
112 |
113 | {{event.start|formatTime}}–{{event.end|formatTime}} •
114 | {{event.name}}
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
302 |
303 |
437 |
444 |
--------------------------------------------------------------------------------
/vue.config.js:
--------------------------------------------------------------------------------
1 | process.env.VUE_APP_VERSION = require("./package.json").version;
2 |
3 | module.exports = {
4 | devServer: {
5 | disableHostCheck: true,
6 | public: '127.0.0.1:8080'
7 | },
8 | productionSourceMap: true,
9 | transpileDependencies: [],
10 | css: {
11 | loaderOptions: {
12 | sass: {
13 | data: `@import "~@/sass/main.scss"`
14 | }
15 | }
16 | },
17 | pwa: {
18 | name: "Harker Bell Schedule",
19 | themeColor: "#FFFFFF",
20 | msTileColor: "#005841",
21 | appleMobileWebAppCapable: "yes",
22 | workboxOptions: {
23 | cleanupOutdatedCaches: true,
24 | clientsClaim: true,
25 | exclude: [/_redirects/],
26 | navigateFallback: "/index.html",
27 | navigateFallbackBlacklist: [/api/, /docs/, /admin/, /submitevent/, /p/],
28 | offlineGoogleAnalytics: {
29 | parameterOverrides: {
30 | cd14: "offline",
31 | },
32 | hitFilter: params => {
33 | const queueTime = Math.round(params.get("qt")/1000);
34 | params.set("cm3", queueTime);
35 | },
36 | },
37 | runtimeCaching: [{
38 | urlPattern: /^https:\/\/fonts\.googleapis\.com/,
39 | handler: "staleWhileRevalidate",
40 | }, {
41 | urlPattern: /^https:\/\/fonts\.gstatic\.com/,
42 | handler: "cacheFirst",
43 | options: {
44 | cacheName: "google-fonts-files",
45 | expiration: {
46 | maxAgeSeconds: 60*60*24*365,
47 | maxEntries: 30
48 | }
49 | }
50 | }, {
51 | urlPattern: "https://www.google-analytics.com/analytics.js",
52 | handler: "staleWhileRevalidate"
53 | }, {
54 | urlPattern: "https://ipmeta.io/plugin.js",
55 | handler: "staleWhileRevalidate"
56 | }, {
57 | urlPattern: "https://qbw8rkkv7x0h.statuspage.io/embed/script.js",
58 | handler: "staleWhileRevalidate"
59 | }],
60 | skipWaiting: true,
61 | },
62 | },
63 | };
--------------------------------------------------------------------------------