Compare commits

..

401 Commits

Author SHA1 Message Date
doprz
4f2a29f268 chore: fix lint errors 2024-09-30 19:31:17 -05:00
doprz
d5c3d32170 Merge branch 'derek/experimental' of https://github.com/DereC4/UT-Registration-Plus into DereC4-derek/experimental 2024-09-30 19:20:03 -05:00
Samuel Gunter
149fda3b72 fix: improper list data propagation (#240) 2024-09-30 19:16:47 -05:00
0ab83efd47 feat: Derek/disable updating (#239)
* feat: first conditional change

* feat: update conditionally 2

* feat: xd
2024-09-30 19:15:25 -05:00
vinsonzheng499
9eaff24cbb fix: added room number to course popup (#231) 2024-09-30 09:47:40 -05:00
Samuel Gunter
75ad4167b6 feat: newer grades and parameterized queries (#238)
previously, we were using template strings,
which had issues for professors with an apostrophe in their name
2024-09-29 21:15:30 -05:00
f34dd95d77 feat: experimental toggle for icons, left off for now (#237)
* feat: temporary removal of waitlist etc

* feat: experimental toggle for icons for now

* feat: popupmain toggle

* feat: cal header toggle

* feat: adjusted course cell for toggle

* feat: fine it'll be a constant

* chore: lint

* feat: other experimental options
2024-09-29 15:11:27 -05:00
9356357545 Merge branch 'Longhorn-Developers:main' into derek/experimental 2024-09-29 14:59:26 -05:00
d424ccce49 feat: temporary removal of waitlist etc (#236) 2024-09-28 23:05:52 -05:00
be87e41814 feat: one single exclamation mark did all that (#235) 2024-09-28 22:21:16 -05:00
438c82bfb5 feat: aesthetically pleasing squishier course blocks when compressed (#232) 2024-09-28 21:19:33 -05:00
0d63c66356 fix: styling 2024-09-08 12:46:26 -05:00
703a0fa596 feat: button custom function prop 2024-09-04 13:05:31 -05:00
7acc91f4ee chore: fix lint 2024-09-04 12:50:06 -05:00
5d9980cac3 feat: using type now 2024-09-04 12:43:45 -05:00
57832b0dd4 fix: story 2024-09-04 12:43:08 -05:00
b9358803dd feat: look at how this switch goes back and forth very mindful very demure 2024-09-04 12:27:12 -05:00
4397dfb1c5 feat: switch button initial test 2024-09-04 12:25:16 -05:00
f83e012d62 feat: link to your registered courses (#228) 2024-08-24 20:34:09 -04:00
0e70bbd52f Merge branch 'Longhorn-Developers:main' into main 2024-06-04 01:14:35 -05:00
Razboy20
bc5d68ce18 fix: dialog movement (#227)
* fix: dialog movement

* chore: lint
2024-06-03 20:53:39 -05:00
Samuel Gunter
863d980b2d feat: fall 2023 grades (#226) 2024-06-03 00:25:26 -05:00
Razboy20
f943e4801b chore: bump version 2024-05-27 21:12:58 -05:00
79f2ffa4cc Merge branch 'Longhorn-Developers:main' into main 2024-05-27 18:26:05 -05:00
Samuel Gunter
78d749a8a9 feat: grades by professor (#225)
* feat: grades by professor

* chore(pr-review): extract into Distributions type

---------

Co-authored-by: doprz <52579214+doprz@users.noreply.github.com>
2024-05-27 16:36:42 -05:00
bc354f3798 feat: additional changes to #201 (#224)
* fix: calendar weight change

* feat: small color change

* feat: stories for logo

* feat: align the logo

* feat: stupid imports

* fix: eslint styling

* Update CalenderHeader.tsx

* Update colors.module.scss

* chore: remove argTypes

* chore: remove argTypes

* chore: remove argTypes

* fix: lock

* chore: styled up logo stories

---------

Co-authored-by: doprz <52579214+doprz@users.noreply.github.com>
2024-05-27 09:32:31 -05:00
8959e0d9f7 feat: rerouted directory to syllabus when click on professor name (#211)
* feat: testing names

* feat: rerouted instructor on click from directory to syllabus

* feat: removed number requirement

* fix: removed department
2024-05-23 15:08:54 -05:00
Razboy20
40ef92b230 style: add slight shadow on hover to calendar course cells (#217) 2024-05-21 23:08:04 -05:00
Razboy20
4f0ae43411 refactor: Update spinner design (#223) 2024-05-21 23:07:41 -05:00
c330e3123a Merge branch 'Longhorn-Developers:main' into master 2024-05-21 21:46:06 -05:00
Samuel Gunter
229a8a29e8 feat: bold red refresh error (#220)
* feat: bold red refresh error

* style: exclamation point location
2024-05-21 21:04:25 -05:00
Razboy20
2f9e9b1297 refactor: Update getCreditHours function to handle additional cases (#222)
* refactor: Update getCreditHours function to handle additional cases

* style: fix prettier
2024-05-21 20:59:08 -05:00
Samuel Gunter
6812d685d0 feat: popout icon for ccip in calendar (#221)
* feat: popout icon for ccip in calendar

* refactor: simplify context

---------

Co-authored-by: Razboy20 <razboy20@gmail.com>
2024-05-21 10:50:31 -05:00
Samuel Gunter
a2303ee35f feat: updated calendar page and recruitment banner links (#219) 2024-05-20 22:31:09 -05:00
Razboy20
e3c2da36df style: always show scrollbar (#215)
* style: always show scrollbar

* fix: hide header scrollbar in specific instances

* hotfix: prettier fail

* style: always show scrollbar

* fix: hide header scrollbar in specific instances
2024-05-20 21:56:36 -05:00
Razboy20
0c7cd3e790 style: fix injected popup top/bottom padding (#218) 2024-05-20 21:49:18 -05:00
Razboy20
7a40008c1e fix: Fix popup drag hitbox (#216)
* hotfix: Fix popup drag hitbox

Somehow, the drags get "lost" within a button element. Converting the parent container to a div works to fix this.

* refactor: Update styling
2024-05-20 21:24:00 -05:00
Razboy20
07cadb8c62 hotfix: prettier fail 2024-05-20 20:14:45 -05:00
Razboy20
d4611952d4 build: update storybook (#214)
* chore: Update updatedAt dates in schedules

Set updatedAt dates to be fixed to '2024-01-01 12:00' for all schedules in
UserScheduleStore.

* build: update storybook

* chore: Removed unnecessary eslint-disable-next-line statements.
2024-05-20 17:08:36 -05:00
Razboy20
88c0061187 chore: update headlessui (#212) 2024-05-20 16:18:34 -05:00
Razboy20
3684ee5e9b build: update pnpm version to 9 (#213)
* ci: update pnpm version to 9 in workflow files

* build: Update lockfileVersion to '9.0' in pnpm-lock.yaml.
2024-05-20 15:59:28 -05:00
Samuel Gunter
d1b921a5b0 feat: DialogProvider component (#198)
* feat: somewhat working DialogProvider

* feat: handle multiple dialogs properly, initial focus

let's just ignore that showFocus=true doesn't work with "nested" dialogs

Co-authored-by: Razboy20 <Razboy20@users.noreply.github.com>

* feat: rework code

* feat: add default styles to prompts

* refactor: fix stylings

---------

Co-authored-by: Razboy20 <Razboy20@users.noreply.github.com>
Co-authored-by: Razboy20 <razboy20@gmail.com>
2024-05-20 15:00:00 -05:00
Abhinav Chadaga
bcb5a8c469 fix: remove course name from the syllabi lookup (#200)
* fix: remove course name from the syllabi lookup

* fix: use instructor name in past syllabi lookup
2024-04-17 10:33:47 -05:00
38d09e4aae Merge branch 'Longhorn-Developers:main' into master 2024-04-16 15:15:38 -05:00
0534f607a5 feat: Initial Splash Text Commit (#208)
* fix: font weight added

* fix: change color hex

* fix: change color again

* fix: text-ut-burntorange

* fix: importance (tailwind wise)

* feat: debauchery

* feat: modernizing some covid-era dialogue

* feat: and more

* feat: ok other people can add now

* feat: one last one

* feat: maybe this will fix it

* fix: unstyled line

* feat: add more phrases

* feat: moved to a .tsx file format

* fix: prettier formatting

* fix: add doprz's quotes back

---------

Co-authored-by: doprz <52579214+doprz@users.noreply.github.com>
2024-04-16 10:14:09 -05:00
98a7cc618b Merge branch 'Longhorn-Developers:main' into master 2024-04-09 14:04:09 -05:00
bae1da43d3 feat: some small changes for colors and font (#201)
* fix: font weight added

* fix: change color hex

* fix: change color again

* fix: text-ut-burntorange

* fix: importance (tailwind wise)
2024-03-30 11:44:15 -05:00
Samuel Gunter
a3f5e0f27f feat: better discord icon (#205) 2024-03-29 13:02:38 -05:00
74cc6e6da1 fix: importance (tailwind wise) 2024-03-29 00:51:25 -05:00
c68b017462 Merge branch 'Longhorn-Developers:main' into master 2024-03-29 00:44:18 -05:00
Samuel Gunter
227de53e84 feat: show async courses in the bottom bar (#204)
* feat: show async courses in the bottom bar

* fix: hide "Async/Other" header when there are no async courses, off-by-n error

(where n is the number async courses)

* refactor: move types closer to map instead of weird "as boolean"

* refactor: move satisfies to return type
2024-03-29 00:01:23 -05:00
Samuel Gunter
d3f64ec79e feat: actually sum for duplicate semesters (different uniques) (#202) 2024-03-25 19:41:33 -05:00
aeb19cff54 fix: text-ut-burntorange 2024-03-25 15:50:59 -05:00
5a73c8ee6c fix: change color again 2024-03-25 15:17:40 -05:00
35b0d01c51 fix: change color hex 2024-03-25 15:16:30 -05:00
b84cf7c15d fix: font weight added 2024-03-25 15:14:52 -05:00
doprz
8e79d6a6a8 fix: db with proper insertion order 2024-03-24 10:12:08 -05:00
doprz
60d1f48bd9 feat: add new db powered by UT_Grade_Parser (#163)
* feat: add new db powered by UT_Grade_Parser

* Merge branch 'main' of https://github.com/Longhorn-Developers/UT-Registration-Plus into feature/update-db

* feat: update db

* feat: update db handlers and types

Co-authored-by: Samuel Gunter <sgunter@utexas.edu>

* fix: type errors

* fix: add Other to grade dist

* fix: db with proper insertion order

* Merge branch 'main' of https://github.com/Longhorn-Developers/UT-Registration-Plus into feature/update-db

* chore: address PR comments

Co-Authored-By: Samuel Gunter <sgunter@utexas.edu>
2024-03-24 00:21:18 -05:00
Som Gupta
ee2b7c40b9 refactor: updated injected popup to match figma (#189)
* refactor: updated injected popup to match figma

* style: ran prettier

* style: fixed pr comments

* style: ran prettier

* refactor: tailwind style

---------

Co-authored-by: Razboy20 <razboy20@gmail.com>
2024-03-23 19:22:49 -05:00
9b4d61c2b0 feat: added flag ;-; (#195)
Co-authored-by: Samuel Gunter <sgunter@utexas.edu>
2024-03-22 19:12:06 -05:00
Samuel Gunter
5b1e4513e2 fix: don't crash on cultural diversity flag (#196)
Co-authored-by: Som Gupta <78577376+knownotunknown@users.noreply.github.com>
2024-03-22 18:52:08 -05:00
Razboy20
0c42979423 feat: listed versioning for beta builds (#192) 2024-03-22 12:15:44 -05:00
Som Gupta
36ac8607a9 refactor: updated styling of conflict component to match figma (#190)
* refactor: updated styling of conflict component to match figma

* style: ran prettier

* refactor: slightly

---------

Co-authored-by: Razboy20 <razboy20@gmail.com>
2024-03-22 11:34:25 -05:00
Razboy20
4f4f34e281 fix: reactivity (#188) 2024-03-21 19:43:55 -05:00
Razboy20
8a5e12ec63 feat: beta builds (#187)
* feat: beta builds

* fix: types
2024-03-21 19:21:33 -05:00
Samuel Gunter
7d4c5d7be8 feat: screenshot whole page, hide certain elements, screenshot fixed size (#180)
* feat: screenshot whole page, hide certain elements, screenshot fixed size

* refactor: use variants instead of groups and custom rules

* feat: scaled header, smaller body, weird padding/margin changes

* feat: consistent sizing & style regardless of zoom

* feat: use downloadBlob instead of hand-rolled image saving

* fix: be type safe is toBlob returns Promise<null>

* fix: revoke object url when it should be

* fix: animation scheduling

---------

Co-authored-by: Razboy20 <razboy20@gmail.com>
2024-03-21 19:20:03 -05:00
doprz
2dfb10e57b feat: use downloadBlob util (#186)
* feat: use downloadBlob util

* chore: lint

* fix: revert saveCalAsPng

* feat: refactor downloadBlob

* chore: remove comments

* chore: lint and remove extra async

* refactor: cleanup

---------

Co-authored-by: Razboy20 <razboy20@gmail.com>
2024-03-21 16:25:37 -05:00
Samuel Gunter
036cd628d3 refactor: move getTabInfo to calendar only (#185) 2024-03-21 16:19:44 -05:00
doprz
9ee567530f chore: remove todo.md 2024-03-21 15:00:13 -05:00
Razboy20
e080e93faa refactor: remove component subfolders (#184)
(and unused components)
2024-03-21 13:47:59 -05:00
doprz
efed1c0edb feat: enable TS strict mode (#168)
* feat: enable TS strict mode

* fix: strict TS errors

* fix: strict TS errors

* fix: strict TS errors

* fix: strict TS errors

* fix: strict TS errors

* fix: strict TS errors

* fix: strict TS errors

* fix: strict TS errors

* fix: colors bug with default

* fix: strict TS errors

* fix: strict TS errors

* fix: strict TS errors

* fix: strict TS errors

* fix: strict TS errors

* fix: strict TS errors

* fix: strict TS errors

* fix: strict TS errors

* fix: strict TS errors

* fix: strict TS errors

* fix: strict TS errors

* fix: text type errors

* fix: strict TS errors

* fix: strict TS errors

* fix: strict TS errors - add definite assignment assertion

* fix: strict TS errors - add definite assignment assertion

* fix: strict TS errors

* fix: strict TS errors

* fix: strict TS errors

* fix: strict TS errors

* fix: strict TS errors

* fix: strict TS errors

* fix(ESLint): error on no-explicit-any

* fix: type annotations for any types

* fix: strict TS errors

* fix: strict TS errors

* fix: strict TS errors

* fix: strict TS errors (and remove packages)

* fix: strict TS errors

* fix: strict TS errors

* fix: strict TS errors

* fix: strict TS errors

* fix: strict TS errors

* fix: strict TS errors

* fix: strict TS errors

* feat: enable React.StrictMode

* fix: strict TS errors (done!)

* fix: build error

* fix: replace no-explicit-any assertions

* refactor: cleanup

* refactor: more cleanup

* style: prettier

---------

Co-authored-by: Lukas Zenick <lukas@utexas.edu>
Co-authored-by: Razboy20 <razboy20@gmail.com>
2024-03-21 13:19:40 -05:00
doprz
0c76052478 feat: add MIMEType 2024-03-21 10:27:07 -05:00
doprz
2af351efa8 feat: add downloadBlob util 2024-03-21 10:20:52 -05:00
Samuel Gunter
949bbb0835 fix: async course display size (#181) 2024-03-20 11:03:23 -05:00
Razboy20
5ed81e4be9 feat: course color generation (#179)
* feat: course color generation

* feat: add proper TS for hex colors

* refactor: fix oklab and improve contrast ratios

* fix: update HexColor type

* refactor: update color switch point

* refactor: color-related functions and types

* fix: imports and TS issues

* fix: imports and TS issues

* chore: add no-restricted-syntax ForInStatement

* chore(docs): add jsdoc

---------

Co-authored-by: doprz <52579214+doprz@users.noreply.github.com>
2024-03-19 18:54:11 -05:00
c5fc6219e1 fix: idk why that comment was there (#177)
* fix: idk why that doc was there

* Update Calendar.tsx

* Update Calendar.tsx
2024-03-19 18:52:14 -05:00
ba2bc60add feat: Course Flag Tooltips (#178)
* feat: initial attempt at hover text

* feat: fixgin it

* fix: heading to master real quick

* feat: tooltip prop added

* feat: tooltip now reads from the map instead of just label. Think we're done!

* Update Chip.tsx
2024-03-18 17:15:02 -05:00
Razboy20
8027c3d1bf feat: match calendar designs & add functionality (#176)
* feat: match calendar designs

* refactor: update breakpoints
2024-03-18 10:06:23 -05:00
Samuel Gunter
dc77cc27da feat: course colors (#175)
* feat: course colors

* docs: fix typo in jsdoc
2024-03-17 02:05:59 -05:00
Samuel Gunter
afa634f085 fix: clean up #173 (#174) 2024-03-17 01:48:09 -05:00
Razboy20
791a42bcd4 feat: calendar matchings (#173)
* feat: calendar matchings

* fix: build

* refactor: resolve pr comments

* fix: destrucure editorRef

---------

Co-authored-by: doprz <52579214+doprz@users.noreply.github.com>
2024-03-17 00:32:50 -05:00
Abhinav Chadaga
df1849180d feat: calendar-course-cell-color-picker (#157)
* feat: calendar-course-cell-color-picker done??

* fix: ensure hex code is lowercase

* fix: make hex codes lower case

* chore: convert px to rem in ColorPatch.tsx

* fix: add functionality to the invert colors button

* fix: some more lowercase stuff

* fix: remove hardcoded color patch hex codes, remove hardcoded pixel values

* chore: remove React.FC

* chore: modify docs

* fix: remove duplicate style

* fix: used name over size specified classes

* fix: grid over flex, elie feedback

* refactor: use color strings instead of indices

* refactor: remove console.log statements
2024-03-16 16:08:28 -05:00
27094846f7 feat: open an injected course page on course block click in popup main (#146)
* feat: Imports to popupcourseblock.tsx

* changing the blocks to accept parameters for clicking functionality which may or may not open the calendar

* put the click parameter in the div of popupcourseblock

* safely calling for onCourseClick in the event it is an undefined function

* handled other calls of popupcourseblock with empty functions for now, and i think popupmain opens calendar now when the course block is clicked

* feat: Testing out passing params to handleOpenCalendar

* url that takes in params to open calendar with params

* further work on url params; from popup main to handleopencalendar to calendar using urlsearchparams

* feat: small calendar shifting after merge:

* fix: merge handling and then references to new click parameter

* fix: optional params

* feat: split into two functions instead

* fix: changing proper usage of handleOpenCalendarWithCourse

* feat: show course popup when calendar opened

* chore: remove useless commented out code

* feat: close popup on calendar nav, fix build errors, remove useless comments/logs

* chore: chromatic so dumb fr why aren't you chrome

* fix: refactor listeners to build properly

* feat: exit early when not in chrome extension

* fix: function return type

* fix: function return type x2

* fix: generic type for useState

* refactor: extract calendar opening on click functions

* refactor: chrome runtime mock, omit question mark if no query params, rename calendar event

* refactor: move course click event into component directly instead of prop

* refactor: removed useless wrapper functions, made popup course block more accessible

* fix: i dont wanna talk about it

---------

Co-authored-by: Samuel Gunter <sgunter@utexas.edu>
2024-03-16 15:57:50 -05:00
Razboy20
ed4fbe5651 refactor: ccpopup (#172)
* refactor: dialog animation improvements

* refactor: update ccpopup to match designs
2024-03-15 23:49:26 -05:00
Razboy20
d04818ccd8 refactor: update text and link components to be polymorphic (#171) 2024-03-15 22:06:36 -05:00
Samuel Gunter
61c43962fb chore: ignore storybook build (#169)
* chore: ignore storybook build

* chore: make storybook-static explicitly a directory match

Co-authored-by: doprz <52579214+doprz@users.noreply.github.com>

---------

Co-authored-by: doprz <52579214+doprz@users.noreply.github.com>
2024-03-15 20:36:36 -05:00
Samuel Gunter
b4ad6870bf feat: pad unique ids to 5 digits (#170)
* feat: pad unique ids to 5 digits

* feat: also pad in popup course block
2024-03-15 19:23:27 -05:00
Razboy20
ea297d09bb build: allow unocss to run through dev mode (#167)
* build: allow unocss to run through dev mode

* build: Update unocss dependencies to latest versions

Updated dependencies '@unocss/eslint-config', '@unocss/postcss', '@unocss/preset-uno', '@unocss/preset-web-fonts', '@unocss/reset', '@unocss/transformer-directives', '@unocss/transformer-variant-group' to version 0.58.6 for consistency and compatibility.

* refactor: add note to readme

* refactor: improve readme

* refactor: update readme (again)

* Update README.md

Updates the UTRP description text

---------

Co-authored-by: Isaiah David Rodriguez <51803892+IsaDavRod@users.noreply.github.com>
2024-03-15 16:30:42 -05:00
Razboy20
85769e9d2c refactor(UserSchedule): index by a unique id rather than name (#166)
* refactor(UserSchedule): index by a unique id rather than name

* refactor: Update parameter names in schedule function jsdocs

* refactor: change more instances of .name to .id

* refactor: Fix typo in variable name and update references

* refactor: Remove console.log statement

* fix(chromatic): Update ScheduleListItem story

* refactor: remove unused eslint rule
2024-03-14 23:09:04 -05:00
Som Gupta
5714ed16d7 fix: list component fixed (#162)
* fix: list component fixed

* style: ran prettier

* Update List.tsx

* refactor: remove console log

---------

Co-authored-by: Razboy20 <razboy20@gmail.com>
2024-03-14 00:17:47 -05:00
Razboy20
e919e96c53 feat: proper injected styles (#164) 2024-03-14 00:10:41 -05:00
Razboy20
442be8cbee feat: update dialog component to headlessui (#159) 2024-03-13 23:09:43 -05:00
Som Gupta
df7a7c65d6 fix: Fixed typescript error (#161)
* fix: fixed bug where activeSchedule doesn't update correctly

* refactor: always returning activeSchedule

* style: prettier

* fix: fixed typescript error

* style: prettier
2024-03-13 23:03:22 -05:00
Som Gupta
a409090b90 fix: fixed bug where activeSchedule doesn't update correctly (#158)
* fix: fixed bug where activeSchedule doesn't update correctly

* refactor: always returning activeSchedule

* style: prettier
2024-03-13 22:15:02 -05:00
Samuel Gunter
5cce1c79fc feat: calendar header formatting and data displaying (#160) 2024-03-13 21:38:40 -05:00
Razboy20
61c1e88dcf style: fix centering of radio item (#156) 2024-03-13 17:29:11 -05:00
Razboy20
038ebaa268 feat: list reordering (#154) 2024-03-13 16:45:32 -05:00
Samuel Gunter
91f62e1943 fix: correct parsing of noon (#155) 2024-03-13 16:09:31 -05:00
Samuel Gunter
7986549fdd feat: fix save as button dividers (#153)
* feat: fix save as button dividers

* fix: only show left divider when there are async classes
2024-03-13 16:08:58 -05:00
Razboy20
1d8da6579e refactor(popup): match styles/reduce paint flicker (#136)
* refactor: match popup styles/reduce paint flicker

* fix: useSchedules hook

* feat: popup 

* fix: repaint issue on popup body

* fix: initial active schedule

* fix: center justification

* fix: reactivity error
2024-03-13 12:33:54 -05:00
Casey Charleston
0dff12232c feat: async text hiding on Calendar's Bottom Bar when there are no async courses (#152)
* feat: async text not visible when no async courses

* refactor: converted useState to boolean

* fix: remove unused import

* fix: maintain component height when hiding is enabled

* refactor: match stylings to figma

* refactor: padding change to match when there are courses to display

---------

Co-authored-by: Razboy20 <razboy20@gmail.com>
2024-03-12 23:13:27 -05:00
Sriram Hariharan
a5e9e3c214 feat: update badge count when schedule changes (#150)
* feat: update badge count when schedule changes

* fix: fixed linting issues
2024-03-12 00:59:14 -05:00
Sriram Hariharan
8e181b3010 feat: added scrapedAt property (#149)
* feat: added scrapedAt property

* fix: type-check
2024-03-12 00:57:00 -05:00
Abhinav Chadaga
44af9e16e4 feat: abhinavchadaga/reusable-popup-prompt (#148)
* feat: some work on popup prompt

* feat: add some stuff

* feat: reusable prompt component

Takes a title, description, and button children.

* fix: pr feedback

* fix: import ReactElement
2024-03-12 00:01:56 -05:00
Som Gupta
7760e3acf4 refactor: doesn't crash tab, and doesn't cause errors (#143)
* refactor: doesn't crash tab, and doesn't cause errors

* Revert "fix: options page (#131)"

This reverts commit dc100b5d3a.

* refactor: Simplify CourseSchedule constructor

* refactor: Refactor CourseSchedule constructor (again)

* Reapply "fix: options page (#131)"

This reverts commit 969c5a234f.

---------

Co-authored-by: Razboy20 <razboy20@gmail.com>
2024-03-11 23:59:15 -05:00
Dhruv
a8ea3bc683 fix: conflict row bug (#130)
* fix: temp fixes, need to still work on it

* fix: im a god at css otl

* fix: handle edge case where they have conflicting classes in their schedule

* fix: got it working

* fix: don't change the font size of course names

* fix: remove unused prop

* fix: lint errs

* fix: remove unused code
2024-03-11 23:41:20 -05:00
Lukas Zenick
4c61ebd3fc fix: Calendar View/Scaling Issues (#144)
* fix: calendar looks normal now; scaling

* fix: array indexing

* chore: eslint

* chore: lint

* feat: team Links and scrolling
2024-03-11 23:34:38 -05:00
Lukas Zenick
591687eee8 fix: injection not working from som/elie commit (#145) 2024-03-11 15:53:45 -05:00
Razboy20
261d2f2e84 refactor: match dropdown to figma & fix issues (#142) 2024-03-09 23:16:56 -06:00
Razboy20
3839bff29e feat: add chrome.storage api mocks for storybook use (#141) 2024-03-09 23:15:50 -06:00
Razboy20
78a6939929 fix: chromatic builds on PRs (#140) 2024-03-09 21:20:41 -06:00
Som Gupta
3406e9a0e2 fix: fixed issues involving course meeting objects not being recognized as course meeting objects (#132)
* fix: coursemeeting objects now created properly. course popup works on calendar

* refactor: removed duplicated getTimeString method in useFlattenedHook

* refactor: meeting constructor

---------

Co-authored-by: Razboy20 <razboy20@gmail.com>
2024-03-09 20:21:38 -06:00
Lukas Zenick
c51e6881d1 fix: Schedule Switching Bugs (#138)
* style: course selection fix

* fix: generic bugs on course selection

* fix: schedule switch lag when popup is open for too long

* fix: storybook bug

* chore: remove unnecessary dependency
2024-03-09 19:54:51 -06:00
Lukas Zenick
d70011016a fix: non-determinstic options page generation (#137)
* fix: non-determinstic options page generation

* fix: remove console log
2024-03-09 15:01:37 -06:00
Razboy20
f67280127a refactor: remove listWidth prop from List component and associated components (#135) 2024-03-09 00:37:31 -06:00
Razboy20
f932168f66 refactor: replace logo image imports with LogoIcon component (#134) 2024-03-09 00:36:52 -06:00
Lukas Zenick
dc100b5d3a fix: options page (#131)
* fix: options page

* chore: rename title of options page

---------

Co-authored-by: Razboy20 <razboy20@gmail.com>
2024-03-08 20:58:52 -06:00
Razboy20
10eb9e4456 chore: prettier 2024-03-07 19:46:39 -06:00
Razboy20
2f537b4f3e fix: theme colors 2024-03-07 18:36:55 -06:00
Razboy20
11303daebc fix: chromatic build 2024-03-07 18:34:35 -06:00
Razboy20
60ab140c55 fix: build 2024-03-07 18:25:57 -06:00
Razboy20
51bbd6590b revert: color palette for calendar (#118) 2024-03-07 18:20:46 -06:00
Razboy20
346b9ced97 chore: remove unused components
(and duplicated calendar components)
2024-03-07 18:08:25 -06:00
Razboy20
2992e784b0 chore: merge and fix conflicts 2024-03-07 17:53:13 -06:00
doprz
2593b371d5 chore: lint and format repo - 1 lint error left 2024-03-07 16:45:35 -06:00
Razboy20
8f360206fb chore: cleanup/resolve PR comments 2024-03-07 16:45:26 -06:00
doprz
0c44849e15 feat: refactor all components in injected 2024-03-06 15:12:44 -06:00
doprz
f93a98e46a fix: delete storybook timestamp file 2024-03-06 15:12:44 -06:00
doprz
e5443122b4 feat: refactor all components in common 2024-03-06 15:12:44 -06:00
doprz
28f192472b feat: refactor calendar 2024-03-06 15:12:40 -06:00
Abhinav Chadaga
745f9dd6fb feat: abhinavchadaga/course-catalog-popup (#128)
* some work

* some work on course popup

update the stories and create the header component

* use chip component in header

* complete CourseHeaderAndActions Component

added course buttons, using proper subcomponents now.

* Change test course to 314

* Add rmp callback

* some unocss updates

* add course button onclick handlers

* add todo for calendar button

* Rename CoursePopup

Old one to "Old", remove "2" from new one

* description stuff done

* Modify story to use proper course info

* Add Grade Distribution Stuff

* Minor tweaks

change style in header

* Add TODO

replace current grade colors with a tailwind palette

* Fix syllabi url

Remove unused variable and unnecessary args to url

* Bunch of renaming

* Kinda complete the handlers

* change grade distribution colors to match updated figma

* change from reducer pattern to state variables, remove chartData from state

* add additional story

* disabled add when course is not open

* use array fill

* Some changes with the instructor names

* trying to get the CES stuff to work

* CES button is working

* remove a todo

* add actual color for dminus

* fix description, start no distribution state

* post merge fixes

* small fixes

* fix: import as type

* fix: some better typescript stuff i think

* fix: manifest.ts

* fix: pr feedback

* fix: remove old CoursePopup component

* fix: course catalog injected popup story should useScott's 314 class since it actually has data

* fix: build error in background.ts:

* chore: run eslint autofix on CourseCatalogInjectedPopup.stories.ts

* chore: run prettier on CourseCatalogInjectedPopup.stories.ts
2024-03-06 15:12:14 -06:00
Abhinav Chadaga
471e55dcea feat: color palette for calendar (#118)
* feat: work on the palette

* feat: palette basically done?

* fix: lint warnings and errors

* fix: minor fixes

* fix: color patch colors and shades

* fix: prettier issue

* chore: use TS satisfies

* chore: remove eslint-disable comment

---------

Co-authored-by: doprz <52579214+doprz@users.noreply.github.com>
2024-03-06 15:12:13 -06:00
doprz
adbe8ac163 chore: remove custom-eslint-rules 2024-03-06 15:12:13 -06:00
doprz
e44223084a chore: lint and format repo 2024-03-06 15:12:13 -06:00
doprz
7718d76be9 chore: regenerate lock file 2024-03-06 15:12:13 -06:00
Som Gupta
f22a3cd7c0 fix: fixed bug with course cells after 12 PM extending past midnight (#122)
* Temporarily uninstalling husky cause github desktop has issues with it

* Cleaned up some code. Removed unnecessary state value on injected popup

* Should've fixed popup alignment issue. Still need to integrate course schedule with calendar. Still debugging.

* Updated CalendarGridStories

* Fix: change to ExampleCourse from exampleCourse

* setCourse and calendar header need work

* Update as part of merge

* Fix: fixed build errors

* Fix: Added Todo

* Chore: Cleaned up useFlattenedCourseSchedule hook

* fix: List now keeps track of state when existing items are switched, while adding new items to the end

* Added back husky

* Update src/views/components/calendar/Calendar/Calendar.tsx

Co-authored-by: doprz <52579214+doprz@users.noreply.github.com>

* refactor: added type-safety, destructuring, etc. ready for re-review

* refactor: got rid of ts-ignore in openNewTabFromContentScript

* Update src/views/components/calendar/CalendarHeader/CalenderHeader.tsx

Co-authored-by: doprz <52579214+doprz@users.noreply.github.com>

* refactor: using path aliasing

Co-authored-by: doprz <52579214+doprz@users.noreply.github.com>

* refactor: using path aliasing

Co-authored-by: doprz <52579214+doprz@users.noreply.github.com>

* refactor: using satisfies instead of as

Co-authored-by: doprz <52579214+doprz@users.noreply.github.com>

* refactor: using satisfies instead of as

Co-authored-by: doprz <52579214+doprz@users.noreply.github.com>

* style: reformatted spacing

* style: eslint import order

* refactor: added new constructor for UserSchedule to avoid passing down null values to child props

* fix: fixed bug with course cell times starting and after 12 PM. commented in CourseMeeting class

* Update src/views/hooks/useFlattenedCourseSchedule.ts

* fix: fixed build errors by removing old apis

* refactor: added type-safety and destructuring

---------

Co-authored-by: doprz <52579214+doprz@users.noreply.github.com>
2024-03-06 15:12:13 -06:00
doprz
5abb2a4d4f chore: fix lint errors 2024-03-06 15:12:12 -06:00
doprz
74e6379d93 chore: use eslint-plugin-import-essentials and update packages 2024-03-06 15:11:58 -06:00
Lukas Zenick
7f2a5893d4 feat: popup schedule select (#126)
* feat: sexy arrow animation

* feat: dropdown technically works

* fix: scss clarity

* feat: beautiful dropdown

* feat: proper switching; fix: no duplicates allowed

* fix: lag using async

* style: schedule options

* fix: can select same schedule again

* fix: annoying build error
2024-03-06 15:11:32 -06:00
Abhinav Chadaga
89d03f4244 feat: course-catalog-injected-popup (#98)
* some work

* some work on course popup

update the stories and create the header component

* use chip component in header

* complete CourseHeaderAndActions Component

added course buttons, using proper subcomponents now.

* Change test course to 314

* Add rmp callback

* some unocss updates

* add course button onclick handlers

* add todo for calendar button

* Rename CoursePopup

Old one to "Old", remove "2" from new one

* description stuff done

* Modify story to use proper course info

* Add Grade Distribution Stuff

* Minor tweaks

change style in header

* Add TODO

replace current grade colors with a tailwind palette

* Fix syllabi url

Remove unused variable and unnecessary args to url

* Bunch of renaming

* Kinda complete the handlers

* change grade distribution colors to match updated figma

* change from reducer pattern to state variables, remove chartData from state

* add additional story

* disabled add when course is not open

* use array fill

* Some changes with the instructor names

* trying to get the CES stuff to work

* CES button is working

* remove a todo

* add actual color for dminus

* fix description, start no distribution state

* post merge fixes

* small fixes

* fix: import as type

* fix: some better typescript stuff i think

* fix: manifest.ts

* fix: pr feedback

* Apply suggestions from code review

---------

Co-authored-by: doprz <52579214+doprz@users.noreply.github.com>
2024-03-06 15:11:32 -06:00
Lukas Zenick
0c5bec8002 fix: calendar storybook issue (#125)
Co-authored-by: doprz <52579214+doprz@users.noreply.github.com>
2024-03-06 15:11:32 -06:00
doprz
265652c420 feat: check-path-alias autofix (#124)
* feat: add autofix

* chore: autofix repo with new custom ESLint rule
2024-03-06 15:11:31 -06:00
doprz
208103d708 feat: add check-path-alias custom ESLint rule (#123) 2024-03-06 15:11:31 -06:00
Dhruv
6ba8b68654 fix: remove extra spacing (#121) 2024-03-06 15:11:31 -06:00
Samuel Gunter
eba5d9f508 feat: dividers in calendar bottom bar (#120)
* feat: added dividers to CalendarBottomBar

* fix: fix import order
2024-03-06 15:11:31 -06:00
Lukas Zenick
d9ee23c5bb feat: working PNG and CAL downloads (#119)
* working save as PNG

* cleanup

* feat(cal): working ICS file
2024-03-06 15:11:31 -06:00
doprz
d62b8d1af1 chore: lint and format the repo 2024-03-06 15:11:31 -06:00
doprz
62f0851406 fix: refactor AST parsing for custom ESLint rule 2024-03-06 15:11:30 -06:00
doprz
7ab5b157b1 chore: regenerate pnpm-lock.yaml 2024-03-06 15:11:30 -06:00
Som Gupta
a99a55788a feat: Calendar Schedule component finished, fix: list didn't allow updates when adding a new schedule (#115)
* Temporarily uninstalling husky cause github desktop has issues with it

* Cleaned up some code. Removed unnecessary state value on injected popup

* Should've fixed popup alignment issue. Still need to integrate course schedule with calendar. Still debugging.

* Updated CalendarGridStories

* Fix: change to ExampleCourse from exampleCourse

* setCourse and calendar header need work

* Update as part of merge

* Fix: fixed build errors

* Fix: Added Todo

* Chore: Cleaned up useFlattenedCourseSchedule hook

* fix: List now keeps track of state when existing items are switched, while adding new items to the end

* Added back husky

* Update src/views/components/calendar/Calendar/Calendar.tsx

Co-authored-by: doprz <52579214+doprz@users.noreply.github.com>

* refactor: added type-safety, destructuring, etc. ready for re-review

* refactor: got rid of ts-ignore in openNewTabFromContentScript

* Update src/views/components/calendar/CalendarHeader/CalenderHeader.tsx

Co-authored-by: doprz <52579214+doprz@users.noreply.github.com>

* refactor: using path aliasing

Co-authored-by: doprz <52579214+doprz@users.noreply.github.com>

* refactor: using path aliasing

Co-authored-by: doprz <52579214+doprz@users.noreply.github.com>

* refactor: using satisfies instead of as

Co-authored-by: doprz <52579214+doprz@users.noreply.github.com>

* refactor: using satisfies instead of as

Co-authored-by: doprz <52579214+doprz@users.noreply.github.com>

* style: reformatted spacing

* style: eslint import order

* refactor: added new constructor for UserSchedule to avoid passing down null values to child props

---------

Co-authored-by: doprz <52579214+doprz@users.noreply.github.com>
2024-03-06 15:11:30 -06:00
doprz
3a48859ddd fix: openTabFromContentScript TS issue 2024-03-06 15:11:30 -06:00
doprz
ced29975b2 fix: chromatic storybook - CourseCatalogInjectedPopup (#106)
* chore: add pnpm peer deps

* fix: merge conflict and rebase

* chore: remove deps
2024-03-06 15:11:29 -06:00
Dhruv
84e8320e8f fix: divider usage in HeaderAndActions (#113)
* fix: use StatusType

* fix: use AutoLoadStatus

* fix: use typof SiteSupport.*

* fix: one more of Status to StatusType

* fix: use import type

* fix: use path alias imports

* fix: use Extract

* fix: remove unnecessary import

* fix: **revert this later** - comment out build errs

* fix: add schedule to story

* feat: add props for dividers

* revert: un-comment build errors

This reverts commit 082e9e0269.

* Revert "fix: remove unnecessary import"

This reverts commit 9230346d26.

* Revert "fix: use Extract"

This reverts commit f6aa80d411.

* Revert "fix: use path alias imports"

This reverts commit ea9bf3c635.

* Revert "fix: use import type"

This reverts commit 27fee47778.

* Revert "Merge branch 'fix/Status-to-StatusType' into fix/divider-usage"

This reverts commit b1715ea360, reversing
changes made to 9ccc43ca27.

* fix: dont use magic number

* fix: lint errs

* fix: reorder imports
2024-03-06 15:11:29 -06:00
Dhruv
5f1c0231e4 fix: remote react-window fully (from List component) (#114)
* chore: pnpm i react-window

* Revert "chore: pnpm i react-window"

This reverts commit 2234d39dbb.

* fix: remote ref to react-window (for good)
2024-03-06 15:11:29 -06:00
Dhruv
b17c3fae6d fix: cleanup imports (#112)
* fix: use path alias

* fix: more path alias

* fix: even more path aliasing

* fix: even moreeeee path aliasing

* fix: sort imports

* fix: sort imports (again)
2024-03-06 15:11:29 -06:00
Dhruv
19fe070491 fix: type issues by using correct import (#111)
* fix: use StatusType

* fix: use AutoLoadStatus

* fix: use typof SiteSupport.*

* fix: one more of Status to StatusType

* fix: use import type

* fix: use path alias imports

* fix: use Extract

* fix: remove unnecessary import
2024-03-06 15:11:29 -06:00
doprz
8c069b7ad3 feat: add custom ESLint rule restrict-import-depth (#110) 2024-03-06 15:11:28 -06:00
Dhruv
87799d8f02 feat: fix icons on PopupMain and convert to tailwind (#108)
* feat: fix icons on PopupMain

* fix: use text-white

* fix: move TestColors

* fix: convert to tailwind

* fix: finish moving TestColors out

* chore: add path alias

* chore: lint PR

* feat: create storybook.ts and move tailwindColorways there

---------

Co-authored-by: doprz <52579214+doprz@users.noreply.github.com>
2024-03-06 15:11:28 -06:00
07ec5abc3e feat: alignment on calendar and header (#109)
* feat: corrected date updated text

* feat: whew alignment looks way better

* feat: aligned the bars and made the latter half justify-end

* fix: limt

* fix: removed broken divider vinson did u do this
2024-03-06 15:11:28 -06:00
doprz
ee37897df4 fix: vitest path alias bug 2024-03-06 15:11:27 -06:00
doprz
8a6e9070e0 chore: lint-format-docs-tests-bugfixes (#105)
* docs: add jsdoc

* feat: change enums to as const objects

* chore(test): add themeColors.test.ts

* fix: fix tests and bugs with strings.ts util

* fix: path alias imports and tsconfig file bug

* fix: remove --max-warnings 0
2024-03-06 15:11:27 -06:00
Samuel Gunter
8ab60c9f01 feat: updated divider component (#99)
* feat: updated divider component

* refactor: inlined Divider's classes, simplified stories

* fix: style to unocss in story

* style: renamed variant to orientation for buttons

* docs: updated comments in Divider after prop name change
2024-03-06 15:11:27 -06:00
doprz
5eb7be246c feat: Best Practices (#102)
* feat: best practices

* feat: add tests workflow

* feat: add best-practices workflow

* fix: wrong indentation in workflow
2024-03-06 15:11:27 -06:00
doprz
fe599dfe75 feat: Conventional Commits (#103)
* feat: add commitlint and husky hook

* chore: fix indentation
2024-03-06 15:11:27 -06:00
knownotunknown
23632d3c09 chore: update CalendarGrid.tsx 2024-03-06 15:11:21 -06:00
knownotunknown
2a0150600f fix: bugs 2024-03-06 15:10:51 -06:00
4d387e8063 fix: old icon removed in .tsx 2024-03-06 15:10:42 -06:00
3b588c2039 feat: icon added successfully 2024-03-06 15:10:36 -06:00
Samuel Gunter
e73c9fe417 fix: change Chromatic action to build current branch, not base branch (#100) 2024-03-06 15:10:36 -06:00
knownotunknown
35fab34445 feat: can open tabs, updated injected popup heading. basically done 2024-03-06 15:10:30 -06:00
knownotunknown
35f3c72250 feat: chrome extension works 2024-03-06 15:09:30 -06:00
knownotunknown
c4a738f281 Squashed commit of the following:
commit c46e4a51c9
Author: Abhinav Chadaga <abhinav.chadaga@utexas.edu>
Date:   Mon Feb 19 21:37:46 2024 -0600

    change from reducer pattern to state variables, remove chartData from state

commit 36bcdd2522
Author: Abhinav Chadaga <abhinav.chadaga@utexas.edu>
Date:   Mon Feb 19 21:15:41 2024 -0600

    change grade distribution colors to match updated figma

commit 11a50df88d
Merge: c16b301 b4c96a9
Author: Abhinav Chadaga <abhinav.chadaga@utexas.edu>
Date:   Mon Feb 19 17:57:13 2024 -0600

    Merge branch 'hackathon' into abhinavchadaga/course-catalog-popup

commit c16b301ff0
Author: Abhinav Chadaga <abhinav.chadaga@utexas.edu>
Date:   Mon Feb 19 17:47:21 2024 -0600

    Kinda complete the handlers

commit 1ac1d9095a
Author: Abhinav Chadaga <abhinav.chadaga@utexas.edu>
Date:   Sun Feb 18 17:36:59 2024 -0600

    Bunch of renaming

commit 925829ad41
Author: Abhinav Chadaga <abhinav.chadaga@utexas.edu>
Date:   Sun Feb 18 17:24:53 2024 -0600

    Fix syllabi url

    Remove unused variable and unnecessary args to url

commit f2e5d51eb3
Author: Abhinav Chadaga <abhinav.chadaga@utexas.edu>
Date:   Sun Feb 18 17:24:22 2024 -0600

    Add TODO

    replace current grade colors with a tailwind palette

commit 747ee44440
Author: Abhinav Chadaga <abhinav.chadaga@utexas.edu>
Date:   Sun Feb 18 01:26:51 2024 -0600

    Minor tweaks

    change style in header

commit ddfe952a32
Author: Abhinav Chadaga <abhinav.chadaga@utexas.edu>
Date:   Sun Feb 18 01:26:38 2024 -0600

    Add Grade Distribution Stuff

commit c27bf3c390
Author: Abhinav Chadaga <abhinav.chadaga@utexas.edu>
Date:   Sun Feb 18 01:26:13 2024 -0600

    Modify story to use proper course info

commit 7afdbac1b8
Author: Abhinav Chadaga <abhinav.chadaga@utexas.edu>
Date:   Sat Feb 17 16:37:01 2024 -0600

    description stuff done

commit 1a89432276
Author: Abhinav Chadaga <abhinav.chadaga@utexas.edu>
Date:   Sat Feb 17 15:26:32 2024 -0600

    Rename CoursePopup

    Old one to "Old", remove "2" from new one

commit 4c2b31e61a
Author: Abhinav Chadaga <abhinav.chadaga@utexas.edu>
Date:   Sat Feb 17 15:23:01 2024 -0600

    add todo for calendar button

commit 11b7a51ded
Author: Abhinav Chadaga <abhinav.chadaga@utexas.edu>
Date:   Sat Feb 17 15:22:18 2024 -0600

    add course button onclick handlers

commit f2dfcec838
Author: Abhinav Chadaga <abhinav.chadaga@utexas.edu>
Date:   Sat Feb 17 14:52:38 2024 -0600

    some unocss updates

commit f9f375514b
Author: Abhinav Chadaga <abhinav.chadaga@utexas.edu>
Date:   Sat Feb 17 13:00:46 2024 -0600

    Add rmp callback

commit 122fc6dbdd
Author: Abhinav Chadaga <abhinav.chadaga@utexas.edu>
Date:   Sat Feb 17 13:00:16 2024 -0600

    Change test course to 314

commit 19b124b3bd
Author: Abhinav Chadaga <abhinav.chadaga@utexas.edu>
Date:   Sat Feb 17 12:19:21 2024 -0600

    complete CourseHeaderAndActions Component

    added course buttons, using proper subcomponents now.

commit 2eea01fc74
Author: Abhinav Chadaga <abhinav.chadaga@utexas.edu>
Date:   Sat Feb 17 11:22:12 2024 -0600

    use chip component in header

commit 9cb13c8fd1
Merge: a62b718 9392085
Author: Abhinav Chadaga <abhinav.chadaga@utexas.edu>
Date:   Sat Feb 17 11:21:12 2024 -0600

    Merge branch 'hackathon' into abhinavchadaga/course-catalog-popup

commit a62b718c43
Merge: 43d2675 7b7b858
Author: Abhinav Chadaga <abhinav.chadaga@utexas.edu>
Date:   Sat Feb 17 10:57:24 2024 -0600

    Merge branch 'hackathon' into abhinavchadaga/course-catalog-popup

commit 43d2675be5
Author: Abhinav Chadaga <abhinav.chadaga@utexas.edu>
Date:   Sat Feb 17 10:54:49 2024 -0600

    some work on course popup

    update the stories and create the header component

commit 31bcef3099
Merge: 874f8d5 fa1d737
Author: Abhinav Chadaga <abhinav.chadaga@utexas.edu>
Date:   Wed Feb 14 14:33:16 2024 -0600

    Merge branch 'main' into abhinavchadaga/course-catalog-popup

    pulling from main

commit 874f8d56cb
Author: Abhinav Chadaga <abhinav.chadaga@utexas.edu>
Date:   Wed Feb 14 14:30:24 2024 -0600

    some work
2024-03-06 15:09:03 -06:00
doprz
e0212d5109 feat: add Calendar Component 2024-03-06 15:09:01 -06:00
knownotunknown
152bc45776 fix: import error 2024-03-06 15:07:47 -06:00
doprz
91d930ee92 chore: merge in finished CalendarGrid 2024-03-06 15:07:45 -06:00
knownotunknown
c6a48dd3f6 fix: build errors and restructure Calendar page 2024-03-06 15:05:48 -06:00
knownotunknown
39947b3694 fix: build errors and merge in Casey's branch (driodiwb) 2024-03-06 15:05:38 -06:00
doprz
8df9ea55a9 feat: use React-icons 2024-03-06 15:05:35 -06:00
DhruvArora-03
bec2649fc1 feat: add button to the rows, use new ConflictsWithWarning component 2024-03-06 15:05:02 -06:00
DhruvArora-03
1599e48d75 fix: ConflictsWithWarning 2024-03-06 15:04:57 -06:00
Lukas Zenick
bda02826b1 feat: html2canvas -> htmlToImage and fix derick's bugs 2024-03-06 15:04:32 -06:00
58c2b4634a feat: Derek/export png (#95)
* Attempting to use more lightweight version

* Did not work.

* This is not what I wanted

* The image saves correctly. Needs padding

* Padding !!

* Removed downloadjs

* Padding more
2024-03-06 15:04:31 -06:00
Lukas Zenick
a30fecf8ec fix: README.md 2024-03-06 15:04:24 -06:00
Lukas Zenick
b27b21bbfc fix: README.md 2024-03-06 15:04:15 -06:00
12d09b54cb feat: Derek vinson/calendar header (#94)
* CalendarHeader alignment progress

* Boom

* css

* Between

* Lol

* Gap fix

* whitespace-nowrap

* Gaps

* Finished alignment of CourseStatus and Buttons

* Colors

* ESLint auto format

* Color UT Registration Plus text

* Reverting vscode

---------

Co-authored-by: Vinson Zheng <vinsonzheng499@gmail.com>
2024-03-06 15:04:15 -06:00
Abhinav Chadaga
25b35846e9 sort the flattened course schedule (#93)
first by dayIndex, then startIndex, then endIndex.
2024-03-06 15:04:14 -06:00
DhruvArora-03
0f43796cd8 fix: extra space 2024-03-06 15:04:09 -06:00
doprz
0f730d6c50 feat: bottom bar for the calendar page (#91) 2024-03-06 15:04:00 -06:00
Lukas Zenick
92462cf0df fix: broken file 2024-03-06 15:03:14 -06:00
DhruvArora-03
c49b094068 chore: reorder classnames 2024-03-06 15:03:03 -06:00
Lukas Zenick
b34aacb067 fix: broken close bracket 2024-03-06 15:02:56 -06:00
vivek12311
103d2e0323 chore: add styled-components 2024-03-06 15:02:30 -06:00
vivek12311
ad83ba4cdc feat: create empty Popup story 2024-03-06 15:02:03 -06:00
doprz
e95ac42d57 chore: WIP 2024-03-06 15:01:59 -06:00
Lukas Zenick
ad18fbd162 feat: save as PNG functionality 2024-03-06 15:00:48 -06:00
Lukas Zenick
bc3054aa43 chore: adjust location of svg files 2024-03-06 15:00:30 -06:00
Lukas Zenick
53e7c7feda feat: add cal save buttons (no functionality) 2024-03-06 15:00:23 -06:00
DhruvArora-03
f8691207e3 chore: cleanup useFlattenedCourseSchedule 2024-03-06 15:00:18 -06:00
knownotunknown
5ad72af566 chore: add courseCells and improve types 2024-03-06 14:59:29 -06:00
Casey Charleston
d1a336e903 feat: add Calendar schedules component (clicking on storybook not working) 2024-03-06 14:59:21 -06:00
DhruvArora-03
5be79730b3 chore: working on course popup - stopping because abhinav already doing 2024-03-06 14:59:00 -06:00
DhruvArora-03
9d0f210548 feat: add empty settings component - waiting on design 2024-03-06 14:58:46 -06:00
Abhinav Chadaga
387b508e40 chore: update CalendarCourseCell.tsx 2024-03-06 14:58:27 -06:00
doprz
0ed8d9c82d chore: add args to default story 2024-03-06 14:58:24 -06:00
doprz
23276e5c7c feat: add CalendarHeader and its Storybook, need to resize 2024-03-06 13:49:32 -06:00
doprz
37b5101e44 chore: update default story 2024-03-06 13:48:12 -06:00
Razboy20
f04ff2fe26 chore: update course block names 2024-03-06 13:47:36 -06:00
doprz
13c69ef862 fix: CalendarCourseCell spacing 2024-03-06 13:47:34 -06:00
knownotunknown
27d945f57c fix: revert last commit
This reverts commit 3c7b35d5f3.
2024-03-06 13:46:12 -06:00
knownotunknown
aef8c3d987 fix: revert "chore: add default story"
This reverts commit 8edd062588.
2024-03-06 13:45:47 -06:00
doprz
115d794ef9 chore: add default story 2024-03-06 13:45:44 -06:00
Abhinav Chadaga
bc935cc80a chore: update CalendarCourseCell.tsx 2024-03-06 13:44:15 -06:00
Abhinav Chadaga
e54f488b17 feat: implement flatten course schedule helper function
takes a course schedule and returns an array of objects that can be used to render all the individual course
2024-03-06 13:44:09 -06:00
Razboy20
cc7138949c fix: margins on list component 2024-03-06 13:44:05 -06:00
Razboy20
74be880f9d fix: calendar course cell colors 2024-03-06 13:43:57 -06:00
DhruvArora-03
5dbee6f0c3 feat: add ImportantLinks Component 2024-03-06 13:43:34 -06:00
knownotunknown
cbb190bf4b fix: made list draggable only by handle 2024-03-06 13:42:55 -06:00
Razboy20
4375118cd9 chore: add eslint on save 2024-03-06 13:42:37 -06:00
Lukas Zenick
9aa78a02a5 fix: undefined color case 2024-03-06 13:42:31 -06:00
Razboy20
1269731b55 chore: update button stylings 2024-03-06 13:42:26 -06:00
Razboy20
79d7832d09 fix: unocss theme color namings 2024-03-06 13:42:17 -06:00
knownotunknown
64fa12b10c chore: use export function instead of React.FC 2024-03-06 13:41:23 -06:00
doprz
56306ab944 fix: revert CalendarGrid and CalendarGridCell back to SCSS from Tailwind 2024-03-06 13:41:21 -06:00
DhruvArora-03
923c673988 chore(docs): add JSdoc to calendar page 2024-03-06 13:40:22 -06:00
doprz
7e2f5eaed7 feat: migrate styles to TailwindCSS 2024-03-06 13:39:53 -06:00
4dc8957c45 fix: align timeBlock div 2024-03-06 13:38:47 -06:00
cbb89c0db5 chore: add comment 2024-03-06 13:38:20 -06:00
ecdaadb83e fix: update daysOfWeek with new DAY_MAP keys 2024-03-06 13:37:59 -06:00
b480fa28b2 chore: shorten DAY_MAP keys 2024-03-06 13:37:12 -06:00
8493b482c4 chore: refactor 2024-03-06 13:36:37 -06:00
18406b0c94 fix: add margin-top: -10px to p
Correcting text
2024-03-06 13:36:11 -06:00
8b9cb065c2 feat: add tickmarks to day div 2024-03-06 13:35:36 -06:00
7ee732b31e chore: polish up code 2024-03-06 13:35:14 -06:00
e49fc295ba feat: finally fix grid JSX.Element generation 2024-03-06 13:34:51 -06:00
b691bf3231 fix: grid JSX.Element generation 2024-03-06 13:34:17 -06:00
0777b822b3 chore: refactor grid with Array.from() 2024-03-06 13:32:18 -06:00
fc5af56bb7 chore: fix alignment 2024-03-06 13:32:04 -06:00
doprz
c9d46b60ec chore: small style adjustments 2024-03-06 13:32:00 -06:00
fd91c3b12e feat: add story for CalendarGridCell 2024-03-06 13:31:11 -06:00
1bb6191244 feat: align day labels and add to grid 2024-03-06 13:30:32 -06:00
063349d96d feat: use filter() instead of pop() 2024-03-06 13:30:05 -06:00
d377afbf8b chore: format correctly 2024-03-06 13:29:14 -06:00
ded3d96aae chore: revert settings.json 2024-03-06 13:29:02 -06:00
doprz
56f6456ce8 feat: add CSS for timeLabelContainer div 2024-03-06 13:28:59 -06:00
95e0544b73 feat: add timeAndGrid div 2024-03-06 13:27:48 -06:00
9d6821127e feat: add time column to Calendar Component 2024-03-06 13:27:15 -06:00
doprz
0ba61534cb feat: add CSS for hourLine div 2024-03-06 13:27:13 -06:00
doprz
6cdcf4930d feat: add CSS for calendarCell div 2024-03-06 13:26:48 -06:00
b535a6eb32 feat: use display: grid for calendarGrid 2024-03-06 13:21:38 -06:00
doprz
0036dc5c80 chore: trying some CSS 2024-03-06 13:21:35 -06:00
doprz
dcc8a6d249 chore: refactor 2024-03-06 13:13:32 -06:00
203b3bb340 chore: center div using display flex 2024-03-06 13:12:02 -06:00
200f67a1eb chore: revert changes in stylesheet 2024-03-06 13:11:39 -06:00
doprz
9105bcba15 chore: add css class 2024-03-06 13:11:36 -06:00
doprz
07f75914bf chore: refactor 2024-03-06 13:10:20 -06:00
doprz
cb3cb5d5fc feat: add Storybook story 2024-03-06 13:08:57 -06:00
doprz
4165d484bf chore: refactor 2024-03-06 13:06:46 -06:00
doprz
7f138dafd0 chore: colors modules switch 2024-03-06 13:04:55 -06:00
doprz
5a8f6a8f1f chore: add docs 2024-03-06 13:03:05 -06:00
doprz
e7ce014c70 chore: format 2024-03-06 13:00:38 -06:00
doprz
40ece9f425 chore: use camelCase for CSS modules 2024-03-06 12:59:44 -06:00
DhruvArora-03
bd73e27db4 chore: undo dummy commit 2024-03-06 12:55:42 -06:00
DhruvArora-03
0695c70dbf chore: dummy commit 2024-03-06 12:55:10 -06:00
knownotunknown
9e0f9df9de feat: convert all LabelsAndDetails Components to Tailwind 2024-03-06 12:54:58 -06:00
Samuel Gunter
8e3aa7ef33 fix: only show button hover effects when not disabled 2024-03-06 12:54:57 -06:00
Samuel Gunter
1fdbe6294e chore: suppress useless TS warnings 2024-03-06 12:54:57 -06:00
doprz
93b65ac2ed feat: add buttons with icons in tailwind 2024-03-06 12:54:30 -06:00
doprz
28d93b3c25 feat: add tailwind version of Button component 2024-03-06 12:52:56 -06:00
doprz
863521fb3b feat: update Button to v2 design 2024-03-06 12:51:09 -06:00
doprz
837fddf804 fix: non-virtual dnd 2024-03-06 10:48:51 -06:00
knownotunknown
677aa624d7 feat: early iteration of non-virtual list 2024-03-06 10:47:32 -06:00
knownotunknown
d390af3c79 chore: update CalendarCourse.stories.tsx 2024-03-06 10:47:26 -06:00
knownotunknown
6af805ba3a fix: eslint and remove React-beautiful-dnd 2024-03-06 10:46:52 -06:00
doprz
cd34601379 feat: made List more extensible 2024-03-06 10:46:47 -06:00
doprz
b800c58502 chore: update packages 2024-03-06 10:45:53 -06:00
knownotunknown
34a6449529 feat: drag only on vertical axis
Made this change a while ago, but didn't push since it looks like we might switch to Elie's DnD. Pushing since it looks like there might still be reviewers.
2024-03-06 10:44:28 -06:00
knownotunknown
0273a23913 fix: revert "rename to course block and fix line height for styling"
This reverts commit 77a1d67af3.
2024-03-06 10:44:15 -06:00
doprz
e6b4049403 feat: add List component 2024-03-06 10:44:09 -06:00
abhinavchadaga
b602b0b895 fix: rename to course block and fix line height for styling 2024-03-06 10:41:32 -06:00
abhinavchadaga
4ca97abd06 chore: change the default course 2024-03-06 10:41:20 -06:00
abhinavchadaga
da9e7aac41 feat: basic CalendarCourseMeeting component laid out - missing Text and Right Icon 2024-03-06 10:39:58 -06:00
knownotunknown
3568b8eb5e feat: change Chip to tailwind css. Fixed eslint for ConflictsWithWarning 2024-03-06 10:39:45 -06:00
knownotunknown
fa05d9c492 chore: update Chip.stories.tsx 2024-03-06 10:39:35 -06:00
knownotunknown
7cf34f2956 chore: delete unnecessary imports 2024-03-06 10:39:20 -06:00
knownotunknown
23e881f14c feat: implement Chip component
Styled inline since I'll need to rewrite in TailwindCSS anyways
2024-03-06 10:38:48 -06:00
knownotunknown
7eb3113ada fix: update alignment 2024-03-06 10:37:57 -06:00
knownotunknown
f3a8a7db56 feat: update with TailwindCSS 2024-03-06 10:36:59 -06:00
knownotunknown
12f680d7e9 feat: finish ScheduleTotalHoursAndCourses 2024-03-06 10:27:44 -06:00
knownotunknown
21b643000d feat: implemented InfoCard 2024-03-06 10:27:20 -06:00
knownotunknown
7dec3c0c2a chore: update ConflictsWithWarning.tsx 2024-03-06 10:27:10 -06:00
knownotunknown
93f3a307b4 feat: implemented ConflictsWithWarning 2024-03-06 10:26:40 -06:00
knownotunknown
babc925967 feat: build without errors 2024-03-06 10:26:12 -06:00
Som Gupta
58d7df499c feat: CourseStatus Component implemented (#83)
Co-authored-by: Razboy20 <razboy20@gmail.com>
2024-03-06 10:26:11 -06:00
Razboy20
b0512e392b ci: update pnpm action (#86)
* ci: update pnpm action

* hotfix: change action type
2024-03-06 10:26:11 -06:00
Razboy20
b7878d81c6 ci: chromatic builds (#84)
* ci: chromatic builds

* hotfix: add pnpm install to workflow

* hotfix: add pnpm version
2024-03-06 10:26:11 -06:00
Dhruv
9accd17bd4 feat: PopupCourseBlock Component (#79)
Co-authored-by: Razboy20 <Razboy20@users.noreply.github.com>
Co-authored-by: Razboy20 <razboy20@gmail.com>
2024-03-06 10:26:11 -06:00
doprz
bfeb2398aa feat: calendar grid and grid cells (#81) 2024-03-06 10:26:05 -06:00
Razboy20
2321540e97 refactor: replace classnames with clsx (#78) 2024-03-06 10:24:58 -06:00
Dhruv
ccea0f4bd1 feat: Create icon helper (#77)
* create icon helper

* change getStatusIcon to StatusIcon react component

---------

Co-authored-by: Razboy20 <razboy20@gmail.com>
2024-03-06 10:24:57 -06:00
Abhinav Chadaga
00e00197ef feat: calendar course block component (#75) 2024-03-06 10:24:57 -06:00
Razboy20
85c7f7817c feat: UnoCSS (TailwindCSS) (Storybook only) (#61)
* feat: unocss (wip)

* feat: unocss (storybook only)

* cleanup unocss config

* revert button.stories.tsx changes
2024-03-06 10:24:57 -06:00
Razboy20
bb3b313fd2 fix: icon library resolution (#74) 2024-03-06 10:24:52 -06:00
Razboy20
52e34cb830 fix: change material icons to material symbols (#71) 2024-03-06 10:24:42 -06:00
Samuel Gunter
8b8433deaf feat: updated Text component to latest design specification (#70) 2024-03-06 10:24:41 -06:00
doprz
4faca8c43b feat: calendar components 3rd attempt at merging (#60) 2024-03-06 10:24:27 -06:00
Razboy20
4455b10cc7 chore: update dependencies (#67) 2024-03-06 10:20:40 -06:00
Razboy20
2d67b1218f feat: unplugin-icons (#62) 2024-03-06 10:20:40 -06:00
Som Gupta
fa1d7374bc feat: CourseStatus Component implemented (#83)
Co-authored-by: Razboy20 <razboy20@gmail.com>
2024-02-10 21:26:21 -06:00
Razboy20
c122e744ef ci: update pnpm action (#86)
* ci: update pnpm action

* hotfix: change action type
2024-02-09 18:39:20 -06:00
Razboy20
708a136a5c ci: chromatic builds (#84)
* ci: chromatic builds

* hotfix: add pnpm install to workflow

* hotfix: add pnpm version
2024-02-09 17:30:12 -06:00
Dhruv
f045b400a5 feat: PopupCourseBlock Component (#79)
Co-authored-by: Razboy20 <Razboy20@users.noreply.github.com>
Co-authored-by: Razboy20 <razboy20@gmail.com>
2024-02-08 11:52:29 -06:00
dd2f696f8d feat: Calendar Grid and Grid Cells (#81) 2024-02-05 23:04:24 -06:00
Razboy20
b2b6a06280 refactor: replace classnames with clsx (#78) 2024-02-05 21:27:22 -06:00
Dhruv
1b51d65c89 feat: Create icon helper (#77)
* create icon helper

* change getStatusIcon to StatusIcon react component

---------

Co-authored-by: Razboy20 <razboy20@gmail.com>
2024-02-05 18:24:02 -06:00
Abhinav Chadaga
a41cb3ed87 feat: calendar course block component (#75) 2024-02-03 16:43:59 -06:00
Razboy20
6521a4b2a9 feat: UnoCSS (TailwindCSS) (Storybook only) (#61)
* feat: unocss (wip)

* feat: unocss (storybook only)

* cleanup unocss config

* revert button.stories.tsx changes
2024-02-03 15:21:19 -06:00
Razboy20
e015a79526 hotfix: icon library resolution (#74) 2024-02-03 14:21:25 -06:00
Razboy20
359e65496f hotfix: change material icons to material symbols (#71) 2024-02-03 13:42:32 -06:00
Samuel Gunter
bb727f70be feat: updated Text component to latest design specification (#70) 2024-02-03 10:20:58 -06:00
ab2cd688fa feat: Calendar Components 3rd Attempt at Merging (#60) 2024-02-03 10:20:30 -06:00
Razboy20
f5e8fb5782 chore: update dependencies (#67) 2024-02-02 20:12:55 -06:00
Razboy20
945e09b49e feat: unplugin-icons (#62) 2024-02-02 11:55:33 -06:00
72b7a9d7b1 Calendar Components 3rd Attempt at Merging 2024-01-30 21:17:19 -06:00
Razboy20
9cc299ced6 feat: Storybook for Vite (#52)
* feat: storybook

* feat: option to add figma links

* refactor: resolve pr comments

* chore: fix storybook build

---------

Co-authored-by: Sriram Hariharan <sghsri@gmail.com>
2024-01-30 16:43:30 -06:00
Razboy20
0560a01a55 refactor: Replace Webpack with Vite (#53) 2024-01-24 19:40:30 -06:00
Sriram Hariharan
1629c85818 Merge pull request #47 from doprz/utrp-v2 2023-12-29 10:14:37 -06:00
doprz
5e98f45210 feat: Refactor database initialization code 2023-12-23 09:26:49 -06:00
Sriram Hariharan
56643f9753 bunch of misc changes 2023-11-17 11:11:01 -06:00
Sriram Hariharan
52431747ee wrote all course/schedule background messages with handlers 2023-09-17 20:58:22 -05:00
Sriram Hariharan
6061295e0a simplified typing 2023-09-17 20:00:28 -05:00
Sriram Hariharan
89423d24b4 minor changes 2023-09-17 19:44:50 -05:00
Sriram Hariharan
4f170db07d fixes + added button titles 2023-09-17 19:38:12 -05:00
Sriram Hariharan
aea9b16f98 refactoring courseschedule storage 2023-09-17 19:29:00 -05:00
Sriram Hariharan
9658697d96 fxied 2023-09-17 15:25:14 -05:00
Sriram Hariharan
864afd8dcb created my calendar main 2023-09-17 15:21:45 -05:00
Sriram Hariharan
1fac71dbd1 generating calendar html 2023-09-17 15:20:42 -05:00
Sriram Hariharan
e199a0b766 fixing errors 2023-09-17 14:55:33 -05:00
Sriram Hariharan
4ae2966e98 not using param 2023-09-17 14:29:18 -05:00
Sriram Hariharan
e2c9955b41 inline database destructure 2023-09-17 14:28:45 -05:00
doprz
9f1dcc667d feat: parallelize initializeDB.ts promises 2023-04-18 10:30:13 -05:00
Sriram Hariharan
fcfda3a447 Merge pull request #45 from doprz/bugfix/ModuleNotFoundError-CaseSensitivePathsPlugin
fix: #44 Module Not Found Error
2023-03-30 13:12:01 -05:00
doprz
486b2e4dfc bugfix: fix #44 2023-03-30 10:33:40 -05:00
Sriram Hariharan
ad85c2b816 adding tooltip with which class has conflicts 2023-03-22 22:48:14 -05:00
Sriram Hariharan
2ddfde2642 course conflict highlighting and calculations 2023-03-22 22:16:51 -05:00
Sriram Hariharan
882b5b4e00 schedules working 2023-03-16 00:32:10 -05:00
Sriram Hariharan
6afd372945 multiple schedule suppport kinda 2023-03-15 23:54:07 -05:00
Sriram Hariharan
6d4a4307cf RecruitmentBanner, thank you Lukas 2023-03-12 23:34:58 -05:00
Sriram Hariharan
fe4f0e7ecd infra changes 2023-03-11 22:03:25 -06:00
Sriram Hariharan
32b73da959 moved stores back into shared 2023-03-10 23:38:39 -06:00
Sriram Hariharan
d06b0f9f7a infrastructure for multiple schedules 2023-03-10 23:37:57 -06:00
Sriram Hariharan
f28ab5182c better select styling for semesters for grade dist 2023-03-09 22:26:56 -06:00
Sriram Hariharan
e60242198a query grade distributions working, and filtering by semesters working 2023-03-09 16:11:42 -06:00
Sriram Hariharan
5be0cbbbf1 test 2023-03-09 11:02:30 -06:00
Sriram Hariharan
2462d24214 removed icon from gitignore 2023-03-09 10:59:24 -06:00
Sriram Hariharan
7dd53f3a94 removed the necessity for hardcoded line height variables 2023-03-08 21:12:14 -06:00
Sriram Hariharan
c1910bacb0 finished grade distribution ui 2023-03-08 20:44:34 -06:00
Sriram Hariharan
2562e65d66 some styling 2023-03-08 00:41:04 -06:00
Sriram Hariharan
fe8c2378d2 displaying bar graph, fetching distributions message 2023-03-08 00:26:56 -06:00
Sriram Hariharan
f3bf746c6e textbook button, properly scraping the semester (year and season) from the url" 2023-03-07 22:17:17 -06:00
Sriram Hariharan
353c43c987 lot of refactoring, reorganized course buttons. Now linking to professors directory page 2023-03-07 21:49:41 -06:00
Sriram Hariharan
04a82fb6a6 bunch of refactor and styling changes, coming along nicely 2023-03-07 01:42:26 -06:00
Sriram Hariharan
f48f39e67b refactoring 2023-03-06 23:28:21 -06:00
Sriram Hariharan
1fa67f451a auto generate classes using scss magic, some restyling and refactoring' 2023-03-06 23:28:08 -06:00
Sriram Hariharan
ebeb7d692b created reusable button component, created course info header component, created utility type for Colors, removed typescript-css-modules plugin, and added a threshold to the infinite scroll hook 2023-03-06 22:45:34 -06:00
Sriram Hariharan
950c4a573a added onclose to popup, coursepopup now displaying time info, renamed vars, added compiler for scss to typescript and tsconfig plugins 2023-03-06 21:02:29 -06:00
Sriram Hariharan
9b76f8afa0 line height text, refactored course schedule, added string representation functions to course meeting 2023-03-06 16:51:46 -06:00
Sriram Hariharan
007ade81a0 minor styling 2023-03-06 00:23:20 -06:00
Sriram Hariharan
8b5fabce0c properly generating and formating instructor text 2023-03-06 00:10:03 -06:00
Sriram Hariharan
7401138d87 fixed serialization type in chrome-extension-toolkit, and then updated package version 2023-03-05 23:58:16 -06:00
Sriram Hariharan
ad8a06d831 Link component, Card component, Course Popup component styling, and wrangling with the serialization type" 2023-03-05 22:52:11 -06:00
Sriram Hariharan
6d69cd2548 renamed panel to popup since that's kinda what it is lmao 2023-03-05 20:50:40 -06:00
Sriram Hariharan
295b466505 fixed bug with material icons, and reusable icon component 2023-03-05 19:54:59 -06:00
Sriram Hariharan
1f2374927d added material icons 2023-03-05 17:46:25 -06:00
Sriram Hariharan
6147289b40 created reusable text component, and setup typing for it automatically. also fixed bugs with autoload and scraper so that it would handle appending course name headers 2023-03-05 17:34:56 -06:00
Sriram Hariharan
0956525e94 auto-loading completely done 2023-03-05 14:34:26 -06:00
Sriram Hariharan
2b952d0591 minor reorg 2023-03-04 23:33:59 -06:00
Sriram Hariharan
15e9ff92a8 moved files around 2023-03-04 23:33:39 -06:00
Sriram Hariharan
b7c3d22961 minor changes 2023-03-04 23:24:50 -06:00
Sriram Hariharan
9dbe0d7ff7 passing a scss classname down to a vanilla dom element on react state changes 🤯 2023-03-04 23:20:12 -06:00
Sriram Hariharan
d9739cdb56 decided to add season support 2023-03-04 23:00:01 -06:00
Sriram Hariharan
bc464cd264 lots of UI changes, and keyboard command support 2023-03-04 22:42:51 -06:00
Sriram Hariharan
00b8cd74b6 added ExtensionRoot for consistent styling across injected components, and added a bunch of comments for all the added types and classes 2023-03-04 21:11:04 -06:00
Sriram Hariharan
070c8ea486 added ExtensionRoot for consistent styling across injected components, and added a bunch of comments for all the added types and classes 2023-03-04 21:10:12 -06:00
Sriram Hariharan
46282a0406 added deviceId, ExtensionStore, working on CoursePopup 2023-03-04 20:33:35 -06:00
Sriram Hariharan
e99ba5864a CourseScraper completely done
'
2023-03-04 20:14:26 -06:00
Sriram Hariharan
c9684beb5b wip scraping infra 2023-03-04 11:51:56 -06:00
Sriram Hariharan
2d940493a3 beginning course scraping from row, and created assets folder with departments.json 2023-03-03 23:53:54 -06:00
Sriram Hariharan
94e74deb24 updated Course schema 2023-03-03 23:43:44 -06:00
Sriram Hariharan
f47ad8272f refactoring 2023-03-03 23:29:06 -06:00
Sriram Hariharan
39016c93aa infinite scroll support 2023-03-03 23:13:31 -06:00
Sriram Hariharan
e9c420a873 injecting into table, created table header, and buttons for each row 2023-03-03 21:57:00 -06:00
Sriram Hariharan
beb61176c1 refactoring, using different pattern for page injection and reusing same pattern for both popup and content scripts 2023-03-03 21:13:43 -06:00
Sriram Hariharan
4ed52a3c9f analyzing page types and populating search inputs 2023-03-03 19:58:53 -06:00
Sriram Hariharan
723caca417 fixed some bugs, and updated dev dashboard useffect" 2023-03-03 18:58:19 -06:00
Sriram Hariharan
57d704b759 added colors and design system to docs 2023-03-03 15:47:35 -06:00
Sriram Hariharan
e9acddfa16 added colors and 2 main components for 2 different contexts 2023-03-03 11:40:54 -06:00
Sriram Hariharan
f3ee5a0854 added license and fixed webpack plugin 2023-03-03 11:08:16 -06:00
Sriram Hariharan
5203d3acf8 clean-webpack-plugin 2023-03-03 11:02:46 -06:00
Sriram Hariharan
b0eba78697 fixing some compiler issues 2023-02-22 22:59:18 -06:00
Sriram Hariharan
bce2717088 using my boilerplate yuh 2023-02-22 22:51:38 -06:00
Sriram Hariharan
21d7056aae deleted everything lmao 2023-02-22 22:35:37 -06:00
284 changed files with 26252 additions and 13594 deletions

9
.editorconfig Normal file
View File

@@ -0,0 +1,9 @@
root = true
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
indent_size = 4
indent_style = space

96
.eslintignore Normal file
View File

@@ -0,0 +1,96 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories and management
node_modules/
jspm_packages/
package.json
package-lock.json
# TypeScript v1 declaration files
typings/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# next.js build output
.next
# Webpack-built output
/dist
# Extension archives
/build
# VsCode
.vscode/*
!.vscode/launch.json
!.vscode/tasks.json
# macOS
.DS_Store
# Terraform
.terraform
# development dependencies
.dev/vue-devtools
.dev/browser-profiles
# IntelliJ
.idea
# Sylelint IntelliJ Plugin Requirement
.stylelintrc.json
# Local environment settings
.env.local
*.svg
config
.eslintrc.js
!.storybook

217
.eslintrc.cjs Normal file
View File

@@ -0,0 +1,217 @@
module.exports = {
root: true,
env: {
browser: true,
es6: true,
node: true,
webextensions: true,
},
ignorePatterns: ['*.html', 'tsconfig.json'],
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:react/recommended',
'plugin:react-hooks/recommended',
'plugin:storybook/recommended',
'airbnb-base',
'airbnb/rules/react',
'airbnb-typescript',
'@unocss',
'prettier',
],
plugins: [
'import',
'import-essentials',
'jsdoc',
'react-prefer-function-component',
'@typescript-eslint',
'simple-import-sort',
],
globals: {
Atomics: 'readonly',
SharedArrayBuffer: 'readonly',
debugger: true,
browser: true,
context: true,
JSX: true,
},
parser: '@typescript-eslint/parser',
parserOptions: {
project: './tsconfig.json',
ecmaVersion: 2022,
sourceType: 'module',
ecmaFeatures: {
jsx: true,
modules: true,
experimentalObjectRestSpread: true,
},
},
settings: {
react: {
version: 'detect',
},
jsdoc: {
mode: 'typescript',
},
'import/parsers': {
'@typescript-eslint/parser': ['.ts', '.tsx'],
},
'import/resolver': {
typescript: {
alwaysTryTypes: true,
project: './tsconfig.json',
},
},
},
rules: {
'prefer-const': [
'off',
{
destructuring: 'any',
ignoreReadBeforeAssign: false,
},
],
'no-plusplus': 'off',
'no-inner-declarations': 'off',
'sort-imports': 'off',
'no-case-declarations': 'off',
'no-unreachable': 'warn',
'no-constant-condition': 'error',
'space-before-function-paren': 'off',
'no-undef': 'off',
'no-return-await': 'off',
'@typescript-eslint/return-await': 'off',
'@typescript-eslint/no-shadow': ['off'],
'@typescript-eslint/no-use-before-define': ['off'],
'class-methods-use-this': 'off',
'react-hooks/exhaustive-deps': 'warn',
'@typescript-eslint/lines-between-class-members': 'off',
'no-param-reassign': [
'error',
{
props: false,
},
],
'no-console': 'off',
'consistent-return': 'off',
'react/destructuring-assignment': 'off',
'import/prefer-default-export': 'off',
'no-promise-executor-return': 'off',
'import/no-cycle': 'off',
'import/no-extraneous-dependencies': 'off',
'react/jsx-props-no-spreading': 'off',
'keyword-spacing': [
'error',
{
before: true,
after: true,
},
],
'no-continue': 'off',
'space-before-blocks': [
'error',
{
functions: 'always',
keywords: 'always',
classes: 'always',
},
],
'react/jsx-filename-extension': [
1,
{
extensions: ['.tsx'],
},
],
'react/no-deprecated': 'warn',
'react/prop-types': 'off',
'react-prefer-function-component/react-prefer-function-component': [
'warn',
{
allowComponentDidCatch: false,
},
],
'react/function-component-definition': 'off',
'react/button-has-type': 'off',
'jsdoc/require-param-type': 'off',
'jsdoc/require-returns-type': 'off',
'jsdoc/newline-after-description': 'off',
'react/require-default-props': 'off',
'jsdoc/require-jsdoc': [
'warn',
{
enableFixer: false,
publicOnly: true,
checkConstructors: false,
require: {
ArrowFunctionExpression: true,
ClassDeclaration: true,
ClassExpression: true,
FunctionExpression: true,
},
contexts: [
'MethodDefinition:not([key.name="componentDidMount"]):not([key.name="render"])',
'ArrowFunctionExpression',
'ClassDeclaration',
'ClassExpression',
'ClassProperty:not([key.name="state"]):not([key.name="componentDidMount"])',
'FunctionDeclaration',
'FunctionExpression',
'TSDeclareFunction',
'TSEnumDeclaration',
'TSInterfaceDeclaration',
'TSMethodSignature',
'TSModuleDeclaration',
'TSTypeAliasDeclaration',
],
},
],
'@typescript-eslint/no-explicit-any': 'error',
'@typescript-eslint/no-unused-vars': 'warn',
'@typescript-eslint/naming-convention': 'off',
'@typescript-eslint/space-before-function-paren': 'off',
'@typescript-eslint/ban-ts-comment': 'off',
'@typescript-eslint/no-empty-interface': 'warn',
'import/no-restricted-paths': [
'error',
{
zones: [
{
target: './src/background',
from: './src/views',
message:
'You cannot import into the `background` directory from the `views` directory (i.e. content script files) because it will break the build!',
},
{
target: './src/views',
from: './src/background',
message:
'You cannot import into the `views` directory from the `background` directory (i.e. background script files) because it will break the build!',
},
{
target: './src/shared',
from: './',
except: ['./src/shared', './node_modules'],
message: 'You cannot import into `shared` from an external directory.',
},
],
},
],
'import/extensions': 'off',
'no-restricted-syntax': [
'error',
'ForInStatement',
'LabeledStatement',
'WithStatement',
{
selector: 'TSEnumDeclaration',
message: "Don't declare enums",
},
],
'@typescript-eslint/consistent-type-exports': 'error',
'@typescript-eslint/consistent-type-imports': 'error',
'simple-import-sort/imports': 'error',
'simple-import-sort/exports': 'error',
'import-essentials/restrict-import-depth': 'error',
'import-essentials/check-path-alias': 'error',
},
};

43
.github/workflows/best-practices.yml vendored Normal file
View File

@@ -0,0 +1,43 @@
name: Best Practices
on: [push, pull_request]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup pnpm
uses: pnpm/action-setup@v3
with:
version: 9
- name: Install dependencies
run: pnpm install
- name: Run ESLint
run: pnpm run lint
format:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup pnpm
uses: pnpm/action-setup@v3
with:
version: 9
- name: Install dependencies
run: pnpm install
- name: Run Prettier
run: pnpm run prettier

24
.github/workflows/check-types.yml vendored Normal file
View File

@@ -0,0 +1,24 @@
name: Type Check
on: [push, pull_request]
jobs:
type-check:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup pnpm
uses: pnpm/action-setup@v3
with:
version: 9
- name: Install dependencies
run: pnpm install
- name: Run tests
run: pnpm run check-types

26
.github/workflows/chromatic.yml vendored Normal file
View File

@@ -0,0 +1,26 @@
name: 'Chromatic'
on: [push, pull_request]
jobs:
chromatic:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup pnpm
uses: pnpm/action-setup@v3
with:
version: 9
- name: Install dependencies
run: pnpm install
- name: Publish to Chromatic
uses: chromaui/action@latest
with:
projectToken: chpt_e8bd07b0b27d8eb
exitZeroOnChanges: true
autoAcceptChanges: 'main'

25
.github/workflows/release.yml vendored Normal file
View File

@@ -0,0 +1,25 @@
name: Create Release
on:
push:
branches:
- production
- preview
jobs:
build:
name: build extension & create release
runs-on: ubuntu-latest
concurrency:
group: ${{ github.ref }}
cancel-in-progress: true
steps:
- uses: actions/checkout@master
- name: Get file permission
run: chmod -R 777 .
- name: Install dependencies
run: npm ci
- name: Release with semantic-release
id: semantic-release
run: npx --no-install semantic-release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

24
.github/workflows/tests.yml vendored Normal file
View File

@@ -0,0 +1,24 @@
name: Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup pnpm
uses: pnpm/action-setup@v3
with:
version: 9
- name: Install dependencies
run: pnpm install
- name: Run tests
run: pnpm test

43
.github/workflows/validate-pr.yml vendored Normal file
View File

@@ -0,0 +1,43 @@
name: Validate PR Title
# thank you ben limmer for this workflow:
# https://github.com/blimmer/semantic-release-demo-2/blob/main/.github/workflows/lint-pr.yml
on:
pull_request_target:
types:
- opened
- reopened
- edited
- synchronize
jobs:
main:
runs-on: ubuntu-latest
steps:
- uses: amannn/action-semantic-pull-request@v3.2.6
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Post Conventional Commit Comment (on failure)
uses: jungwinter/comment@v1
id: conventional-commit-help
with:
type: create
issue_number: ${{ github.event.pull_request.number }}
token: ${{ secrets.GITHUB_TOKEN }}
body: |
Your pull request title did not conform to [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/) standards. Our upcoming automated release pipeline will automatically determine
the proper release version based on your pull request title.
**Cheat Sheet**
- feat: A new feature
- fix: A bug fix
- docs: Documentation only changes
- style: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
- refactor: A code change that neither fixes a bug nor adds a feature
- perf: A code change that improves performance
- test: Adding missing tests or correcting existing tests
- build: Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)
- ci: Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs)
- chore: Other changes that don't modify src or test files
- revert: Reverts a previous commit
if: ${{ failure() }}

212
.gitignore vendored Normal file
View File

@@ -0,0 +1,212 @@
# File created using '.gitignore Generator' for Visual Studio Code: https://bit.ly/vscode-gig
# Created by https://www.toptal.com/developers/gitignore/api/visualstudiocode,macos,node,react,storybookjs
# Edit at https://www.toptal.com/developers/gitignore?templates=visualstudiocode,macos,node,react,storybookjs
### macOS ###
# General
.DS_Store
.AppleDouble
.LSOverride
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
### macOS Patch ###
# iCloud generated files
*.icloud
### Node ###
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build
# Dependency directories
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional stylelint cache
.stylelintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# vuepress v2.x temp and cache directory
.temp
# Docusaurus cache and generated files
.docusaurus
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*
### Node Patch ###
# Serverless Webpack directories
.webpack/
# Optional stylelint cache
# SvelteKit build / generate output
.svelte-kit
### react ###
.DS_*
**/*.backup.*
**/*.back.*
node_modules
*.sublime*
psd
thumb
sketch
### VisualStudioCode ###
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
!.vscode/*.code-snippets
# Local History for Visual Studio Code
.history/
# Built Visual Studio Code Extensions
*.vsix
### VisualStudioCode Patch ###
# Ignore all local history of files
.history
.ionide
# End of https://www.toptal.com/developers/gitignore/api/visualstudiocode,macos,node,react,storybookjs
# Custom rules (everything added below won't be overriden by 'Generate .gitignore File' if you use 'Update' option)
package-lock.json
storybook-static/

1
.husky/commit-msg Normal file
View File

@@ -0,0 +1 @@
npx --no -- commitlint --edit $1

1
.nvmrc Normal file
View File

@@ -0,0 +1 @@
v18.12.1

19
.prettierignore Normal file
View File

@@ -0,0 +1,19 @@
*.css
# macOS
.DS_Store
# Webpack-built output
/dist
# Extension archives
/build
# Optional npm cache directory
.npm
# Dependency directories and management
node_modules/
jspm_packages/
package.json
package-lock.json
# Coverage directory used by tools like istanbul
coverage

12
.prettierrc.json Normal file
View File

@@ -0,0 +1,12 @@
{
"useTabs": false,
"printWidth": 120,
"tabWidth": 4,
"singleQuote": true,
"trailingComma": "es5",
"arrowParens": "avoid",
"bracketSpacing": true,
"bracketSameLine": false,
"semi": true,
"jsxSingleQuote": true
}

37
.releaserc.json Normal file
View File

@@ -0,0 +1,37 @@
{
"branches": [
"production",
{
"name": "preview",
"channel": "alpha",
"prerelease": "alpha"
}
],
"plugins": [
[
"@semantic-release/commit-analyzer",
{
"preset": "conventionalcommits"
}
],
[
"@semantic-release/release-notes-generator",
{
"preset": "conventionalcommits"
}
],
[
"@semantic-release/exec",
{
"prepareCmd": "SEMANTIC_VERSION=${nextRelease.version} npm run build"
}
],
[
"@semantic-release/github",
{
"assets": "build/**/artifacts/*.*",
"failComment": false
}
]
]
}

24
.storybook/main.ts Normal file
View File

@@ -0,0 +1,24 @@
import type { StorybookConfig } from '@storybook/react-vite';
const config: StorybookConfig = {
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
addons: [
'@storybook/addon-links',
'@storybook/addon-essentials',
'@storybook/addon-designs',
'@storybook/test',
'@chromatic-com/storybook',
],
framework: {
name: '@storybook/react-vite',
options: {
builder: {
viteConfigPath: '.storybook/vite-storybook.config.ts',
},
},
},
docs: {
autodocs: 'tag',
},
};
export default config;

178
.storybook/preview.tsx Normal file
View File

@@ -0,0 +1,178 @@
import { UserScheduleStore } from '@shared/storage/UserScheduleStore';
import type { Preview } from '@storybook/react';
import ExtensionRoot from '@views/components/common/ExtensionRoot/ExtensionRoot';
import React from 'react';
const preview: Preview = {
parameters: {
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/i,
},
},
},
decorators: [
Story => (
<React.StrictMode>
<ExtensionRoot>
<Story />
</ExtensionRoot>
</React.StrictMode>
),
],
};
let localData = {};
type ListenerFunction = (
changes: { [key: string]: chrome.storage.StorageChange },
areaName: chrome.storage.AreaName
) => void;
const localDataListeners = new Map<
ListenerFunction, // key to remove listener
(changes: { [key: string]: chrome.storage.StorageChange }) => void
>();
// mock chrome api
globalThis.chrome = {
storage: {
local: {
/**
* Removes all items from storage.
* @param callback Optional.
* Callback on success, or on failure (in which case runtime.lastError will be set).
*/
async clear() {
localData = {};
},
/**
* Gets one or more items from storage.
* @param keys A single key to get, list of keys to get, or a dictionary specifying default values.
* An empty list or object will return an empty result object. Pass in null to get the entire contents of storage.
* @return A Promise that resolves with an object containing items
*/
async get(keys?: string | string[] | { [key: string]: any } | null) {
if (keys === null) {
return localData;
}
if (Array.isArray(keys)) {
return keys.reduce((acc, key) => {
acc[key] = localData[key];
return acc;
}, {} as string); // funny types
}
if (typeof keys === 'string') {
return { [keys]: localData[keys] };
}
return keys;
},
/**
* Gets the amount of space (in bytes) being used by one or more items.
* @param keys Optional. A single key or list of keys to get the total usage for. An empty list will return 0. Pass in null to get the total usage of all of storage.
* @param callback Callback with the amount of space being used by storage, or on failure (in which case runtime.lastError will be set).
* Parameter bytesInUse: Amount of space being used in storage, in bytes.
*/
async getBytesInUse() {
return 0;
},
/**
* Removes one or more items from storage.
* @param keys A single key or a list of keys for items to remove.
* @param callback Optional.
* Callback on success, or on failure (in which case runtime.lastError will be set).
*/
async remove(keys: string | string[]) {
if (Array.isArray(keys)) {
keys.forEach(key => {
for (const listener of localDataListeners.values()) {
listener({ [key]: { oldValue: localData[key], newValue: undefined } });
}
delete localData[key];
});
} else {
for (const listener of localDataListeners.values()) {
listener({ [keys]: { oldValue: localData[keys], newValue: undefined } });
}
delete localData[keys];
}
},
/**
* Sets multiple items.
* @param items An object which gives each key/value pair to update storage with. Any other key/value pairs in storage will not be affected.
* Primitive values such as numbers will serialize as expected. Values with a typeof "object" and "function" will typically serialize to {}, with the exception of Array (serializes as expected), Date, and Regex (serialize using their String representation).
* @param callback Optional.
* Callback on success, or on failure (in which case runtime.lastError will be set).
*/
async set(items: { [key: string]: any }) {
for (const key in items) {
const oldValue = localData[key];
localData[key] = JSON.parse(JSON.stringify(items[key]));
for (const listener of localDataListeners.values()) {
listener({ [key]: { oldValue: oldValue, newValue: localData[key] } });
}
}
},
},
onChanged: {
/**
* Registers an event listener callback to an event.
* @param callback Called when an event occurs. The parameters of this function depend on the type of event.
*/
addListener(
listener: (
changes: { [key: string]: chrome.storage.StorageChange },
areaName: chrome.storage.AreaName
) => void
) {
localDataListeners.set(listener, (changes: { [key: string]: chrome.storage.StorageChange }) => {
listener(changes, 'local');
});
},
/**
* Deregisters an event listener callback from an event.
* @param callback Listener that shall be unregistered.
*/
removeListener(listener: ListenerFunction) {
localDataListeners.delete(listener);
},
},
},
runtime: {
id: 'fake-id',
getManifest(): chrome.runtime.Manifest {
return {
manifest_version: 3,
name: 'fake-name',
version: '0.0.0',
};
},
onMessage: {
/**
* Registers an event listener callback to an event.
* @param callback Called when an event occurs. The parameters of this function depend on the type of event.
*/
addListener<T extends Function>(callback: T) {},
/**
* Deregisters an event listener callback from an event.
* @param callback Listener that shall be unregistered.
*/
removeListener<T extends Function>(callback: T) {},
},
},
} as typeof chrome;
// set updatedAt dates to be fixed
UserScheduleStore.get('schedules').then(schedules => {
schedules.forEach(schedule => {
schedule.updatedAt = new Date('2024-01-01 12:00').getTime();
});
UserScheduleStore.set('schedules', schedules);
});
export default preview;

View File

@@ -0,0 +1,28 @@
import react from '@vitejs/plugin-react-swc';
import { resolve } from 'path';
import UnoCSS from 'unocss/vite';
import Icons from 'unplugin-icons/vite';
import { defineConfig } from 'vite';
const root = resolve(__dirname, '../src');
const pagesDir = resolve(root, 'pages');
const assetsDir = resolve(root, 'assets');
const publicDir = resolve(__dirname, '../public');
console.log(root);
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react(), UnoCSS(), Icons({ compiler: 'jsx', jsx: 'react' })],
resolve: {
alias: {
src: root,
'@assets': assetsDir,
'@pages': pagesDir,
'@public': publicDir,
'@shared': resolve(root, 'shared'),
'@background': resolve(pagesDir, 'background'),
'@views': resolve(root, 'views'),
},
},
});

9
.vscode/extensions.json vendored Normal file
View File

@@ -0,0 +1,9 @@
{
"recommendations": [
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
"antfu.unocss",
"editorconfig.editorconfig",
"figma.figma-vscode-extension"
]
}

18
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,18 @@
{
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Run current script",
"runtimeExecutable": "npx",
"runtimeArgs": [
"tsx"
],
"program": "${file}",
"skipFiles": [
"<node_internals>/**"
],
}
]
}

40
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,40 @@
{
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
},
"editor.defaultFormatter": "esbenp.prettier-vscode",
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[yaml]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[typescriptreact]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[svg]": {
"editor.defaultFormatter": "jock.svg"
},
"material-icon-theme.activeIconPack": "react",
"material-icon-theme.folders.associations": {
"analytics": "Json",
"background": "Delta",
"navigation": "Routes",
"logging": "log",
"popup": "Layout",
"storage": "Database",
},
"material-icon-theme.files.associations": {
"tsconfig.extension.json": "tsconfig",
"tsconfig.build.json": "tsconfig",
"tsconfig.test.json": "tsconfig"
},
"[html]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"typescript.tsdk": "node_modules/typescript/lib",
}

10
@types/vite-env.d.ts vendored Normal file
View File

@@ -0,0 +1,10 @@
/// <reference types="vite/client" />
interface ImportMetaEnv {
readonly VITE_PACKAGE_VERSION: string;
readonly VITE_BETA_BUILD?: 'true';
}
interface ImportMeta {
readonly env: ImportMetaEnv;
}

21
LICENSE.md Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2023 Sriram Hariharan
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -1,40 +1,59 @@
# UT Registration Plus
# UT Registration Plus
## (or Sriram's Sexy Scheduling Script)
[Try it for yourself on the Chrome Web Store](https://chrome.google.com/webstore/detail/hboadpjkoaieogjimneceaahlppnipaa)
We've all been there. 20 tabs of Rate My Professor, Google Spreadsheet, and the UT Course Schedule open and you still don't know what classes to take.
This extension, UT Registration Plus (UTRP), tries to streamline most of the unnecessary steps and headaches of registering for classes at UT Austin.
- For each class in the UT Course Schedule site, UTRP provides a "breakdown" popup, with quick and easy links to the instructor's RateMyProfessor, Course Evaluation Survey (CES) and past syllabi.
We've all been there. 20 tabs of Rate My Professor, Catalyst, and the UT Course Catalog open and you still don't know what classes to take.
This extension tries to streamline most of the unnecessary steps and headaches of registering for classes at UT Austin.
- Shows the course description with highlighted information on prerequisites, restrictions, etc.
- Shows an aggregate and semesterly graph of the grade distributions for each course.
- For each class on the UT Course Catalog it provides a "breakdown" popup, with quick and easy links to the RateMyProfessor and eCIS pages of the professor, as well as syllabi from when the professor taught the class in the past.
- Gives you the ability to add "Add Course" and view them in the extension popup, a quick list of all the courses you have saved and an easy way to copy unique numbers.
- Gets the course description and highlight the important information like prerequisites, restrictions, etc.
- Highlights and crosses-out what courses on the UT Course Catalog would conflict with your currently saved courses, making selecting courses that fit with your schedule so much easier.
- Shows an aggregate graph of the grade distributions for when the professor taught the class in the past.
- Display's a weekly schedule based on your saved courses.
- Gives you the ability to "Save Courses" and view them in the extension popup. This lets you see any schedule conflicts, and makes copy-pasting the unique code much easier when you're actually registering.
- Give you the ability to create multiple schedules to plan for different scenarios.
- Highlights and crosses-out what courses on the UT Course Catalog would conflict with your currently saved courses, making selecting courses that fit with your schedule so much easier.
- ... and much more!
- Display's a weekly schedule based on your saved courses
## Toolchain
<p align="center">
<img src="https://lh3.googleusercontent.com/X5hqHGPU-F2lF3_shT2injxd40eFYXLJfZVxpU1v2w1YvFRW1jQMEXu2yzWHKKpqn5huJL-NEHY=w640-h400-e365">
</p>
<p align="center">
<img src="https://lh3.googleusercontent.com/ZCRxTFKFjpGm5ZRMv2iHzMqdnrQHUx_Ih_XhGhy2O4Yn29YccvU5yXXrWXKuVKsNAmEJJ0As4xc=w640-h400-e365">
</p>
<p align="center">
<img src="https://lh3.googleusercontent.com/3iRi25wDnVqgzc7pnYUXQq1TvdPpAeDjCmIF9hLU-WKmlchEYQUh_xU-XV00fEbKUr2XVKGkOw=w640-h400-e365">
</p>
<p align="center">
<img src="https://lh3.googleusercontent.com/x95blI5D1mseNPLOtHETlLmoVtHm0eeye9uyeWSDd5W6m6fSoZxMMMyQTGUFo5swoTgRivGVyw=w640-h400-e365">
</p>
<p align="center">
<img src="https://lh3.googleusercontent.com/bbey8OGOTtJWUaHGVIU5wewbWg6X6s-gjD15RwXHhvgH_9kax2mE4bcrjem_iZGH-q5z6NT7g94=w640-h400-e365">
</p>
- React 18
- TypeScript
- Vite 5
- ESLint
- Prettier
- Storybook
- Semantic-Release
- Custom Messaging & Storage Wrappers
# 2.0 coming soon....
## Development: Getting Started
1. Clone this repo
2. This project uses `pnpm` to manage and patch dependencies. Run `pnpm install` to configure the repository for building/development
3. Using either of the methods listed below, the extension will build to the `dist/` directory.
### Development Builds
- Run `pnpm dev`
> [!NOTE]
> Injected content such as extension content on UT pages is not properly styled, and are missing class stylings. When developing for these pages, use `NODE_ENV='development' pnpm run dev build --mode development -w` to build and watch for changes. This will ensure you are seeing an accurate representation of the extension.
### Production Builds
- Run `pnpm build`
<details>
<summary>Beta builds</summary>
Use `BETA=true pnpm build` to build a beta build.
</details>
## Development: Loading the Extension Manually
Open [chrome://extensions](chrome://extensions), ensure you have 'Developer Mode' enabled, and click 'Load unpacked'.
Navigate to the `dist/` folder, and click 'select' to import the extension.

View File

@@ -1,42 +0,0 @@
<!DOCTYPE html>
<!-- This file is the html file serving the "My Schedule / Calendar" page. -->
<head>
<link rel='stylesheet' href='css/fullcalendar.min.css' />
<link rel='stylesheet' href='css/_materialFullCalendar.css' />
<script src='js/lib/jquery-3.3.1.min.js'></script>
<script src='js/lib/moment.min.js'></script>
<script src='js/lib/fullcalendar.min.js'></script>
</head>
<body>
<div style='display:flex'>
<div id='calendar' style="flex-grow: 1"></div>
<div class="card" id="header"
style="text-align:center;margin:5px 0px 0px 15px;display: inline-table;padding-bottom: 5px;">
<h1 id='hours'
style="font-size:30px;font-weight:500; border-bottom: 3px solid black;display: inline-block;padding-bottom: 5px;margin-bottom: 5px;">
0
hours</h1>
<h1 id='num' style="font-size:20px;font-weight:500; margin: 2px;">0
Courses</h1>
<br>
<div style="margin:5px;display: flex;flex-direction: column;">
<button id="clear" class="matbut"
style="font-size:medium; background:#4CAF50;margin: 10px;white-space: nowrap;text-align: center;">Clear
All</button>
<button id="save" class="matbut"
style="font-size:medium; background:#FF9800;margin: 10px;white-space: nowrap;text-align: center;">Save
as PNG</button>
<button id="export" class="matbut"
style="font-size:medium; background:#FF0000;margin: 10px;white-space: nowrap;text-align: center;">Export
Cal</button>
</div>
</div>
</div>
</body>
<script src='js/config.js'></script>
<script src='js/lib/html2canvas.min.js'></script>
<script src='js/lib/ics.min.js'></script>
<script src='js/Template.js'></script>
<script src='js/util.js'></script>
<script src='js/calendar.js'></script>

5
chromatic.config.json Normal file
View File

@@ -0,0 +1,5 @@
{
"onlyChanged": true,
"projectId": "Project:65c5172964f36dcf207985bf",
"zip": true
}

123
commitlint.config.ts Normal file
View File

@@ -0,0 +1,123 @@
import { RuleConfigCondition, RuleConfigSeverity, TargetCaseType } from '@commitlint/types';
export default {
parserPreset: 'conventional-changelog-conventionalcommits',
rules: {
'body-leading-blank': [RuleConfigSeverity.Warning, 'always'] as const,
'body-max-line-length': [RuleConfigSeverity.Error, 'always', 100] as const,
'footer-leading-blank': [RuleConfigSeverity.Warning, 'always'] as const,
'footer-max-line-length': [RuleConfigSeverity.Error, 'always', 100] as const,
'header-max-length': [RuleConfigSeverity.Error, 'always', 100] as const,
'header-trim': [RuleConfigSeverity.Error, 'always'] as const,
'subject-case': [
RuleConfigSeverity.Error,
'never',
['sentence-case', 'start-case', 'pascal-case', 'upper-case'],
] as [RuleConfigSeverity, RuleConfigCondition, TargetCaseType[]],
'subject-empty': [RuleConfigSeverity.Error, 'never'] as const,
'subject-full-stop': [RuleConfigSeverity.Error, 'never', '.'] as const,
'type-case': [RuleConfigSeverity.Error, 'always', 'lower-case'] as const,
'type-empty': [RuleConfigSeverity.Error, 'never'] as const,
'type-enum': [
RuleConfigSeverity.Error,
'always',
['build', 'chore', 'ci', 'docs', 'feat', 'fix', 'perf', 'refactor', 'revert', 'style', 'test'],
] as [RuleConfigSeverity, RuleConfigCondition, string[]],
},
prompt: {
questions: {
type: {
description: "Select the type of change that you're committing",
enum: {
feat: {
description: 'A new feature',
title: 'Features',
emoji: '✨',
},
fix: {
description: 'A bug fix',
title: 'Bug Fixes',
emoji: '🐛',
},
docs: {
description: 'Documentation only changes',
title: 'Documentation',
emoji: '📚',
},
style: {
description:
'Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)',
title: 'Styles',
emoji: '💎',
},
refactor: {
description: 'A code change that neither fixes a bug nor adds a feature',
title: 'Code Refactoring',
emoji: '📦',
},
perf: {
description: 'A code change that improves performance',
title: 'Performance Improvements',
emoji: '🚀',
},
test: {
description: 'Adding missing tests or correcting existing tests',
title: 'Tests',
emoji: '🚨',
},
build: {
description:
'Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)',
title: 'Builds',
emoji: '🛠',
},
ci: {
description:
'Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs)',
title: 'Continuous Integrations',
emoji: '⚙️',
},
chore: {
description: "Other changes that don't modify src or test files",
title: 'Chores',
emoji: '♻️',
},
revert: {
description: 'Reverts a previous commit',
title: 'Reverts',
emoji: '🗑',
},
},
},
scope: {
description: 'What is the scope of this change (e.g. component or file name)',
},
subject: {
description: 'Write a short, imperative tense description of the change',
},
body: {
description: 'Provide a longer description of the change',
},
isBreaking: {
description: 'Are there any breaking changes?',
},
breakingBody: {
description:
'A BREAKING CHANGE commit requires a body. Please enter a longer description of the commit itself',
},
breaking: {
description: 'Describe the breaking changes',
},
isIssueAffected: {
description: 'Does this change affect any open issues?',
},
issuesBody: {
description:
'If issues are closed, the commit requires a body. Please enter a longer description of the commit itself',
},
issues: {
description: 'Add issue references (e.g. "fix #123", "re #123".)',
},
},
},
};

View File

@@ -1,344 +0,0 @@
/*
This is the Material Design theme for FullCalendar Weekly Agenda view
Creation Date: Aug 19th 2015
Author: Jacky Liang
Version: FullCalendar 2.4.0
Tested Using the Following FC Settings:
editable: false,
handleWindowResize: true,
weekends: false, // Hide weekends
defaultView: 'agendaWeek', // Only show week view
header: false, // Hide buttons/titles
minTime: '07:30:00', // Start time for the calendar
maxTime: '22:00:00', // End time for the calendar
columnFormat: {
week: 'ddd' // Only show day of the week names
},
displayEventTime: true,
allDayText: 'Online/TBD'
Note: This has NOT been tested on Monthly or Daily views.
Colors: Use the following - https://www.google.com/design/spec/style/color.html#color-color-palette
at the 700 level. An opacity of 0.65 is automatically applied to the
700 level colors to generate a soft and pleasing look.
Color were applied to each event using the following code:
events.push({
title: 'This is a Material Design event!',
start: 'someStartDate',
end: 'someEndDate',
color: '#C2185B'
});
*/
.fc-state-highlight {
opacity: 0;
border: none;
}
/* Styling for each event from Schedule */
.fc-time-grid-event.fc-v-event.fc-event {
border-radius: 4px;
border: none;
padding: 5px;
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.3);
transition: 0.3s;
opacity: 1;
}
.html2canvas-container {
width: 3000px !important;
height: 3000px !important;
}
.fc-time-grid-event.fc-v-event.fc-event:hover {
box-shadow: 0 8px 12px 0 rgba(0, 0, 0, 0.3);
}
/* Bolds the name of the event and inherits the font size */
.fc-event {
font-size: small !important;
font-weight: bold !important;
}
/* Remove the header border from Schedule */
.fc td,
.fc th {
border-style: ridge !important;
border-width: 1px !important;
padding: 4px 3px 0px 3px !important;
vertical-align: top !important;
border-left-width: 0;
}
.fc-row fc-widget-header {
border-color: transparent;
}
.fc td {
border-top-width: 0;
padding: 3px !important;
}
.fc-widget-header {
background-color: #cc5500;
color: white;
}
/* Inherits background for each event from Schedule. */
.fc-event .fc-bg {
z-index: 1 !important;
background: inherit !important;
opacity: 0.25 !important;
}
/* Normal font weight for the time in each event */
.fc-time-grid-event .fc-time {
font-weight: normal !important;
}
/* Apply same opacity to all day events */
.fc-ltr .fc-h-event.fc-not-end,
.fc-rtl .fc-h-event.fc-not-start {
opacity: 0.65 !important;
margin-left: 12px !important;
padding: 5px !important;
}
/* Apply same opacity to all day events */
.fc-day-grid-event.fc-h-event.fc-event.fc-not-start.fc-end {
opacity: 0.65 !important;
margin-left: 12px !important;
padding: 5px !important;
}
/* Material design button */
.matbut {
border: none;
outline: none;
cursor: pointer;
color: white;
margin: 10px 10px 10px 0px;
padding: 10px 10px;
border-radius: 10px;
font-size: medium;
font-style: bold;
box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.4);
}
.matbut {
position: relative;
overflow: hidden;
}
.matbut:after {
content: "";
position: absolute;
top: 50%;
left: 50%;
width: 5px;
height: 5px;
background: rgba(255, 255, 255, 0.5);
opacity: 0;
border-radius: 100%;
transform: scale(1, 1) translate(-50%);
transform-origin: 50% 50%;
}
@keyframes ripple {
0% {
transform: scale(0, 0);
opacity: 1;
}
20% {
transform: scale(25, 25);
opacity: 1;
}
100% {
opacity: 0;
transform: scale(40, 40);
}
}
.matbut:focus:not(:active)::after {
animation: ripple 1s ease-out;
}
.fc-button {
display: inline-block;
position: relative;
cursor: pointer;
min-height: 36px;
min-width: 88px;
line-height: 36px;
vertical-align: middle;
-webkit-box-align: center;
-webkit-align-items: center;
align-items: center;
text-align: center;
border-radius: 2px;
box-sizing: border-box;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
outline: none;
border: 0;
padding: 0 6px;
margin: 6px 8px;
letter-spacing: 0.01em;
background: transparent;
color: currentColor;
white-space: nowrap;
text-transform: uppercase;
font-weight: 500;
font-size: 14px;
font-style: inherit;
font-variant: inherit;
font-family: inherit;
text-decoration: none;
overflow: hidden;
-webkit-transition: box-shadow 0.4s cubic-bezier(0.25, 0.8, 0.25, 1), background-color 0.4s cubic-bezier(0.25, 0.8, 0.25, 1);
transition: box-shadow 0.4s cubic-bezier(0.25, 0.8, 0.25, 1), background-color 0.4s cubic-bezier(0.25, 0.8, 0.25, 1);
}
.fc-button:hover {
background-color: rgba(158, 158, 158, 0.2);
}
.fc-button:focus,
.fc-button:hover {
text-decoration: none;
}
/* The active button box is ugly so the active button will have the same appearance of the hover */
.fc-state-active {
background-color: rgba(158, 158, 158, 0.2);
}
/* Not raised button */
.fc-state-default {
box-shadow: None;
}
.modal {
display: none;
position: fixed;
z-index: 1;
padding-top: 300px;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: rgb(0, 0, 0);
background-color: rgba(0, 0, 0, 0.4);
}
#classname {
font-weight: bold;
margin-bottom: -10px;
font-size: large;
}
.modal-content {
background-color: #fefefe;
margin: auto;
max-height: 85%;
overflow-y: auto;
padding: 15px;
border: 1px solid #888;
width: 35%;
}
#prof {
font-size: medium;
margin-bottom: -5px;
}
#info {
margin: 10px;
}
body a:link,
body a:visited {
font-weight: bold;
color: #3c87a3;
}
.time {
margin-left: auto;
margin-right: auto;
margin-bottom: 5px;
font-size: medium;
}
.fc td,
.fc th {
border-color: #E7E7E7;
}
.fc-time-grid .fc-slats td {
height: 1.5em;
border-bottom: initial;
border-color: #E7E7E7;
}
/* The Close Button */
.close {
color: #aaaaaa;
float: right;
padding: 5px;
font-size: 28px;
font-weight: bold;
}
.close:hover,
.close:focus {
color: #000;
text-decoration: none;
cursor: pointer;
}
.card {
transition: 0.3s;
margin-bottom: 5px;
box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.16), 0 2px 10px 0 rgba(0, 0, 0, 0.12);
}
.card:hover {
box-shadow: 0 4px 10px 0 rgba(0, 0, 0, 0.2), 0 4px 20px 0 rgba(0, 0, 0, 0.19);
}
.cardcontainer {
padding: 2px 16px;
display: block;
transition: width 300ms ease-in-out, height 300ms ease-in-out;
}
tbody {
border-width: 0px;
}

File diff suppressed because one or more lines are too long

View File

@@ -1,81 +0,0 @@
.version {
padding: 0px 5px 5px 0px;
text-align: right;
}
.creator-tag {
margin: 10px 5px 5px 0px;
text-align: center;
}
.options-card {
width: 400px;
margin-left: auto;
margin-right: auto;
height: auto;
padding-bottom: 5px;
}
#version-container {
margin-left: auto;
margin-right: auto;
width: 400px;
}
.options-header {
padding: 16px 16px 0px 16px;
font-size: 20px;
}
#contributors_container {
text-align: center;
margin-top: 30;
padding: 5px 20px 20px 20px;
width: auto;
margin-right: 20%;
margin-left: 20%;
margin-top: 20px;
}
#contributor-list {
max-width: 1200px;
margin: 0 auto;
display: grid;
grid-gap: 1rem;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}
.contributor-card {
cursor: pointer;
padding: 5px;
max-width: 1200px;
margin: 0 auto;
text-align: center;
display: grid;
grid-gap: 1rem;
}
.contributor-card img {
width: 200px;
height: 200px;
}
.contributor-name {
font-weight: bold;
margin: 0;
}
.contributor-username {
margin: 0 0 5px 0;
font-style: italic;
}
.contributor-title {
margin-bottom: 0;
font-size: 15px;
}
.open-source-tag {
padding: 10px;
font-size: 16px;
}

View File

@@ -1,563 +0,0 @@
.card {
box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.16), 0 2px 10px 0 rgba(0, 0, 0, 0.12);
transition: 0.3s;
margin: 5px;
}
.card:hover {
box-shadow: 0 4px 10px 0 rgba(0, 0, 0, 0.2), 0 4px 20px 0 rgba(0, 0, 0, 0.19);
}
.container {
padding: 3px 16px 3px 12px;
}
h2 {
padding: 10px 0px 0 10px;
}
li {
width: 350px;
}
body {
min-width: 370px;
min-height: 400px;
position: relative;
}
.conflict_message{
font-size:small;
font-weight:bold;
color:red;
margin:5px 5px 5px 10px;
}
.course_list {
list-style-type: none;
padding: 5px;
margin-bottom: 30px;
}
.course_list_card {
cursor: pointer;
}
.course_list_item {
padding: 0px 5px 5px 5px;
overflow-y: auto;
max-height:400px;
}
.course_list_item_subtext {
font-size:medium;
}
.empty_message {
font-weight: normal;
font-size: large;
margin: 60px 30px 200px 30px;
text-align: center;
}
#empty #main {
margin-bottom: 5px;
}
#empty span {
font-size: small;
display: table;
margin: 0 auto;
font-weight: bold;
}
.time_line_days {
display:inline-block;
width: 20%;
}
.time_line_hours {
margin-left:10px;
display:inline-block;
width: 50%;
text-align:center;
}
.time_line_location {
float:right;
display:inline-block;
text-align:right;
width: 25%;
}
.time_line_location_link {
color:#3c87a3;
text-decoration:none;
}
.more_info_button{
background: #2196F3;
}
.remove_button {
background: #F44336;
}
.register_button {
background: #4CAF50;
}
.settings_button {
margin-right: 2px;
border: 0px;
border-radius: 50%;
transition: 0.3s;
opacity: 0.5;
}
.settings_button:focus {
outline: 0;
}
.settings_button:hover {
box-shadow: 0 4px 10px 0 rgba(0, 0, 0, 0.16), 0 4px 15px 0 rgba(0, 0, 0, 0.12);
opacity: 1;
}
.selected {
box-shadow: 0 0 0 1pt #FF9800;
}
.settings_button:focus:after:hover {
outline: 0;
transition: 0.3s;
}
.copy_button {
background-color: transparent;
padding: 0px;
border: none;
font-size: 15px;
cursor: pointer;
border-radius: 50%;
transition: .2s;
}
.copy_button:focus {
outline: 0;
}
.shadow {
box-shadow: 0 2px 20px 0 rgba(0, 0, 0, 0.2), 0 4px 20px 0 rgba(0, 0, 0, 0.16);
}
i {
padding: 4px 0px;
}
.copy_button_icon {
color:white;
float:left;
border-radius: 50%;
padding: 3px;
text-shadow: 2px 2px 5px rgba(0, 0, 0, 0.16);
font-size: x-large;
}
.header_container{
border-radius: 7px;
}
.header_buttons {
padding: 5px 10px 5px 10px;
display: flex;
justify-content: space-between;
}
.header_button {
font-size:15px !important;
margin: 7px !important;
}
.clear_button {
background:#4CAF50;
}
.ris_button {
background:#FF9800;
}
.schedule_button {
background: #FF0000;
}
.settings {
position: absolute;
bottom: 0px;
right: 0px;
display: flex;
vertical-align: middle;
box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.16), 0 2px 10px 0 rgba(0, 0, 0, 0.12);
transition: 0.3s;
padding: 7px 5px 5px 7px;
margin: 0px 5px 0px 0px;
border-radius: 7px;
}
.material_button {
border: none;
outline: none;
cursor: pointer;
color: white;
margin: 10px 10px 10px 0px;
padding: 10px 10px;
border-radius: 10px;
font-size: medium;
font-style: bold;
box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.4);
position: relative;
overflow: hidden;
}
.course_name_truncate_box {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
width: 80%;
color:white;
margin:5px;
display:inline-block;
font-size:large;
align-items:center;
}
.settings_divider {
margin-bottom: 0px;
display:inline-block;
}
.search_button {
background-color:white;
margin-left: 5px;
margin-right: 3px;
}
.course_list_item_options {
display: none;
}
.import_button {
background-color:white;
}
.options_button {
background-color:white;
margin-right: 0px;
}
.settings_icon {
color:#FF9800;
}
.course_list_item_time_box {
font-weight:bold;
padding:10px;
margin:0px 5px 0px 15px;
font-size:small;
}
.course_list_item_options_button_container {
border-radius:0px;
}
.course_list_item_options_buttons{
float:right;
margin:5px;
}
.arrow {
float:right;
font-size:small;
display:inline-block;
margin-top:10px;
color:white;
font-family: sans-serif;
}
.material_button:after {
content: "";
position: absolute;
top: 50%;
left: 50%;
width: 5px;
height: 5px;
background: rgba(255, 255, 255, 0.5);
opacity: 0;
border-radius: 100%;
transform: scale(1, 1) translate(-50%);
transform-origin: 50% 50%;
}
.card:after {
content: "";
position: absolute;
top: 50%;
left: 50%;
width: 5px;
height: 5px;
background: rgba(255, 255, 255, 0.5);
opacity: 0;
border-radius: 100%;
transform: scale(1, 1) translate(-50%);
transform-origin: 50% 50%;
}
.modal {
display: none;
position: fixed;
z-index: 1;
padding-top: 100px;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: rgb(0, 0, 0);
background-color: rgba(0, 0, 0, 0.4);
}
.modal-content {
background-color: #fefefe;
margin: auto;
max-height: 85%;
overflow-y: auto;
padding: 20px;
border: 1px solid #888;
width: 50%;
}
/* The Close Button */
.close {
color: #aaaaaa;
float: right;
padding: 5px;
font-size: 28px;
font-weight: bold;
}
@keyframes ripple {
0% {
transform: scale(0, 0);
opacity: 1;
}
20% {
transform: scale(25, 25);
opacity: 1;
}
100% {
opacity: 0;
transform: scale(40, 40);
}
}
.material_button:focus:not(:active)::after {
animation: ripple 1s ease-out;
}
.card:focus:not(:active)::after {
animation: ripple 1s ease-out;
}
input {
border-radius: 5px;
color: rgba(0, 0, 45, 0.48)
}
input:focus {
outline: 0;
}
input[type=number]::-webkit-inner-spin-button,
input[type=number]::-webkit-outer-spin-button {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
margin: 0;
}
#search-popup {
background: white;
color: #747474;
box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.16), 0 2px 10px 0 rgba(0, 0, 0, 0.12);
position: absolute;
margin: auto;
bottom: 42px;
right: 20px;
padding: 10px;
border-radius: inherit;
width: 120px;
}
.flex-container {
display: flex;
flex-direction: column;
}
.item {
flex: 1 1 auto;
}
.select-style {
border: 1px solid #979797;
margin: 5px 0px;
border-radius: 5px;
display: none;
background: transparent no-repeat 90% 50%;
position: relative;
}
.select-style:after {
content: '\e5c5';
font-family: 'Material Icons';
font-weight: normal;
font-style: normal;
font-size: 24px;
color: rgba(0, 0, 45, 0.48);
position: absolute;
pointer-events: none;
bottom: 0px;
right: 0px;
}
.select-style select {
padding: 5px 8px;
border: none;
color: rgba(0, 0, 45, 0.48);
box-shadow: none;
background: transparent;
background-image: none;
-webkit-appearance: none;
appearance: none;
width: 120px;
word-break: normal;
-ms-word-break: normal;
overflow: visible;
}
.select-style select:focus {
outline: none;
}
.class_id_input {
display: none;
border: 1px solid #979797;
border-radius: 5px;
margin: 10px 0px;
padding: 5px 8px;
}
.class_id_input::placeholder {
color: rgba(0, 0, 45, 0.48);
}
.search-button {
float: right;
font-size: inherit;
padding: 7px 11px;
margin: 8px 0px;
background-color: #FF9800;
}
#import-export-popup {
background: white;
color: #747474;
box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.16), 0 2px 10px 0 rgba(0, 0, 0, 0.12);
position: absolute;
margin: auto;
bottom: 42px;
right: 10px;
border-radius: inherit;
width: 140px;
}
.simple-menu-option {
display: inline-block;
color: rgba(0, 0, 45, 0.48);
border: none;
background-color: white;
font-size: 15px;
overflow: hidden;
vertical-align: middle;
cursor: pointer;
text-align: left;
padding: 10px 0px 5px 5px;
border-radius: 7px;
}
.simple-menu-option i {
font-size: 19px;
vertical-align: middle;
margin-top: 0px;
margin-left: 0px;
margin-right: 3px;
margin-bottom: 3px;
}
.simple-menu-option:hover {
background-color: rgba(177, 175, 175, 0.200);
transition-duration: 0.4s;
/* color: #FF9800; */
}
.simple-menu-option:focus {
outline: none;
}
.hide {
display: none !important;
}
.meta{
margin:0;
color:#FF9800;
position: absolute;
bottom: 0px;
left: 0px;
vertical-align: middle;
padding: 7px 5px 5px 7px;
margin: 0px 5px 0px 5px;
font-size: 10px;
}
.meta-metric{
font-size: 20px;
font-weight: bold;
}
.meta-container{
margin: 5px 5px 10px 5px;
}
.input-box{
color: rgba(0, 0, 45, 0.48);
border: 1px solid #8C8C8C;
font-size: 11px;
padding: 5px;
border-radius: 7px;
width: 90%;
}
.input-box::placeholder {
color: rgba(0, 0, 45, 0.345);
}

View File

@@ -1,308 +0,0 @@
.modal {
display: none;
position: fixed;
z-index: 1;
padding-top: 75px;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: rgb(0, 0, 0);
background-color: rgba(0, 0, 0, 0.4);
}
.modal-content {
background-color: #fefefe;
margin: auto;
max-height: 85%;
overflow-y: auto;
padding: 20px;
border: 1px solid #888;
width: 50%;
}
.loader {
border: 10px solid #f3f3f3;
border-top: 10px solid #FF9800;
border-radius: 50%;
width: 50px;
height: 50px;
display: none;
animation: spin 2s linear infinite;
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.close {
color: #aaaaaa;
float: right;
padding: 5px;
font-size: 28px;
font-weight: bold;
}
.title {
font-size: x-large;
font-weight: bold;
padding-top: 5px;
line-height: 1;
padding-left: 5px;
margin: 5px 0px 5px 0px;
}
.distButton {
vertical-align: bottom;
}
.description {
padding: 5px;
font-size: 15px;
font-weight: normal;
}
.chartloader {
position: absolute;
margin-left: auto;
margin-right: auto;
margin-top: 50px;
left: 0;
right: 0;
}
.profname {
margin-left: 5px;
padding-bottom: 5px;
font-size: medium;
margin-top: 5px;
}
.dateTimePlace {
margin-left: 5px;
margin-bottom: 0px;
margin-top: 0px;
font-size: smaller;
font-weight: bold;
}
#chartcontainer {
max-width: 100%;
height: 250px;
}
#chart {
min-width: auto;
max-width: 100%;
height: 250px;
margin: 0 auto;
z-index: 1;
}
.card {
transition: 0.3s;
margin-bottom: 10px;
box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.16), 0 2px 10px 0 rgba(0, 0, 0, 0.12);
}
.card:hover {
box-shadow: 0 4px 10px 0 rgba(0, 0, 0, 0.2), 0 4px 20px 0 rgba(0, 0, 0, 0.19);
}
.cardcontainer {
padding: 2px 16px;
transition: width 300ms ease-in-out, height 300ms ease-in-out;
}
.description {
padding: 10px;
}
.close:hover,
.close:focus {
color: #000;
text-decoration: none;
cursor: pointer;
}
.topbuttons .material-button {
display: inline-block;
}
.rmp-button {
}
.ecis-button {
}
.textbook-button{
}
.material-button {
border: none;
outline: none;
cursor: pointer;
color: white;
margin: 10px 10px 10px 0px;
padding: 10px 10px;
border-radius: 10px;
font-size: medium;
font-style: bold;
box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.4);
background: #ff9800;
position: relative;
overflow: hidden;
}
.material-button:after {
content: "";
position: absolute;
top: 50%;
left: 50%;
width: 5px;
height: 5px;
background: rgba(255, 255, 255, 0.5);
opacity: 0;
border-radius: 100%;
transform: scale(1, 1) translate(-50%);
transform-origin: 50% 50%;
}
@keyframes ripple {
0% {
transform: scale(0, 0);
opacity: 1;
}
15% {
transform: scale(25, 25);
opacity: 1;
}
100% {
opacity: 0;
transform: scale(40, 40);
}
}
.material-button:focus:not(:active)::after {
animation: ripple 1s ease-out;
}
#snackbar {
visibility: hidden;
min-width: 250px;
margin-left: -200px;
background-color: #333;
color: #fff;
border-radius: 2px;
padding: 16px;
position: fixed;
z-index: 1;
left: 50%;
bottom: 30px;
}
.descriptionli {
padding: 0px 5px 5px 5px;
}
#snackbar.show {
visibility: visible;
-webkit-animation: fadein 0.5s, fadeout 0.5s 2.5s;
animation: fadein 0.5s, fadeout 0.5s 2.5s;
}
#semesters {
padding: 5px;
margin-right: 10px;
margin-top: 10px;
}
#semesters:focus {
outline: 0;
}
.tooltip {
position: relative;
}
.tooltip .tooltiptext {
visibility: hidden;
background-color: black;
background:rgba(1,1,1,0.5);
color: #fff;
text-align: left;
border-radius: 6px;
font-size: 10px;
max-width: 100px;
margin-left: 5px;
padding: 5px 10px;
z-index: 2;
position: absolute;
}
.tooltip:hover .tooltiptext {
visibility: visible;
}
@-webkit-keyframes fadein {
from {
bottom: 0;
opacity: 0;
}
to {
bottom: 30px;
opacity: 1;
}
}
@keyframes fadein {
from {
bottom: 0;
opacity: 0;
}
to {
bottom: 30px;
opacity: 1;
}
}
@-webkit-keyframes fadeout {
from {
bottom: 30px;
opacity: 1;
}
to {
bottom: 0;
opacity: 0;
}
}
@keyframes fadeout {
from {
bottom: 30px;
opacity: 1;
}
to {
bottom: 0;
opacity: 0;
}
}

Binary file not shown.

View File

@@ -1 +0,0 @@
theme: jekyll-theme-cayman

View File

@@ -1 +0,0 @@
["ACC","ADV","ASE","AFR","AFS","ASL","AMS","AHC","ANT","ALD","ARA","ARE","ARI","ARC","AED","ARH","ART","AET","AAS","ANS","AST","BSN","BEN","BCH","BIO","BME","BDP","B A","BAX","BGS","CHE","CH","CHI","C E","CLA","C C","CGS","COM","CLD","CMS","CRP","C L","COE","CSE","C S","CON","CTI","CRW","CDI","EDC","CZ","DAN","DSC","D S","DES","DEV","D B","DRS","DCH","ECO","ELP","EDP","E E","ECE","EER","EMA","ENM","E M","E S","E","ESL","ENS","EVE","EVS","EUP","EUS","FIN","F A","FLU","FR","F H","G E","GRG","GEO","GER","GSD","GOV","GRS","GK","GUI","HAR","H S","HCT","HED","HEB","HIN","HIS","HDF","HDO","H E","HMN","ILA","I","ISP","INF","ITD","I B","IRG","ISL","ITL","ITC","JPN","J S","J","KIN","KOR","LAR","LTC","LAT","LAL","LAS","LAW","LEB","L A","LAH","LIN","MAL","MAN","MIS","MFG","MNS","MKT","MSE","M","M E","MDV","MAS","MEL","MES","M S","MOL","MUS","NSC","N S","NEU","NOR","N","NTR","OBO","OPR","O M","ORI","ORG","PER","PRS","PGE","PGS","PHM","PHL","PED","P S","PHY","PIA","POL","POR","PRC","PSY","P A","PBH","P R","RIM","RTF","R E","R S","RHE","R M","RUS","REE","SAN","SAX","STC","STM","S C","SEL","S S","S W","SOC","SPN","SPC","SED","SLH","STA","SDS","SUS","SWE","TAM","TXA","T D","TRO","TRU","TBA","TUR","T C","UKR","UGS","UDN","URB","URD","UTS","UTL","VIA","VIO","V C","VAS","VOI","WGS","WRT","YID","YOR"]

BIN
grades.db

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.5 KiB

View File

@@ -1,196 +0,0 @@
class Template {}
Template.Main = class {
static modal() {
return `<div class=modal id=myModal>
<div class=modal-content>
<span class=close>×</span>
<div class=card>
<div class=cardcontainer>
<h2 class=title id="title">Computer Fluency (C S 302)</h2>
<h2 class=profname id="profname">with <a id="professor_link">Bruce Porter</a></h2>
<div id="topbuttons" class=topbuttons>
<button class=material-button id="rateMyProf" style="background: #4CAF50;"> RMP </button>
<button class=material-button id="eCIS" style="background: #CDDC39;"> eCIS </button>
<button class=material-button id="textbook" style="background: #FFC107;"> Textbook </button>
<button class=material-button id="Syllabi"> Past Syllabi </button>
<button class=material-button id="saveCourse" value="add" style="background: #F44336;"> Save Course +</button>
</div>
</div>
</div>
<div class=card>
<div class=cardcontainer style="">
<div class="chartloader">
<div class="loader" id='descload'></div>
</div>
<ul class=description id="description" style="list-style-type:disc"></ul>
</div>
</div>
<div class=card style='text-align:center'>
<select id="semesters" style='text-align-last:center;color:#666666;fill:#666666;'>
</select>
<div class="chartloader">
<div class="loader" id='chartload'></div>
</div>
<div id="chartcontainer" class=cardcontainer>
<div id=chart></div>
</div>
</div>
</div>
</div>`;
}
static extension_button() {
return `<td data-th="Plus"><input type="image" class="distButton" id="distButton" width="20" height="20" src='${chrome.extension.getURL("images/disticon.png")}'/></td>`;
}
};
Template.Catalog = class {
static loading() {
return `<div style="text-align:center">
<div class="loader" id='loader'></div>
<br>
<h1 id="nextlabel"style="color: #FF9800;display:none;">Loading Courses</h1>
<h1 id="retrylabel"style="color: #F44336;display:none;">Failed to Load Courses</h1>
<br>
<button class=material-button id="retry" style="background: #F44336;display:none;">Retry</button>
</div>`;
}
};
Template.UTPlanner = class {
static modal() {
return `<div class=modal id=myModal>
<div class=modal-content>
<span class=close>×</span>
<div class=card>
<div class=cardcontainer>
<h2 class=title id="title">Computer Fluency (C S 302)</h2>
<h2 class=profname id="profname" style="margin-bottom:0px;">with Bruce Porter</h2>
<div id="topbuttons" class=topbuttons>
<button class=material-button id="moreInfo" style="background: #2196F3;"> More Info </button>
<button class=material-button id="textbook" style="background: #FFC107;"> Textbook </button>
<button class=material-button id="Syllabi"> Past Syllabi </button>
<button class=material-button id="saveCourse" value="add" style="background: #F44336;opacity:.4;"> Unable to Save</button>
</div>
</div>
</div>
<div class=card style='text-align:center'>
<select id="semesters" style='text-align-last:center;color:#666666;fill:#666666;'>
</select>
<div class="chartloader">
<div class="loader" id='chartload'></div>
</div>
<div id="chartcontainer" class=cardcontainer>
<div id=chart></div>
</div>
</div>
</div>
</div>`;
}
};
Template.Calendar = class {
static line(line) {
let { days, start_time, end_time, location_link, location_full } = line;
return `<p class='time' style='font-size:large;'>
<span style='display:inline-block;'>${days}:</span>
<span style='margin-left:10px;display:inline-block;text-align:center;'>${start_time} to ${end_time}</span>
<span style='float:right;display:inline-block;text-align:right;width: 25%;'>
<a target='_blank' style='color:#3c87a3;text-decoration:none;'href='${location_link}'>${location_full}</a>
</span>
</p>`;
}
static modal() {
return `<div id="myModal" class="modal">
<div class="modal-content">
<span class="close">&times;</span>
<div class="card">
<div id="colorStrip" style="height:10px;"></div>
<div class="cardcontainer">
<div id='header'>
<div style="display:flex;">
<h2 id="classname">Classname</h2>
</div>
<p id="prof">Prof</p>
</div>
<button id="info" class="matbut" style="font-size:medium; margin-right: auto; margin-left:auto; background: #2196F3;">More Info</button>
<button id="register" class="matbut" style="font-size:medium; margin-right: auto; margin-left:10px; background: #4CAF50;">Register</button>
<button id="remove" class="matbut" style="font-size:medium;margin:10px;background: #FF0000;">Remove</button>
</div>
</div>
</div>`;
}
};
Template.Popup = class {
static list_item(i, list_tile_color, unique, department, number, profname, list_sub_color, line) {
return `<li id='${i}' class='course_list_item'>
<div class='card course_list_card'>
<div class='container' style='background:${list_tile_color}'>
<button class='copy_button' title='Copy Unique #' value='${unique}'>
<i id='copyicon' class="material-icons copy_button_icon">content_copy</i>
</button>
<h4 class='course_name_truncate_box'>
<b>${department} ${number} <span class='course_list_item_subtext'> with ${profname} (${unique})</span></b>
</h4>
<p id='arrow' class='arrow'>&#9658;</p>
</div>
</div>
<div id='moreInfo' class='course_list_item_options'>
<p style='background-color:${list_sub_color};' class='course_list_item_time_box'>${line}</p>
<div id='infoButtons' class='course_list_item_options_button_container'>
<button class='material_button course_list_item_options_buttons remove_button' id='listRemove'>Remove</button>
<button class='material_button course_list_item_options_buttons register_button' id='register'>Register</button>
<button class='material_button course_list_item_options_buttons more_info_button' id='listMoreInfo'>More Info</button>
</div>
</div>
</li>`;
}
static conflict_message(conflict_message) {
return `<p id='conflict' class='conflict_message'>${conflict_message}</>`;
}
static line(line) {
let { days, start_time, end_time, location_link, location_full } = line;
return `<span class='time_line_days'>${days}:</span>
<span class='time_line_hours'>${start_time} to ${end_time}</span>
<span class='time_line_location'>
<a target='_blank' class= 'time_line_location_link' href='${location_link}'>${location_full}</a>
</span>
<br>`;
}
};
Template.Import = class {
static import_button() {
return `<button class='material-button' id='import' style='margin:15px 0px;'>${Text.button_text_default}</button><br>`;
}
static waitlist_import_button() {
return `<button class='material-button' id='import_waitlist' style='margin:0px'>${Text.waitlist_button_text_default}</button><br>`;
}
static store_waitlist_message() {
return `<h1 id="nextlabel"style="color: #FF9800;display:none;"></h1>`;
}
};
Template.Options = class {
static options_row(key, enabled) {
let button_text = enabled ? "Turn Off" : "Turn On";
let button_color = enabled ? Colors.closed : Colors.open;
let label_text = capitalizeString(key.replace(/([A-Z]+)*([A-Z][a-z])/g, "$1 $2"));
return `<h2 style="padding: 5px 16px 5px 16px; font-weight: normal;display: inline-block;text-align:left;">
${label_text}
</h2>
<button id="${key}" value=${enabled} class="material-button" style="display:inline-block;font-size:medium; float:right; background:${button_color}">
${button_text}
</button>
<br>`;
}
static contributor_card(username, name, image_url, profile_url) {
return `<div class='card contributor-card' id="${username}" data-url="${profile_url}">
<img class='contributor-image' src="${image_url}"></img>
${name ? `<p class='contributor-name'>${name}</p>` : ""}
<p class='contributor-username'>${username}</p>
</div>`;
}
};

View File

@@ -1,487 +0,0 @@
console.log(`UT Registration Plus background page: ${window.location.href}`);
var grades; // caching the grades database in memory for faster queries
var current_semesters = {};
var departments = [];
var should_open = false; // toggled flag for automatically opening popup on new pages when 'more info' hit
// these are the default options that the extension currently supports
const default_options = {
loadAll: true,
courseConflictHighlight: true,
storeWaitlist: true,
};
onStartup();
function onStartup() {
updateBadge(true);
loadDataBase();
getCurrentSemesters();
getCurrentDepartments();
}
/* Handle messages and their commands from content and popup scripts*/
chrome.runtime.onMessage.addListener(function (request, sender, response) {
switch (request.command) {
case "courseStorage":
if (request.action == "add") {
add(request, sender, response);
}
if (request.action == "remove") {
remove(request, sender, response);
}
break;
case "isSingleConflict":
isSingleConflict(request.dtarr, request.unique, response);
break;
case "checkConflicts":
checkConflicts(response);
break;
case "updateBadge":
updateBadge();
break;
case "updateStatus":
updateStatus(response);
break;
case "alreadyContains":
alreadyContains(request.unique, response);
break;
case "updateCourseList":
updateTabs();
break;
case "gradesQuery":
executeQuery(request.query, response);
break;
case "currentSemesters":
response({ semesters: current_semesters });
getCurrentSemesters();
break;
case "currentDepartments":
response({ departments: departments });
break;
case "setOpen":
should_open = true;
chrome.tabs.create({ url: request.url });
break;
case "shouldOpen":
response({ open: should_open });
should_open = false;
break;
case "getOptionsValue":
getOptionsValue(request.key, response);
break;
case "setOptionsValue":
setOptionsValue(request.key, request.value, response);
break;
default:
const xhr = new XMLHttpRequest();
const method = request.method ? request.method.toUpperCase() : "GET";
xhr.open(method, request.url, true);
console.log(request);
xhr.onload = () => {
console.log(xhr.responseUrl);
response(xhr.responseText);
};
xhr.onerror = () => response(xhr.statusText);
if (method == "POST") {
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
}
xhr.send(request.data);
break;
}
return true;
});
/* Initially set the course data in storage */
chrome.runtime.onInstalled.addListener(function (details) {
if (details.reason == "install") {
setDefaultOptions();
chrome.storage.sync.get("savedCourses", function (data) {
if (!data.savedCourses) {
chrome.storage.sync.set({
savedCourses: [],
});
}
});
} else if (details.reason == "update") {
// if there's been an update, call setDefaultOptions in case their settings have gotten wiped
setDefaultOptions();
console.log("updated");
}
});
chrome.storage.onChanged.addListener(function (changes) {
for (key in changes) {
if (key === "savedCourses") {
updateBadge(false, changes.savedCourses.newValue); // update the extension popup badge whenever the savedCourses have been changed
}
}
});
// get the value of an option if it exists
function getOptionsValue(key, sendResponse) {
chrome.storage.sync.get("options", function (data) {
if (!data.options) {
setDefaultOptions();
} else {
sendResponse({
value: data.options[key],
});
}
});
}
// set the value of an option if it exists
function setOptionsValue(key, value, sendResponse) {
chrome.storage.sync.get("options", function (data) {
let new_options = data.options;
if (!data.options) {
// if there are no options set, set the defaults
setDefaultOptions();
new_options = default_options;
}
new_options[key] = value;
chrome.storage.sync.set(
{
options: new_options,
},
function () {
sendResponse({
value: new_options[key],
});
}
);
});
}
// set the default options if the options haven't been set before
function setDefaultOptions() {
chrome.storage.sync.get("options", function (data) {
if (!data.options) {
chrome.storage.sync.set(
{
options: default_options,
},
function () {
console.log("default options:", default_options);
}
);
}
});
}
async function getCurrentSemesters() {
let webData;
if(Object.keys(current_semesters).length > 0) {
chrome.storage.local.set({
semesterCache: current_semesters
});
}
async function goFetch(linkend="") {
console.log("lk " + linkend)
return fetch("https://registrar.utexas.edu/schedules/" + linkend)
.then((response) => {
return response.text()
.then((data) => {
return data;
}).catch((err) => {
console.log(err);
})
});
}
await goFetch().then((data) => {webData = data});
if(webData == null) {
webData = ""
}
let arr = webData.split("\n");
let i = 0
for(let row=0; row<arr.length; row++) {
let currentRow = arr[row]
if(currentRow.startsWith('<li><a href="https://registrar.utexas.edu/schedules/') && currentRow[52] != "a") {
let newWebData;
// let start = currentRow.indexOf('Schedule">')+10;
let start = Math.max(currentRow.lastIndexOf('Summer'), Math.max(currentRow.lastIndexOf('Spring'), currentRow.lastIndexOf('Fall')))
let end = currentRow.indexOf('</a></li>');
console.log(currentRow)
console.log(start + " " + end)
let name = currentRow.substring(start,end);
console.log("my name: " + name)
let num = currentRow.indexOf('"https://registrar.utexas.edu/schedules/">')+53;
let numend = currentRow.indexOf('" target');
let short_sem_num = currentRow.substring(num,numend);
current_semesters[name] = "code";
await goFetch(short_sem_num).then((data) => {newWebData = data});
arr2 = newWebData.split("\n")
for(let row2=0; row2<arr2.length; row2++) {
if(arr2[row2].startsWith('<div class="gobutton"><a href="')) {
let start2 = arr2[row2].indexOf('<div class="gobutton"><a href="')+31;
let end2 = arr2[row2].indexOf('" target="');
var scheduleLink = arr2[row2].substring(start2,end2);
var sem_num = scheduleLink.substring(scheduleLink.lastIndexOf("/") + 1).trim();
if (current_semesters[name] != sem_num) {
current_semesters[name] = sem_num;
}
}
}
}
}
}
// use the utexas review api for getting the list of departments
function getCurrentDepartments() {
$.get("https://raw.githubusercontent.com/sghsri/UT-Registration-Plus/master/docs/departments.json", function (response) {
if (response) {
departments = JSON.parse(response);
}
});
}
// update the badge text to reflect the new changes
function updateBadge(first, new_changes) {
if (new_changes) {
updateBadgeText(first, new_changes);
} else {
chrome.storage.sync.get("savedCourses", function (data) {
let courses = data.savedCourses;
updateBadgeText(first, courses);
});
}
}
// update the badge text to show the number of courses that have been saved by the user
function updateBadgeText(first, courses) {
let badge_text = courses.length > 0 ? `${courses.length}` : "";
let flash_time = !first ? 200 : 0;
chrome.browserAction.setBadgeText({
text: badge_text,
});
if (!first) {
// if isn't the first install of the extension, flash the badge to bring attention to it
chrome.browserAction.setBadgeBackgroundColor({
color: Colors.badge_flash,
});
}
setTimeout(function () {
chrome.browserAction.setBadgeBackgroundColor({
color: Colors.badge_default,
});
}, flash_time);
}
/* Find all the conflicts in the courses and send them out/ if there is even a conflict*/
function checkConflicts(sendResponse) {
chrome.storage.sync.get("savedCourses", function (data) {
var conflicts = [];
var courses = data.savedCourses;
for (let i = 0; i < courses.length; i++) {
for (let j = i + 1; j < courses.length; j++) {
let course_a = courses[i];
let course_b = courses[j];
if (isConflict(course_a.datetimearr, course_b.datetimearr)) conflicts.push([course_a, course_b]);
}
}
sendResponse({
isConflict: conflicts.length !== 0,
between: conflicts.length ? conflicts : undefined,
});
});
}
/* Find if the course at unique and with currdatearr is contained in the saved courses and if it conflicts with any other courses*/
function isSingleConflict(currdatearr, unique, sendResponse) {
chrome.storage.sync.get("savedCourses", function (data) {
var courses = data.savedCourses;
var conflict_list = [];
var conflict = false;
var contains = false;
for (let i = 0; i < courses.length; i++) {
let course = courses[i];
if (isConflict(currdatearr, course.datetimearr)) {
conflict = true;
conflict_list.push(course);
}
if (!contains && isSameCourse(course, unique)) {
contains = true;
}
}
sendResponse({
isConflict: conflict,
alreadyContains: contains,
conflictList: conflict_list,
});
});
}
/* Check if conflict between two date-time-arrs*/
function isConflict(adtarr, bdtarr) {
for (var i = 0; i < adtarr.length; i++) {
var current_day = adtarr[i][0];
var current_times = adtarr[i][1];
for (var j = 0; j < bdtarr.length; j++) {
var next_day = bdtarr[j][0];
var next_times = bdtarr[j][1];
if (next_day == current_day) {
if (current_times[0] < next_times[1] && current_times[1] > next_times[0]) {
return true;
}
}
}
}
return false;
}
/* Add the requested course to the storage*/
function add(request, sender, sendResponse) {
chrome.storage.sync.get("savedCourses", function (data) {
var courses = data.savedCourses;
if (!contains(courses, request.course.unique)) {
courses.push(request.course);
console.log(courses);
chrome.storage.sync.set({
savedCourses: courses,
});
}
sendResponse({
done: "Added: (" + request.course.unique + ") " + request.course.coursename,
label: "Remove Course -",
value: "remove",
});
});
}
/* Find and Remove the requested course from the storage*/
function remove(request, sender, sendResponse) {
chrome.storage.sync.get("savedCourses", function (data) {
var courses = data.savedCourses;
console.log(courses);
var index = 0;
while (index < courses.length && courses[index].unique != request.course.unique) {
index++;
}
courses.splice(index, 1);
chrome.storage.sync.set({
savedCourses: courses,
});
sendResponse({
done: "Removed: (" + request.course.unique + ") " + request.course.coursename,
label: "Add Course +",
value: "add",
});
});
}
/* Find if the unique is already contained within the storage*/
function alreadyContains(unique, sendResponse) {
chrome.storage.sync.get("savedCourses", function (data) {
var courses = data.savedCourses;
sendResponse({
alreadyContains: contains(courses, unique),
});
});
}
// find if a course with the current unique number exists in the user's saved courses
function contains(courses, unique) {
var i = 0;
while (i < courses.length) {
if (isSameCourse(courses[i], unique)) {
return true;
}
i++;
}
return false;
}
// does it have the same unique number as provided
function isSameCourse(course, unique) {
return course.unique == unique;
}
// send a message to every tab open to updateit's course list (and thus recalculate its conflicts highlighting)
function updateTabs() {
chrome.tabs.query({}, function (tabs) {
for (var i = 0; i < tabs.length; i++) {
chrome.tabs.sendMessage(tabs[i].id, {
command: "updateCourseList",
});
}
});
}
// const UPDATE_INTERVAL = 1000 * 60 * 16;
// setInterval(updateStatus, UPDATE_INTERVAL);
// // updateStatus();
// function updateStatus(sendResponse) {
// chrome.storage.sync.get("savedCourses", function (data) {
// var courses = data.savedCourses;
// var no_change = true;
// for (let i = 0; i < courses.length; i++) {
// try {
// let c = courses[i];
// let old_status = c.status;
// let old_link = c.link;
// $.ajax({
// url: old_link,
// success: function (result) {
// if (result) {
// console.log(result);
// var object = $("<div/>").html(result).contents();
// let new_status = object.find('[data-th="Status"]').text();
// let register_link = object.find('td[data-th="Add"] a');
// if (register_link) register_link = register_link.attr("href");
// var haschanged = new_status == old_status && register_link == old_link;
// if (!haschanged) console.log(c.unique + " updated from " + old_status + " to " + new_status + " and " + old_link + " to " + register_link);
// no_change &= haschanged;
// c.registerlink = register_link;
// c.status = new_status;
// }
// },
// });
// } catch (e) {
// console.log(e);
// console.log("Not logged into UT Coursebook. Could not update class statuses.");
// }
// }
// if (!no_change) {
// chrome.storage.sync.set({
// savedCourses: courses,
// });
// console.log("updated status");
// }
// });
// }
// execute a query on the grades database
function executeQuery(query, sendResponse) {
var res = grades.exec(query)[0];
sendResponse({
data: res,
});
}
/* Load the database*/
function loadDataBase() {
sql = window.SQL;
loadBinaryFile("grades.db", function (data) {
var sqldb = new SQL.Database(data);
grades = sqldb;
});
}
/* load the database from file */
function loadBinaryFile(path, success) {
var xhr = new XMLHttpRequest();
xhr.open("GET", chrome.extension.getURL(path), true);
xhr.responseType = "arraybuffer";
xhr.onload = function () {
var data = new Uint8Array(xhr.response);
var arr = new Array();
for (var i = 0; i != data.length; ++i) arr[i] = String.fromCharCode(data[i]);
success(arr.join(""));
};
xhr.send();
}

View File

@@ -1,291 +0,0 @@
var color_counter = 0;
var {
calendar_fade_time,
button_delay
} = Timing;
var saved_courses = [];
var curr_course = {}
$("#calendar").after(Template.Calendar.modal());
chrome.storage.sync.get("savedCourses", function (data) {
// Iterate through each saved course and add to 'event'
saved_courses = data.savedCourses;
console.log(saved_courses);
let event_source = buildEventSource(saved_courses);
$("#calendar").fullCalendar({
editable: false, // Don't allow editing of events
handleWindowResize: true,
weekends: false, // will hide Saturdays and Sundays
slotDuration: "00:30:00", // 15 minute intervals on vertical column
slotEventOverlap: false, // No overlapping between events
defaultView: "agendaWeek", // Only show week view
header: false, // Hide buttons/titles
minTime: "08:00:00", // Start time
maxTime: "21:00:01", // End time
columnHeaderFormat: "ddd", // Only show day of the week names
displayEventTime: true, // Display event time
allDaySlot: false,
Duration: {
hours: 1
},
height: 'auto',
events: event_source,
slotLabelFormat: [
'h:mm A' // lower level of text
],
eventRender: function (event, element, view) {
$(element).css("padding", "5px").css("margin-bottom", "5px");
},
eventClick: function (data, event, view) {
displayModal(data)
}
});
});
function displayModal(data) {
$("#myModal").fadeIn(calendar_fade_time);
$("#colorStrip").css('background-color', data.color);
curr_course = saved_courses[data.index];
setUpModal()
}
function setUpModal() {
let {
coursename,
unique,
datetimearr,
profname,
status,
registerlink
} = curr_course;
$("#classname").html(`${coursename} <span style='font-size:small'>(${unique})</span>`);
buildTimeTitle(datetimearr);
$("#prof").html(`with <span style='font-weight:bold;'>${capitalizeString(profname)}</span>`);
setRegisterButton(status, registerlink)
}
function setRegisterButton(status, registerlink) {
if (canNotRegister(status, registerlink))
$("#register").text("Can't Register").css("background-color", Colors.closed);
else if (status.includes("waitlisted"))
$("#register").text("Join Waitlist").css("background-color", Colors.waitlisted);
else
$("#register").text("Register").css("background-color", Colors.open);
}
function buildTimeTitle(datetimearr) {
$('#timelines').remove();
var arr = convertDateTimeArrToLine(datetimearr)
var output = "";
for (let i = 0; i < arr.length; i++) {
let line = arr[i];
output += Template.Calendar.line(line);
}
$("#header").after(`<div id='timelines'>${output}</div`);
}
// Iterate through each saved course and add to 'event'
function buildEventSource(saved_courses) {
color_counter = 0;
let event_source = [];
var hours = 0;
for (let i = 0; i < saved_courses.length; i++) {
let {
coursename,
datetimearr
} = saved_courses[i];
let number = separateCourseNameParts(coursename).number;
let class_length = parseInt(number.charAt(0));
let multi_semester_code = number.slice(-1);
if (["A","B"].includes(multi_semester_code)) {
hours += Math.floor(class_length/2);
} else if (["X","Y","Z"].includes(multi_semester_code)) {
hours += Math.floor(class_length/3);
} else {
hours += class_length;
}
for (let j = 0; j < datetimearr.length; j++) {
let session = datetimearr[j]; // One single session for a class
let event_obj = setEventForSection(session, color_counter, i);
event_source.push(event_obj);
}
color_counter++;
}
displayMetaData(hours, saved_courses);
return event_source;
}
function displayMetaData(hours, saved_courses) {
$("#hours").text(hours + " Hours");
$("#num").text(saved_courses.length + " Courses");
}
//create the event object for every section
function setEventForSection(session, colorCounter, i) {
let full_day = days.get(session[0]);
let course = saved_courses[i];
let {
coursename,
profname,
} = course;
let {
department,
number
} = separateCourseNameParts(coursename)
beg_day = calculateBeginningDate(full_day)
start_date = formatCalculateDate(beg_day, full_day, session[1][0]);
end_date = formatCalculateDate(beg_day, full_day, session[1][1]);
event_obj = {
title: `${department}-${number} with ${capitalizeString(profname)}`,
start: start_date,
end: end_date,
color: Colors.material_colors[colorCounter],
building: session[2],
index: i,
allday: false
};
return event_obj;
}
function formatCalculateDate(beg_day, full_day, hour) {
return beg_day + moment().day(full_day)._d.toString().split(" ")[2] + "T" + hour + ":00";
}
function calculateBeginningDate(full_day) {
var year = moment().day(full_day)._d.toString().split(" ")[3];
var month_num = moment(moment().day(full_day)._d.toString().split(" ")[1], "MMM").format('MM');
return `${year}-${month_num}-`;
}
function updateCalendar() {
chrome.storage.sync.get("savedCourses", function (data) {
saved_courses = data.savedCourses
let event_source = buildEventSource(saved_courses);
$('#calendar').fullCalendar('removeEventSources');
$("#calendar").fullCalendar('addEventSource', event_source, true);
});
}
chrome.runtime.onMessage.addListener(
function (request, sender, sendResponse) {
if (request.command == "updateCourseList" || request.command == "courseAdded") {
updateCalendar();
}
}
);
$("#info").click(() => {
openMoreInfoWithOpenModal(curr_course.link);
});
$("#save").click(() => {
takePicture();
});
$("#clear").click(() => {
/*Clear the list and the storage of courses*/
chrome.storage.sync.set({
savedCourses: []
});
updateAllTabsCourseList();
updateCalendar();
});
$("#remove").click(() => {
setTimeout(() => {
chrome.runtime.sendMessage({
command: "courseStorage",
course: curr_course,
action: "remove"
}, function () {
$("#myModal").fadeOut(calendar_fade_time);
updateCalendar();
updateAllTabsCourseList();
});
}, button_delay);
});
$("#register").click(function () {
let {
registerlink,
status
} = curr_course;
if (!canNotRegister(status, registerlink)) {
setTimeout(() => {
window.open(registerlink);
}, button_delay);
}
});
$("#export").click(function () {
var cal = ics();
var calendarEvents = $('#calendar').fullCalendar('clientEvents');
for (i in calendarEvents) {
var event = calendarEvents[i];
buildICSFile(cal, event);
}
cal.download("My_Course_Calendar");
});
function buildICSFile(cal, event) {
let {
title,
start,
end,
building
} = event;
let class_name = title.split('with')[0];
let description = `with ${title.split('with')[1]}`;
let time = start._d.toUTCString();
cal.addEvent(class_name, description, building, start._i, end._i, {
rrule: `RRULE:FREQ=WEEKLY;BYDAY=${time.substring(0, time.indexOf(",") - 1).toUpperCase()};INTERVAL=1`
});
}
function takePicture() {
var width = $("#calendar").width() * window.devicePixelRatio;
var height = $("#calendar").height() * window.devicePixelRatio;
let cropper = document.createElement('canvas').getContext('2d');
html2canvas(document.querySelector("#calendar"), Export.png_options).then(c => {
cropper.canvas.width = width;
cropper.canvas.height = height;
cropper.drawImage(c, 0, 0);
var a = document.createElement('a');
a.href = cropper.canvas.toDataURL("image/png");
a.download = 'mySchedule.png';
a.click();
});
}
/*Close Modal when hit escape*/
$(document).keydown((e) => {
if (e.keyCode == 27) {
$("#myModal").fadeOut(calendar_fade_time);
}
});
$('.close').click(function () {
close();
});
$('#myModal').click(function (event) {
if (event.target.id == 'myModal') {
close();
}
});
function close() {
$("#myModal").fadeOut(calendar_fade_time);
}

View File

@@ -1,93 +0,0 @@
class Timing {}
Timing.fade_time = 100;
Timing.calendar_fade_time = 100;
Timing.button_delay = 75;
class Colors {}
Colors.material_colors = [
"#4CAF50",
"#CDDC39",
"#FFC107",
"#2196F3",
"#F57C00",
"#9C27B0",
"#FF5722",
"#673AB7",
"#FF5252",
"#E91E63",
"#009688",
"#00BCD4",
"#4E342E",
"#424242",
"#9E9E9E",
];
Colors.open = "#4CAF50";
Colors.waitlisted = "#FF9800";
Colors.closed = "#FF5722";
Colors.no_status = "#607D8B";
Colors.open_light = "#C8E6C9";
Colors.waitlisted_light = "#FFE0B2";
Colors.closed_light = "#FFCCBC";
Colors.no_status_light = "#CFD8DC";
Colors.highlight_conflict = "#F44336";
Colors.highlight_default = "#333333";
Colors.highlight_saved = "#4CAF50";
Colors.badge_flash = "#FF5722";
Colors.badge_default = "#bf5700";
class Export {}
Export.png_options = {
foreignObjectRendering: true,
logging: true,
removeContainer: true,
async: true,
};
class Popup {}
Popup.num_semesters = 2;
/*
* Funny comments that popup when no classes have been chosen
*/
class Text {}
Text.emptyText = function () {
let arr = [
"Doesn't Look Like Anything To Me.",
"You Can't Fail Classes You're Not In.",
"Pro-Tip: Don't Take O-Chem.",
"Jendy's Fofofo™",
"Fine Dining at Jester City Limits",
"Rec Sports is full and it's only 2pm.",
"Hope Domino is doing well rn &#129402;",
"The year is 2055 and Welch still isn't finished.",
"Wear a Mask.",
"Motivation dropping faster than ur GPA",
"No Work Happens On PCL 5th Floor.",
"Sophomore But Freshman By Credit.",
"Pain is temporary, GPA is forever.",
"You've Yee'd Your Last Haw.",
"lol everything is already waitlisted.",
"At Least You're Not At A&M.",
`It's ${moment().format("h:mm")} and OU Still Sucks.`,
"TeXAs iS BaCK GuYZ",
"'Academically Challenged'",
"Does McCombs teach Parseltongue?",
"Feel bad if you say Wampus.",
"No Cruce Enfrente Del Bus.",
"Midterm 1 has been Unmuted",
"Omae Wa Mou Shindeiru...",
"Bevo Bucks are the new Bitcoin",
"Every day, another brick disappears from Speedway",
"The GDC will annex the EER one day",
"To hike to Kins, or not to hike to Kins...",
];
let index = Math.floor(Math.random() * arr.length);
return arr[index];
};
Text.button_text_default = "<span style='font-size:small'>Import to </span><b>UT Reg +<b>";
Text.waitlist_button_text_default = "<span style='font-size:small'>Import Waitlists to </span><b>UT Reg +<b>";
Text.button_success = "Courses Saved!";

View File

@@ -1,627 +0,0 @@
console.log(`UT Registration Plus is running on this page: ${window.location.href}`);
var curr_course = {}
var semester_code = new URL(window.location.href).pathname.split('/')[4];
var done_loading = true;
var next = $("#next_nav_link");
if (next) {
chrome.runtime.sendMessage({
command: "getOptionsValue",
key: "loadAll",
}, function (response) {
if(response.value){
$('[title*="next listing"]').remove();
}
});
}
//This extension may be super lit, but you know what's even more lit?
//Matthew Tran's twitter and insta: @MATTHEWTRANN and @matthew.trann
if (document.querySelector('#fos_fl')) {
let params = (new URL(document.location)).searchParams;
let dep = params.get("fos_fl");
let level = params.get("level");
if (dep && level) {
if (dep.length == 3 && (level == 'U' || level == 'L' || level == 'G')) {
document.querySelector('#fos_fl').value = dep;
document.querySelector('#level').value = level;
}
}
}
//make heading and modal
if (!$("#kw_results_table").length) {
$("table").after(Template.Catalog.loading());
$("#container").prepend(Template.Main.modal());
$("#myModal").prepend("<div id='snackbar'>save course popup...</div>");
// now add to the table
$("table thead th:last-child").after('<th scope=col>Plus</th>');
$('table').find('tr').each(function () {
if (!($(this).find('td').hasClass("course_header")) && $(this).has('th').length == 0) {
$(this).append(Template.Main.extension_button());
}
});
}
if(isIndividualCoursePage()){
chrome.runtime.sendMessage({
command: "shouldOpen",
}, function (response) {
if(response.open){
$("#distButton").click();
}
});
}
updateListConflictHighlighting();
$("body").on('click', '#distButton', function () {
var row = $(this).closest('tr');
$('.modal-content').stop().animate({
scrollTop: 0
}, 500);
$(this).blur();
curr_course = getCourseInfo(row);
getDistribution(curr_course);
});
function updateLinks(course_info, first_name) {
let {
prof_name,
number
} = course_info;
course_info["first_name"] = first_name;
course_info["links"]["rate_my_prof"] = `http://www.ratemyprofessors.com/search.jsp?queryBy=teacherName&schoolName=university+of+texas+at+austin&queryoption=HEADER&query=${first_name} ${prof_name};&facetSearch=true`;
course_info["links"]["ecis"] = profname ? `http://utdirect.utexas.edu/ctl/ecis/results/index.WBX?&s_in_action_sw=S&s_in_search_type_sw=N&s_in_search_name=${prof_name}%2C%20${first_name}` :
`http://utdirect.utexas.edu/ctl/ecis/results/index.WBX?s_in_action_sw=S&s_in_search_type_sw=C&s_in_max_nbr_return=10&s_in_search_course_dept=${department}&s_in_search_course_num=${number}`;
}
function buildCourseLinks(course_info) {
let {
department,
number,
unique,
prof_name
} = course_info
links = {
"textbook": `https://www.universitycoop.com/adoption-search-results?sn=${semester_code}__${department}__${number}__${unique}`,
"syllabi": `https://utdirect.utexas.edu/apps/student/coursedocs/nlogon/?year=&semester=&department=${department}&course_number=${number}&course_title=&unique=&instructor_first=&instructor_last=${prof_name}&course_type=In+Residence&search=Search`,
//default ones (before first name can be used)
"rate_my_prof": "http://www.ratemyprofessors.com/campusRatings.jsp?sid=1255",
"ecis": "http://utdirect.utexas.edu/ctl/ecis/results/index.WBX?"
}
course_info["links"] = links;
return course_info;
}
function buildBasicCourseInfo(row, course_name, individual) {
let {
name,
department,
number
} = separateCourseNameParts(course_name);
let instructor_text = $(row).find('td[data-th="Instructor"]').text();
let has_initial = instructor_text.indexOf(',') > 0;
course_info = {
"full_name": course_name,
"name": name,
"department": department,
"number": number,
"individual": individual ? individual : $(row).find('td[data-th="Unique"] a').prop('href'),
"register": $(row).find('td[data-th="Add"] a').prop('href'),
"unique": $(row).find('td[data-th="Unique"]').text(),
"status": $(row).find('td[data-th="Status"]').text(),
"prof_name": instructor_text ? has_initial ? capitalizeString(instructor_text.split(', ')[0]) : capitalizeString(instructor_text) : "Undecided",
"initial": instructor_text && has_initial ? instructor_text.split(', ')[1].substring(0, 1) : "",
"time_data": {
"days": $(row).find('td[data-th="Days"]>span').toArray().map(x => $(x).text().trim()),
"times": $(row).find('td[data-th="Hour"]>span').toArray().map(x => $(x).text().trim()),
"places": $(row).find('td[data-th="Room"]>span').toArray().map(x => $(x).text().trim())
},
"links": {}
}
return buildCourseLinks(course_info);
}
/*For a row, get all the course information and add the date-time-lines*/
function getCourseInfo(row) {
let course_name = "";
let course_row = {}
let individual = undefined;
if (isIndividualCoursePage()) {
course_name = $("#details h2").text();
course_row = $('table');
individual = document.URL;
} else {
$('table').find('tr').each(function () {
if ($(this).find('td').hasClass("course_header")) {
course_name = $(this).find('td').text() + "";
}
if ($(this).is(row)) {
course_row = row;
return false;
}
});
}
curr_course = buildBasicCourseInfo(course_row, course_name, individual);
getDescription(curr_course);
return curr_course;
}
function saveCourse() {
console.log(curr_course);
console.log(JSON.stringify(curr_course));
let {
full_name,
unique,
prof_name,
status,
individual,
register
} = curr_course;
let dtarr = getDayTimeArray(undefined, curr_course);
var c = new Course(full_name, unique, prof_name, dtarr, status, individual, register);
chrome.runtime.sendMessage({
command: "courseStorage",
course: c,
action: $("#saveCourse").val()
}, function (response) {
$("#saveCourse").text(response.label);
$("#saveCourse").val(response.value);
$("#snackbar").text(response.done);
toggleSnackbar();
chrome.runtime.sendMessage({
command: "updateCourseList"
});
});
}
/* Update the course list to show if the row contains a course that conflicts with the saved course is one of the saved courses */
function updateListConflictHighlighting(start = 0) {
chrome.runtime.sendMessage({
command: "getOptionsValue",
key: "courseConflictHighlight",
}, function (response) {
let canHighlight = response.value;
$('table').find('tr').each(function (i) {
if (i >= start) {
if (!($(this).find('td').hasClass("course_header")) && $(this).has('th').length == 0) {
var unique = $(this).find('td[data-th="Unique"]').text();
chrome.runtime.sendMessage({
command: "isSingleConflict",
dtarr: getDayTimeArray(this),
unique: unique
}, (response) => {
let {
isConflict,
alreadyContains,
conflictList
} = response
updateTextHighlighting($(this).find('td'), canHighlight, isConflict, alreadyContains, conflictList, $(this), unique);
});
}
}
});
});
}
function updateTextHighlighting(tds, canHighlight, isConflict, alreadyContains, conflictList, row, unique) {
conflict_texts = row.find('.tooltiptext');
let unique_list = conflictList.filter(function(course){
if(course.unique != unique){
return true;
}
return false;
}).map(function(course){
let { name, department, number} = separateCourseNameParts(course.coursename);
return `${department} ${number} (${course.unique})`;
});
if(isConflict && unique_list.length){
if(conflict_texts){
row.find('.tooltiptext').remove();
}
row.addClass('tooltip');
row.append(`<span class='tooltiptext'><span style='text-decoration: underline;'>Conflicts:<br></span> ${unique_list.join('<br>')}</span>`);
} else {
row.removeClass('tooltip');
conflict_texts.remove();
}
let current_color = rgb2hex(tds.css('color'));
if (isConflict && canHighlight && !alreadyContains) {
if (current_color != Colors.highlight_conflict){
tds.css('color', Colors.highlight_conflict).css('text-decoration', 'line-through').css('font-weight', 'normal')
}
} else if (!alreadyContains) {
if (tds.css('color') != Colors.highlight_default)
tds.css('color', Colors.highlight_default).css('text-decoration', 'none').css('font-weight', 'normal');
}
if (alreadyContains) {
if (tds.css('color') != Colors.highlight_saved)
tds.css('color', Colors.highlight_saved).css('text-decoration', 'none').css('font-weight', 'bold');
}
}
/* For a row, get the date-time-array for checking conflicts*/
function getDayTimeArray(row, course_info) {
var day_time_array = []
let days = course_info ? course_info["time_data"]["days"] : $(row).find('td[data-th="Days"]>span').toArray().map(x => $(x).text().trim());
let times = course_info ? course_info["time_data"]["times"] : $(row).find('td[data-th="Hour"]>span').toArray().map(x => $(x).text().trim());
let places = course_info ? course_info["time_data"]["places"] : $(row).find('td[data-th="Room"]>span').toArray().map(x => $(x).text().trim());
for (var i = 0; i < days.length; i++) {
let date = days[i];
let time = times[i];
let place = places[i];
for (var j = 0; j < date.length; j++) {
let letter = date.charAt(j);
if (letter == "T" && j < date.length - 1 && date.charAt(j + 1) == "H") {
day_time_array.push(["TH", convertTime(time), place]);
} else {
if (letter != "H")
day_time_array.push([letter, convertTime(time), place]);
}
}
}
return day_time_array;
}
function convertDateTimeArrToLine(date, time, place) {
let arr = separateDays(date)
let output = prettifyDaysText(arr)
let building = place.substring(0, place.search(/\d/) - 1);
building = building ? building : "Undecided Location";
return `${output} at ${time.replace(/\./g, '').replace(/\-/g, ' to ')} in <a style='font-size:medium' target='_blank' href='https://maps.utexas.edu/buildings/UTM/${building}'>${building}</>`;
}
function badData(course_data, res) {
return typeof res == 'undefined' || course_data["prof_name"] == "Undecided";
}
/*Query the grades database*/
function getDistribution(course_data, sem) {
toggleChartLoading(true);
let query = buildQuery(course_data, sem);
chrome.runtime.sendMessage({
command: "gradesQuery",
query: query
}, function (response) {
var res = response.data;
if (!sem) {
openDialog(course_data, res);
} else {
var data = badData(course_data, res) ? [] : res.values[0];
setChart(data);
}
});
}
function buildTitle(course_data) {
return `${course_data["name"]} (${course_data["department"]} ${course_data["number"]})`
}
function buildTimeTitle(course_info) {
$("h2.dateTimePlace").remove();
let {
days,
times,
places
} = course_info["time_data"]
var lines = [];
for (let i = 0; i < days.length; i++) {
var date = days[i];
var time = times[i];
var place = places[i];
lines.push($(`<h2 class="dateTimePlace">${convertDateTimeArrToLine(date, time, place)}</th>`));
}
return lines;
}
function buildProfTitle(course_data) {
const {
initial,
prof_name
} = course_data;
return `with ${initial?initial+". ":""}${prof_name}`;
}
function buildSemestersDropdown(course_data, res) {
$("#semesters").empty();
if (badData(course_data, res)) {
$("#semesters").append("<option>No Data</option>")
} else {
var semesters = res.values[0][18].split(",");
semesters.sort(semesterSort);
semesters.reverse().unshift('Aggregate');
var sems = [];
for (var i = 0; i < semesters.length; i++) {
sems.push($(`<option value="${semesters[i]}">${semesters[i]}</option>`));
}
$("#semesters").append(sems);
}
}
function displayBasicCourseInfo(course_info){
$("#title").text(buildTitle(course_info))
$("#topbuttons").before(buildTimeTitle(course_info));
$("#profname").text(buildProfTitle(course_info));
$("#myModal").fadeIn(Timing.fade_time);
console.log(course_info);
}
/*Open the modal and show all the data*/
function openDialog(course_info, res) {
displayBasicCourseInfo(course_info);
//initial text on the "save course button"
chrome.runtime.sendMessage({
command: "alreadyContains",
unique: course_info["unique"]
}, function (response) {
let button_text = response.alreadyContains ? "Remove Course -" : "Add Course +";
let button_val = response.alreadyContains ? "remove" : "add";
$("#saveCourse").text(button_text);
$("#saveCourse").val(button_val);
});
buildSemestersDropdown(course_info, res)
var data = []
if (!badData(course_info, res))
data = res.values[0];
allowClosing();
setChart(data);
}
function setChart(data) {
// set up the chart
toggleChartLoading(false);
Highcharts.chart('chart', buildChartConfig(data), function (chart) { // on complete
if (data.length == 0) {
//if no data, then show the message and hide the series
chart.renderer.text('Could not find data for this Instructor teaching this Course.', 100, 120)
.css({
fontSize: '20px',
width: '300px',
align: 'center',
left: '160px'
})
.add();
$.each(chart.series, function (i, ser) {
ser.hide();
});
}
});
}
var error_message = "<p style='color:red;font-style:bold'>You have been logged out. Please refresh the page and log back in using your UT EID and password.</p>";
function buildFormattedDescription(description_lines) {
let description = ""
for (let i in description_lines) {
let sentence = description_lines[i];
if (sentence.indexOf("Prerequisite") == 0)
sentence = `<li style='font-weight: bold;' class='descriptionli'>${sentence}</li>`;
else if (sentence.indexOf("May be") >= 0)
sentence = `<li style='font-style: italic;' class='descriptionli'>${sentence}</li>`;
else if (sentence.indexOf("Restricted to") == 0)
sentence = `<li style='color:red;' class='descriptionli'>${sentence}</li>`;
else
sentence = `<li class='descriptionli'>${sentence}</li>`;
description += sentence;
}
if (!description)
description = error_message;
return description;
}
function extractFirstName(response_node) {
let full_name = response_node.find('td[data-th="Instructor"]').text().split(', ');
let first = full_name[full_name.length - 1];
first = first.indexOf(' ') > 0 ? first.split(' ')[0] : first;
return capitalizeString(first);
}
function displayDescription(description) {
toggleDescriptionLoading(false);
$("#description").animate({
'opacity': 0
}, 200, function () {
$(this).html(description).animate({
'opacity': 1
}, 200);
});
}
/*Get the course description from the profurl and highlight the important elements, as well as set the eCIS, and rmp links.*/
function getDescription(course_info) {
toggleDescriptionLoading(true);
$.ajax({
url: course_info["individual"],
success: function (response) {
if (response) {
let response_node = htmlToNode(response);
description_lines = response_node.find('#details > p').toArray().map(x => $(x).text());
displayDescription(buildFormattedDescription(description_lines));
let first_name = extractFirstName(response_node);
updateLinks(course_info, first_name);
} else {
displayDescription(error_message);
}
}
});
}
function loadNextPages(num_pages) {
if (num_pages === undefined) num_pages = 1;
if (num_pages == 0) return;
chrome.runtime.sendMessage({
command: "getOptionsValue",
key: "loadAll",
}, function (response) {
if(response.value){
let link = next.prop('href');
if (done_loading && next && link) {
toggleLoadingPage(true);
$.get(link, function (response) {
if (response) {
var next_page = htmlToNode(response);
var current = $('tbody');
var old_length = $('tbody tr').length;
var last = current.find('.course_header>h2:last').text();
next = next_page.find("#next_nav_link");
toggleLoadingPage(false);
var new_rows = [];
next_page.find('tbody>tr').each(function () {
let has_course_header = $(this).find('td').hasClass("course_header");
if (!(has_course_header && $(this).has('th').length == 0))
$(this).append(Template.Main.extension_button());
if (!(has_course_header && last == $(this).find('td').text()))
new_rows.push($(this));
});
current.append(new_rows);
updateListConflictHighlighting(old_length + 1)
}
loadNextPages(num_pages-1);
}).fail(function () {
toggleLoadingPage(false);
$("#retrylabel").css('display', 'inline-block');
$('#retry').css('display', 'inline-block');
});
}
}
});
}
$("#myModal").on('click', '#saveCourse', function () {
setTimeout(function () {
saveCourse();
}, 0);
});
$("#Syllabi").click(function () {
setTimeout(function () {
window.open(curr_course["links"]["syllabi"]);
}, Timing.button_delay);
});
$("#rateMyProf").click(function () {
setTimeout(function () {
window.open(curr_course["links"]["rate_my_prof"]);
}, Timing.button_delay);
});
$("#eCIS").click(function () {
setTimeout(function () {
window.open(curr_course["links"]["ecis"]);
}, Timing.button_delay);
});
$("#textbook").click(function () {
setTimeout(function () {
window.open(curr_course["links"]["textbook"]);
}, Timing.button_delay);
});
$("#semesters").on('change', function () {
let sem = $(this).val();
sem = sem == "Aggregate" ? undefined : sem;
getDistribution(curr_course, sem);
});
$("#retry").click(function () {
$("#retrylabel").hide();
$(this).hide();
loadNextPages();
});
function toggleLoadingPage(loading) {
if (loading) {
done_loading = false;
$('#loader').css('display', 'inline-block');
$("#nextlabel").css('display', 'inline-block');
} else {
done_loading = true;
$('#loader').hide();
$("#nextlabel").hide();
}
}
function toggleChartLoading(loading) {
if (loading) {
$('#chartload').css('display', 'inline-block');
$("#chart").hide();
} else {
$('#chartload').hide();
$("#chart").show();
}
}
function toggleDescriptionLoading(loading) {
if (loading) {
$('#descload').css('display', 'inline-block');
} else {
$('#descload').hide();
}
}
function toggleSnackbar() {
setTimeout(function () {
$("#snackbar").attr("class", "show");
}, 200);
setTimeout(function () {
$("#snackbar").attr("class", "");
}, 3000);
}
function allowClosing() {
$('.close').click(function () {
close();
});
$('#myModal').click(function (event) {
if (event.target.id == 'myModal') {
close();
}
});
}
function close() {
$("#myModal").fadeOut(Timing.fade_time);
$("#snackbar").attr("class", "");
}
/*Listen for update mssage coming from popup or calendar or other course catalog pages*/
chrome.runtime.onMessage.addListener(
function (request, sender, sendResponse) {
if (request.command == "updateCourseList") {
updateListConflictHighlighting(0);
}
}
);
$(document).keydown(function (e) {
/*Close Modal when hit escape*/
if (e.keyCode == 27) {
close();
} else if (e.keyCode == 13 && $('#myModal').is(':visible')) {
saveCourse();
}
});
$(window).scroll(function () {
if ($(document).height() <= $(window).scrollTop() + $(window).height() + 150)
loadNextPages();
});
$(window).on('load', function () {
loadNextPages(3);
});

View File

@@ -1,164 +0,0 @@
var waitlist;
var sem;
$(function () {
waitlist = !(window.location.href.includes('https://utdirect.utexas.edu/registration/classlist.WBX'));
sem = waitlist ? $('[name="s_ccyys"]').val() : $("option[selected='selected']").val();
if (waitlist) {
$("[href='#top']").before(Template.Import.import_button());
$("[name='wl_see_my_waitlists']").before(Template.Import.store_waitlist_message());
$("[name='wl_see_my_waitlists']").after(Template.Import.waitlist_import_button());
extractWaitlistInfo();
} else {
$("table").after(Template.Import.import_button());
}
$("#import").prepend("<div id='snackbar'>import snackbar..</div>");
$("#import").click(function () {
search_nodes = waitlist ? $(".tbg").last().find(".tbon>td:first-child") : $("tr>td:first-child");
$(search_nodes).each(function () {
importCourse($(this), true);
})
importButtonAnimation($(this));
});
$("#import_waitlist").click(function () {
search_nodes = $("tr.tb span:first-child");
$(search_nodes).each(function () {
importCourse($(this), false);
})
importButtonAnimation($(this));
});
});
function extractWaitlistInfo(){
let class_boxes = $("[name='wl_see_my_waitlists']>table");
let waitlist_info = [];
$(class_boxes).each(function(){
let data = $(this).find('tr.tb span');
let unique_num = $(data[0]).text().trim();
let class_name = $(data[1]).text().trim().split('\n').filter(part => part.trim() != '').map(part => part.trim()).join(' ');
let waitlist_size = $(this).find('tr.tbon:eq(2) td:eq(1)').text().trim().split(' of ')[1];
waitlist_info.push({
"id": unique_num,
"class": class_name,
"wait": waitlist_size,
"time": moment().format('DD-MM-YYYY HH:mm:ss')
});
});
console.log(waitlist_info);
return waitlist_info;
}
function importButtonAnimation(button) {
let is_waitlisted_button = $(button).attr('id') == "import_waitlist";
let return_text = is_waitlisted_button ? Text.waitlist_button_text_default : Text.button_text_default;
$(button).text(Text.button_success).css("background-color", Colors.open);
setTimeout(function () {
$(button).html(return_text).css('background-color', Colors.waitlisted);
}, 1000);
}
function importCourse(unique_node, force) {
let unique = $(unique_node).text().replace(/\s/g, '').substring(0,5);
link = `https://utdirect.utexas.edu/apps/registrar/course_schedule/${sem}/${unique}/`;
buildAddCourse(link, force)
}
function buildAddCourse(link, force) {
$.get(link, function (response) {
if (response) {
let simp_course = buildSimplifiedCourseObject(response, link, force);
chrome.runtime.sendMessage({
command: "courseStorage",
course: simp_course,
action: "add"
}, function () {
chrome.runtime.sendMessage({
command: "updateCourseList"
});
});
}
})
}
function buildSimplifiedCourseObject(response, link, force) {
let imported_course = getCourseObject(htmlToNode(response), link);
let {
full_name,
unique,
prof_name,
individual,
status,
register
} = curr_course;
let dtarr = getDayTimeArray(undefined, curr_course);
if(force === true) {
status = "open" //forces the green status for courses a user is already registered for
}
return new Course(full_name, unique, prof_name, dtarr, status, individual, register);
}
/*For a row, get all the course information and add the date-time-lines*/
function getCourseObject(response_node, individual) {
let course_name = $(response_node).find("#details h2").text();
let course_row = $(response_node).find('table');
curr_course = buildBasicCourseInfo(course_row, course_name, individual);
}
function buildBasicCourseInfo(row, course_name, individual) {
let {
name,
department,
number
} = separateCourseNameParts(course_name);
let instructor_text = $(row).find('td[data-th="Instructor"]').text();
let has_initial = instructor_text.indexOf(',') > 0;
course_info = {
"full_name": course_name,
"name": name,
"department": department,
"number": number,
"individual": individual ? individual : $(row).find('td[data-th="Unique"] a').prop('href'),
"register": $(row).find('td[data-th="Add"] a').prop('href'),
"unique": $(row).find('td[data-th="Unique"]').text(),
"status": $(row).find('td[data-th="Status"]').text(),
"prof_name": instructor_text ? has_initial ? capitalizeString(instructor_text.split(', ')[0]) : capitalizeString(instructor_text) : "Undecided",
"initial": instructor_text && has_initial ? instructor_text.split(', ')[1].substring(0, 1) : "",
"time_data": {
"days": $(row).find('td[data-th="Days"]>span').toArray().map(x => $(x).text().trim()),
"times": $(row).find('td[data-th="Hour"]>span').toArray().map(x => $(x).text().trim()),
"places": $(row).find('td[data-th="Room"]>span').toArray().map(x => $(x).text().trim())
},
"links": {}
}
return course_info;
}
/* For a row, get the date-time-array for checking conflicts*/
function getDayTimeArray(row, course_info) {
var day_time_array = []
let days = course_info ? course_info["time_data"]["days"] : $(row).find('td[data-th="Days"]>span').toArray().map(x => $(x).text().trim());
let times = course_info ? course_info["time_data"]["times"] : $(row).find('td[data-th="Hour"]>span').toArray().map(x => $(x).text().trim());
let places = course_info ? course_info["time_data"]["places"] : $(row).find('td[data-th="Room"]>span').toArray().map(x => $(x).text().trim());
for (var i = 0; i < days.length; i++) {
let date = days[i];
let time = times[i];
let place = places[i];
for (var j = 0; j < date.length; j++) {
let letter = date.charAt(j);
if (letter == "T" && j < date.length - 1 && date.charAt(j + 1) == "H") {
day_time_array.push(["TH", convertTime(time), place]);
} else {
if (letter != "H")
day_time_array.push([letter, convertTime(time), place]);
}
}
}
return day_time_array;
}

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

231
js/lib/ics.min.js vendored
View File

@@ -1,231 +0,0 @@
/*! ics.js Wed Aug 20 2014 17:23:02 */
var saveAs = saveAs || function (e) {
"use strict";
if (typeof e === "undefined" || typeof navigator !== "undefined" && /MSIE [1-9]\./.test(navigator.userAgent)) {
return
}
var t = e.document,
n = function () {
return e.URL || e.webkitURL || e
},
r = t.createElementNS("http://www.w3.org/1999/xhtml", "a"),
o = "download" in r,
a = function (e) {
var t = new MouseEvent("click");
e.dispatchEvent(t)
},
i = /constructor/i.test(e.HTMLElement) || e.safari,
f = /CriOS\/[\d]+/.test(navigator.userAgent),
u = function (t) {
(e.setImmediate || e.setTimeout)(function () {
throw t
}, 0)
},
s = "application/octet-stream",
d = 1e3 * 40,
c = function (e) {
var t = function () {
if (typeof e === "string") {
n().revokeObjectURL(e)
} else {
e.remove()
}
};
setTimeout(t, d)
},
l = function (e, t, n) {
t = [].concat(t);
var r = t.length;
while (r--) {
var o = e["on" + t[r]];
if (typeof o === "function") {
try {
o.call(e, n || e)
} catch (a) {
u(a)
}
}
}
},
p = function (e) {
if (/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(e.type)) {
return new Blob([String.fromCharCode(65279), e], {
type: e.type
})
}
return e
},
v = function (t, u, d) {
if (!d) {
t = p(t)
}
var v = this,
w = t.type,
m = w === s,
y, h = function () {
l(v, "writestart progress write writeend".split(" "))
},
S = function () {
if ((f || m && i) && e.FileReader) {
var r = new FileReader;
r.onloadend = function () {
var t = f ? r.result : r.result.replace(/^data:[^;]*;/, "data:attachment/file;");
var n = e.open(t, "_blank");
if (!n) e.location.href = t;
t = undefined;
v.readyState = v.DONE;
h()
};
r.readAsDataURL(t);
v.readyState = v.INIT;
return
}
if (!y) {
y = n().createObjectURL(t)
}
if (m) {
e.location.href = y
} else {
var o = e.open(y, "_blank");
if (!o) {
e.location.href = y
}
}
v.readyState = v.DONE;
h();
c(y)
};
v.readyState = v.INIT;
if (o) {
y = n().createObjectURL(t);
setTimeout(function () {
r.href = y;
r.download = u;
a(r);
h();
c(y);
v.readyState = v.DONE
});
return
}
S()
},
w = v.prototype,
m = function (e, t, n) {
return new v(e, t || e.name || "download", n)
};
if (typeof navigator !== "undefined" && navigator.msSaveOrOpenBlob) {
return function (e, t, n) {
t = t || e.name || "download";
if (!n) {
e = p(e)
}
return navigator.msSaveOrOpenBlob(e, t)
}
}
w.abort = function () {};
w.readyState = w.INIT = 0;
w.WRITING = 1;
w.DONE = 2;
w.error = w.onwritestart = w.onprogress = w.onwrite = w.onabort = w.onerror = w.onwriteend = null;
return m
}(typeof self !== "undefined" && self || typeof window !== "undefined" && window || this.content);
if (typeof module !== "undefined" && module.exports) {
module.exports.saveAs = saveAs
} else if (typeof define !== "undefined" && define !== null && define.amd !== null) {
define("FileSaver.js", function () {
return saveAs
})
}
var ics = function (e, t) {
"use strict"; {
if (!(navigator.userAgent.indexOf("MSIE") > -1 && -1 == navigator.userAgent.indexOf("MSIE 10"))) {
void 0 === e && (e = "default"), void 0 === t && (t = "Calendar");
var r = -1 !== navigator.appVersion.indexOf("Win") ? "\r\n" : "\n",
n = [],
i = ["BEGIN:VCALENDAR", "PRODID:" + t, "VERSION:2.0"].join(r),
o = r + "END:VCALENDAR",
a = ["SU", "MO", "TU", "WE", "TH", "FR", "SA"];
return {
events: function () {
return n
},
calendar: function () {
return i + r + n.join(r) + o
},
addEvent: function (t, i, o, l, u, s) {
if (void 0 === t || void 0 === i || void 0 === o || void 0 === l || void 0 === u) return !1;
if (s && !s.rrule) {
if ("YEARLY" !== s.freq && "MONTHLY" !== s.freq && "WEEKLY" !== s.freq && "DAILY" !== s.freq) throw "Recurrence rrule frequency must be provided and be one of the following: 'YEARLY', 'MONTHLY', 'WEEKLY', or 'DAILY'";
if (s.until && isNaN(Date.parse(s.until))) throw "Recurrence rrule 'until' must be a valid date string";
if (s.interval && isNaN(parseInt(s.interval))) throw "Recurrence rrule 'interval' must be an integer";
if (s.count && isNaN(parseInt(s.count))) throw "Recurrence rrule 'count' must be an integer";
if (void 0 !== s.byday) {
if ("[object Array]" !== Object.prototype.toString.call(s.byday)) throw "Recurrence rrule 'byday' must be an array";
if (s.byday.length > 7) throw "Recurrence rrule 'byday' array must not be longer than the 7 days in a week";
s.byday = s.byday.filter(function (e, t) {
return s.byday.indexOf(e) == t
});
for (var c in s.byday)
if (a.indexOf(s.byday[c]) < 0) throw "Recurrence rrule 'byday' values must include only the following: 'SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA'"
}
}
var g = new Date(l),
d = new Date(u),
f = new Date,
S = ("0000" + g.getFullYear().toString()).slice(-4),
E = ("00" + (g.getMonth() + 1).toString()).slice(-2),
v = ("00" + g.getDate().toString()).slice(-2),
y = ("00" + g.getHours().toString()).slice(-2),
A = ("00" + g.getMinutes().toString()).slice(-2),
T = ("00" + g.getSeconds().toString()).slice(-2),
b = ("0000" + d.getFullYear().toString()).slice(-4),
D = ("00" + (d.getMonth() + 1).toString()).slice(-2),
N = ("00" + d.getDate().toString()).slice(-2),
h = ("00" + d.getHours().toString()).slice(-2),
I = ("00" + d.getMinutes().toString()).slice(-2),
R = ("00" + d.getMinutes().toString()).slice(-2),
M = ("0000" + f.getFullYear().toString()).slice(-4),
w = ("00" + (f.getMonth() + 1).toString()).slice(-2),
L = ("00" + f.getDate().toString()).slice(-2),
O = ("00" + f.getHours().toString()).slice(-2),
p = ("00" + f.getMinutes().toString()).slice(-2),
Y = ("00" + f.getMinutes().toString()).slice(-2),
U = "",
V = "";
y + A + T + h + I + R != 0 && (U = "T" + y + A + T, V = "T" + h + I + R);
var B, C = S + E + v + U,
j = b + D + N + V,
m = M + w + L + ("T" + O + p + Y);
if (s)
if (s.rrule) B = s.rrule;
else {
if (B = "rrule:FREQ=" + s.freq, s.until) {
var x = new Date(Date.parse(s.until)).toISOString();
B += ";UNTIL=" + x.substring(0, x.length - 13).replace(/[-]/g, "") + "000000Z"
}
s.interval && (B += ";INTERVAL=" + s.interval), s.count && (B += ";COUNT=" + s.count), s.byday && s.byday.length > 0 && (B += ";BYDAY=" + s.byday.join(","))
}(new Date).toISOString();
var H = ["BEGIN:VEVENT", "UID:" + n.length + "@" + e, "CLASS:PUBLIC", "DESCRIPTION:" + i, "DTSTAMP;VALUE=DATE-TIME:" + m, "DTSTART;VALUE=DATE-TIME:" + C, "DTEND;VALUE=DATE-TIME:" + j, "LOCATION:" + o, "SUMMARY;LANGUAGE=en-us:" + t, "TRANSP:TRANSPARENT", "END:VEVENT"];
return B && H.splice(4, 0, B), H = H.join(r), n.push(H), H
},
download: function (e, t) {
if (n.length < 1) return !1;
t = void 0 !== t ? t : ".ics", e = void 0 !== e ? e : "calendar";
var a, l = i + r + n.join(r) + o;
if (-1 === navigator.userAgent.indexOf("MSIE 10")) a = new Blob([l]);
else {
var u = new BlobBuilder;
u.append(l), a = u.getBlob("text/x-vCalendar;charset=" + document.characterSet)
}
return saveAs(a, e + t), l
},
build: function () {
return !(n.length < 1) && i + r + n.join(r) + o
}
}
}
console.log("Unsupported Browser")
}
};

File diff suppressed because one or more lines are too long

View File

@@ -1 +0,0 @@
(function($){"use strict";var combinators=[" ",">","+","~"];var fraternisers=["+","~"];var complexTypes=["ATTR","PSEUDO","ID","CLASS"];function grok(msobserver){if(!$.find.tokenize){msobserver.isCombinatorial=true;msobserver.isFraternal=true;msobserver.isComplex=true;return}msobserver.isCombinatorial=false;msobserver.isFraternal=false;msobserver.isComplex=false;var token=$.find.tokenize(msobserver.selector);for(var i=0;i<token.length;i++){for(var j=0;j<token[i].length;j++){if(combinators.indexOf(token[i][j].type)!=-1)msobserver.isCombinatorial=true;if(fraternisers.indexOf(token[i][j].type)!=-1)msobserver.isFraternal=true;if(complexTypes.indexOf(token[i][j].type)!=-1)msobserver.isComplex=true}}}var MutationSelectorObserver=function(selector,callback,options){this.selector=selector.trim();this.callback=callback;this.options=options;grok(this)};var msobservers=[];msobservers.initialize=function(selector,callback,options){var seen=[];var callbackOnce=function(){if(seen.indexOf(this)==-1){seen.push(this);$(this).each(callback)}};$(options.target).find(selector).each(callbackOnce);var msobserver=new MutationSelectorObserver(selector,callbackOnce,options);this.push(msobserver);var observer=new MutationObserver(function(mutations){var matches=[];for(var m=0;m<mutations.length;m++){if(mutations[m].type=="attributes"){if(mutations[m].target.matches(msobserver.selector))matches.push(mutations[m].target);if(msobserver.isFraternal)matches.push.apply(matches,mutations[m].target.parentElement.querySelectorAll(msobserver.selector));else matches.push.apply(matches,mutations[m].target.querySelectorAll(msobserver.selector))}if(mutations[m].type=="childList"){for(var n=0;n<mutations[m].addedNodes.length;n++){if(!(mutations[m].addedNodes[n]instanceof Element))continue;if(mutations[m].addedNodes[n].matches(msobserver.selector))matches.push(mutations[m].addedNodes[n]);if(msobserver.isFraternal)matches.push.apply(matches,mutations[m].addedNodes[n].parentElement.querySelectorAll(msobserver.selector));else matches.push.apply(matches,mutations[m].addedNodes[n].querySelectorAll(msobserver.selector))}}}for(var i=0;i<matches.length;i++)$(matches[i]).each(msobserver.callback)});var defaultObeserverOpts={childList:true,subtree:true,attributes:msobserver.isComplex};observer.observe(options.target,options.observer||defaultObeserverOpts);return observer};$.fn.initialize=function(callback,options){return msobservers.initialize(this.selector,callback,$.extend({},$.initialize.defaults,options))};$.initialize=function(selector,callback,options){return msobservers.initialize(selector,callback,$.extend({},$.initialize.defaults,options))};$.initialize.defaults={target:document.documentElement,observer:null}})(jQuery);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,60 +0,0 @@
var manifestData = chrome.runtime.getManifest();
$("#version").text(manifestData.version);
chrome.storage.sync.get("options", function (data) {
if (data.options) {
console.log(data.options);
Object.keys(data.options).forEach(key => {
let enabled = data.options[key];
$("#options_container").append(Template.Options.options_row(key, enabled));
});
}
});
$("body").on("click", "button", function () {
let key = $(this).attr("id");
let old_status = $(this).val() === "true";
let new_status = !old_status;
chrome.runtime.sendMessage(
{
command: "setOptionsValue",
key: key,
value: new_status,
},
function (response) {
console.log(response.value);
toggle(key, response.value);
updateAllTabsCourseList();
}
);
});
$.get("https://api.github.com/repos/sghsri/UT-Registration-Plus/stats/contributors", data => {
data = data.sort((a, b) => b.total - a.total);
console.log("data", data);
for (var contributorData of data) {
$.get(`https://api.github.com/users/${contributorData.author.login}`, userData => {
let fullData = { ...contributorData, ...userData };
let { login, avatar_url, html_url, name } = fullData;
if(name){
$("#contributor-list").append(Template.Options.contributor_card(login, name, avatar_url, html_url));
}
else{
$("#contributor-list").append(Template.Options.contributor_card("", login, avatar_url, html_url));
}
});
}
});
$("body").on("click", ".contributor-card", function () {
console.log("hello world");
window.open($(this).data("url"), "_blank");
});
function toggle(key, value) {
let button_text = value ? "Turn Off" : "Turn On";
let button_color = value ? Colors.closed : Colors.open;
$(`#${key}`).text(button_text);
$(`#${key}`).css("background", button_color);
$(`#${key}`).val(value);
}

View File

@@ -1,437 +0,0 @@
var courses;
setCourseList();
getSemesters();
getDepartments();
var can_remove = true;
function setCourseList() {
$("#courseList").empty();
chrome.storage.sync.get("savedCourses", function (data) {
updateConflicts();
courses = data.savedCourses;
handleEmpty();
let hours = 0;
// build and append the course list element
for (var i = 0; i < courses.length; i++) {
let { coursename, unique, profname, status, datetimearr } = courses[i];
profname = capitalizeString(profname);
let line = buildTimeLines(datetimearr);
let list_tile_color = getStatusColor(status);
let list_sub_color = getStatusColor(status, true);
let { department, number } = separateCourseNameParts(coursename);
let class_length = parseInt(number.charAt(0));
let multi_semester_code = number.slice(-1);
if (["A", "B"].includes(multi_semester_code)) {
hours += Math.floor(class_length / 2);
} else if (["X", "Y", "Z"].includes(multi_semester_code)) {
hours += Math.floor(class_length / 3);
} else {
hours += class_length;
}
let list_html = Template.Popup.list_item(i, list_tile_color, unique, department, number, profname, list_sub_color, line);
$("#courseList").append(list_html);
}
$("#meta-metric").text(hours);
});
}
/* convert from the dtarr and maek the time lines*/
function buildTimeLines(datetimearr) {
let lines = convertDateTimeArrToLine(datetimearr);
let output = "";
if (lines.length == 0) {
output = "<span style='font-size:medium;'>This class has no meeting times.</span>";
} else {
for (let i = 0; i < lines.length; i++) {
let line = lines[i];
output += Template.Popup.line(line);
}
}
return output;
}
/* Update the conflict messages */
function updateConflicts() {
chrome.runtime.sendMessage(
{
command: "checkConflicts",
},
function (response) {
console.log("updateConflicts -> response", response);
if (response.isConflict) {
var between = response.between;
let conflict_message = "";
for (var i = 0; i < between.length; i++) {
let courseA = between[i][0];
let courseB = between[i][1];
conflict_message += `CONFLICT: ${formatShortenedCourseName(courseA)} and ${formatShortenedCourseName(courseB)}`;
if (i != between.length - 1) conflict_message += "<br>";
}
$(Template.Popup.conflict_message(conflict_message)).prependTo("#courseList").hide().fadeIn(200);
}
}
);
}
/* prettify the name for the conflict messages*/
function formatShortenedCourseName(course) {
let { number, department } = separateCourseNameParts(course.coursename);
return `${department} ${number} (${course.unique})`;
}
$(document).click(function (event) {
$target = $(event.target);
// If we're not clicking on search button or search popup, and popup is visible, hide it
if (!$target.closest("#search").length && !$target.closest("#search-popup").length && $("#search-popup").is(":visible")) {
hideSearchPopup();
}
// If we're not clicking on import/export button or imp/exp popup, and popup is visible, hide it
if (!$target.closest("#impexp").length && !$target.closest("#import-export-popup").length && $("#import-export-popup").is(":visible")) {
hideImportExportPopup();
}
});
$("#clear").click(function () {
chrome.storage.sync.set({
savedCourses: [],
});
$("#courseList").empty();
updateAllTabsCourseList();
showEmpty();
});
$("#RIS").click(function () {
chrome.tabs.create({
url: "https://utdirect.utexas.edu/registrar/ris.WBX",
});
});
$("#calendar").click(function () {
chrome.tabs.create({
url: "calendar.html",
});
});
$("#impexp").click(function () {
if ($("#impexp>i").text() == "close") {
hideImportExportPopup();
} else {
if ($("#search>i").text() == "close") {
hideSearchPopup();
}
showImportExportPopup();
}
});
$("#search").click(function () {
if ($("#search>i").text() == "close") {
hideSearchPopup();
} else {
if ($("#impexp>i").text() == "close") {
hideImportExportPopup();
}
showSearchPopup();
}
});
$("#import-class").click(function () {
$("#import_input").click();
console.log("back to improting");
});
function isImportedValid(imported_courses) {
return imported_courses && imported_courses.length && (imported_courses.length == 0 || validateCourses(imported_courses));
}
$("#import_input").change(function (e) {
console.log("hello");
var files = e.target.files;
var reader = new FileReader();
reader.onload = function () {
try {
var imported_courses = JSON.parse(this.result);
if (isImportedValid(imported_courses)) {
chrome.storage.sync.set({
savedCourses: imported_courses,
});
updateAllTabsCourseList();
setCourseList();
hideImportExportPopup();
$("#import_input").val("");
} else {
Alert("There was an error.");
}
} catch (err) {
console.log(err);
}
};
reader.readAsText(files[0]);
});
function exportCourses(url) {
var exportlink = document.createElement("a");
exportlink.setAttribute("href", url);
exportlink.setAttribute("download", "my_courses.json");
exportlink.click();
}
function createBlob(export_courses) {
return new Blob([JSON.stringify(export_courses, null, 4)], {
type: "octet/stream",
});
}
$("#export-class").click(function () {
chrome.storage.sync.get("savedCourses", function (data) {
let export_courses = data.savedCourses;
if (export_courses.length > 0) {
let url = window.URL.createObjectURL(createBlob(export_courses));
exportCourses(url);
} else {
alert("No Saved Courses to Export.");
}
hideImportExportPopup();
});
});
function openSearch(semester, department, level, courseCode) {
var link = "";
if (courseCode) {
link = `https://utdirect.utexas.edu/apps/registrar/course_schedule/${semester}/results/?search_type_main=COURSE&fos_cn=${department}&course_number=${courseCode}`;
} else {
link = `https://utdirect.utexas.edu/apps/registrar/course_schedule/${semester}/results/?fos_fl=${department}&level=${level}&search_type_main=FIELD`;
}
chrome.tabs.create({ url: link });
}
$("#search-class").click(() => {
let semester = $("#semesters").find(":selected").val();
let department = $("#department").find(":selected").val();
let level = $("#level").find(":selected").val();
let courseCode = $("#courseCode").val();
openSearch(semester, department, level, courseCode);
});
$("#options_button").click(function () {
chrome.tabs.create({
url: "options.html",
});
});
$("#courseList")
.on("mouseover", ".copy_button", function () {
$(this).addClass("shadow");
})
.on("mouseleave", ".copy_button", function () {
$(this).removeClass("shadow");
});
$("#courseList").on("click", ".copy_button", function (e) {
e.stopPropagation();
copyButtonAnimation($(this));
let unique = $(this).val();
copyUnique(unique);
});
function copyUnique(unique) {
var temp = $("<input>");
$("body").append(temp);
temp.val(unique).select();
document.execCommand("copy");
temp.remove();
}
$("#courseList").on("click", "li", function () {
let clicked_item = $(this).closest("li");
let curr_course = courses[$(clicked_item).attr("id")];
handleMoreInfo(clicked_item, curr_course);
handleRegister(clicked_item, curr_course);
handleRemove(clicked_item, curr_course);
toggleTimeDropdown(clicked_item);
});
function handleRegister(clicked_item, curr_course) {
let { status, registerlink } = curr_course;
let register_button = $(clicked_item).find("#register");
let can_not_register = canNotRegister(status, registerlink);
let register_text = can_not_register ? "Can't Register" : status.includes("waitlisted") ? "Join Waitlist" : "Register";
let register_color = can_not_register ? Colors.closed : status.includes("waitlisted") ? Colors.waitlisted : Colors.open;
if (!status) {
register_text = "No Status";
register_color = Colors.no_status;
}
$(register_button).text(register_text).css("background-color", register_color);
if (!can_not_register) {
$(register_button).click(function () {
setCurrentTabUrl(registerlink);
});
}
}
function handleRemove(clicked_item, curr_course) {
let list = $(clicked_item).closest("ul");
$(clicked_item)
.find("#listRemove")
.click(function () {
if (can_remove) {
can_remove = false;
$(list)
.find("#conflict")
.fadeOut(300, function () {
$(clicked_item).remove();
});
subtractHours(curr_course);
chrome.runtime.sendMessage(
{
command: "courseStorage",
course: curr_course,
action: "remove",
},
() => {
$(clicked_item).fadeOut(200);
if ($(list).children(":visible").length === 1) showEmpty();
can_remove = true;
updateConflicts();
updateAllTabsCourseList();
}
);
}
});
}
function subtractHours(curr_course) {
let curr_total_hours = parseInt($("#meta-metric").text());
let curr_course_number = separateCourseNameParts(curr_course.coursename).number;
let class_length = parseInt(curr_course_number.charAt(0));
let multi_semester_code = curr_course_number.slice(-1);
if (["A", "B"].includes(multi_semester_code)) {
$("#meta-metric").text(curr_total_hours - Math.floor(class_length / 2));
} else if (["X", "Y", "Z"].includes(multi_semester_code)) {
$("#meta-metric").text(curr_total_hours - Math.floor(class_length / 3));
} else {
$("#meta-metric").text(curr_total_hours - class_length);
}
}
function handleMoreInfo(clicked_item, curr_course) {
$(clicked_item)
.find("#listMoreInfo")
.click(function () {
openMoreInfoWithOpenModal(curr_course.link);
});
}
function handleEmpty() {
if (courses.length != 0) {
$("#empty").hide();
$("#courseList").show();
} else {
showEmpty();
}
}
function copyButtonAnimation(copy_button) {
$(copy_button).find("i").text("check");
$(copy_button).stop(true, false).removeAttr("style").removeClass("shadow", {
duration: 200,
});
$(copy_button)
.find("i")
.delay(400)
.queue(function (n) {
$(this).text("content_copy");
$(this).parent().removeClass("shadow");
if ($(this).parent().is(":hover")) {
$(this).parent().addClass("shadow");
}
n();
});
}
function toggleTimeDropdown(clicked_item) {
let more_info_button = $(clicked_item).find("#moreInfo");
let arrow = $(clicked_item).find("#arrow");
if ($(more_info_button).is(":hidden")) {
$(more_info_button).fadeIn(200);
$(arrow).css("transform", "rotate(90deg)");
} else {
$(more_info_button).fadeOut(200);
$(arrow).css("transform", "");
}
}
function showEmpty() {
$("#courseList").hide();
$("#empty").fadeIn(200);
$("#main").html(Text.emptyText());
$("#meta-metric").text("0");
}
function hideSearchPopup() {
$("#search>i").text("search");
$("#semcon").hide();
$("#depcon").hide();
$("#semesters").hide();
$("#levcon").hide();
$("#search-popup").addClass("hide");
}
function showSearchPopup() {
$("#search>i").text("close");
$("#class_id_input").show();
$("#semesters").show();
$("#semcon").show();
$("#depcon").show();
$("#levcon").show();
$("#search-popup").removeClass("hide");
}
function hideImportExportPopup() {
$("#import-export-popup").addClass("hide");
$("#impexp>i").text("import_export");
}
function showImportExportPopup() {
$("#impexp>i").text("close");
$("#import-export-popup").removeClass("hide");
}
function getSemesters() {
chrome.runtime.sendMessage(
{
command: "currentSemesters",
},
function (response) {
let { semesters } = response;
let semester_names = Object.keys(semesters);
for (let i = 0; i < semester_names.length; i++) {
let name = semester_names[i];
$("#semesters").append(`<option value='${semesters[name]}'>${name}</option>`);
}
}
);
}
function getDepartments() {
chrome.runtime.sendMessage(
{
command: "currentDepartments",
},
function (response) {
let { departments } = response;
console.log(departments);
for (let i = 0; i < departments.length; i++) {
let abv = departments[i];
$("#department").append(`<option value='${abv}'>${abv}</option>`);
}
// $("#department").val('C S');
}
);
}

View File

@@ -1,242 +0,0 @@
let semester_code = "";
curr_course = {}
chrome.runtime.sendMessage({
command: "currentSemesters"
}, function(response){
let semester_text = $('.row:contains(Semester)').find('span').text();
let key = semester_text.split(' ').reverse().join(' ');
semester_code = response.semesters[key];
});
$.initialize("table.section-detail-grid", function () {
$(this).find('thead>tr').append('<th> Plus</th')
$(this).find('tbody>tr').each(function () {
$(this).append(Template.Main.extension_button());
})
});
$("body").prepend(Template.UTPlanner.modal());
$("body").on('click', '#distButton', function () {
var row = $(this).closest('tr');
$('.modal-content').stop().animate({ scrollTop: 0 }, 500);
$(this).blur();
getCourseInfo(row)
});
function getCourseInfo(row) {
let rowdata = $(row).find('td').slice(3).toArray().map(x => $(x).text().trim());
let [uniquenum, department, coursenum, coursename, profname, notes, rawtime] = rowdata
let profinit = ""
if (profname !== undefined && profname != "Staff") {
profinit = profname.split(',')[1].trim();
profname = profname.split(',')[0].trim();
}
let times = rawtime.split('\n').map(x => x.trim());
var course_data = {
"unique": uniquenum,
"department": department,
"number": coursenum,
"name": coursename,
"prof_name": profname,
"initial": profinit,
"notes": notes,
"individual": `https://utdirect.utexas.edu/apps/registrar/course_schedule/${semester_code}/${uniquenum}/`,
"times": times,
}
curr_course = buildCourseLinks(course_data);
getDistribution(course_data);
var modal = document.getElementById('myModal');
window.onclick = function (event) {
if (event.target == modal) {
close();
}
}
}
function buildCourseLinks(course_info) {
console.log(semester_code);
let {
department,
number,
unique,
prof_name
} = course_info
links = {
"textbook": `https://www.universitycoop.com/adoption-search-results?sn=${semester_code}__${department}__${number}__${unique}`,
"syllabi": `https://utdirect.utexas.edu/apps/student/coursedocs/nlogon/?year=&semester=&department=${department}&course_number=${number}&course_title=&unique=&instructor_first=&instructor_last=${prof_name}&course_type=In+Residence&search=Search`,
}
course_info["links"] = links;
return course_info;
}
function badData(course_data, res) {
return typeof res == 'undefined' || course_data["prof_name"] == "Staff";
}
$("#semesters").on('change', function () {
var sem = $(this).val();
sem = sem == "Aggregate" ? undefined : sem;
getDistribution(curr_course, sem);
});
$("#Syllabi").click(function () {
setTimeout(function () {
window.open(curr_course["links"]["syllabi"]);
}, Timing.button_delay);
});
$("#textbook").click(function () {
setTimeout(function () {
window.open(curr_course["links"]["textbook"]);
}, Timing.button_delay);
});
$("#moreInfo").click(function () {
openMoreInfoWithOpenModal(curr_course["individual"]);
});
function toggleChartLoading(loading) {
if (loading) {
$('#chartload').css('display', 'inline-block');
$("#chart").hide();
} else {
$('#chartload').hide();
$("#chart").show();
}
}
function openDialog(course_data, res) {
console.log(course_data);
$("#title").text(buildTitle(course_data))
$("#topbuttons").before(buildTimeTitle(course_data["times"]));
$("#profname").text(buildProfTitle(course_data));
$("#myModal").fadeIn(Timing.fade_time);
buildSemestersDropdown(course_data, res)
var data = []
if (!badData(course_data, res))
data = res.values[0];
setChart(data);
allowClosing();
}
function buildProfTitle(course_data) {
const {
initial,
prof_name
} = course_data;
return `with ${initial?initial+". ":""}${prof_name}`;
}
function buildSemestersDropdown(course_data, res) {
$("#semesters").empty();
if (badData(course_data, res)) {
$("#semesters").append("<option>No Data</option>")
} else {
var semesters = res.values[0][18].split(",");
semesters.sort(semesterSort);
semesters.reverse().unshift('Aggregate');
var sems = [];
for (var i = 0; i < semesters.length; i++) {
sems.push($(`<option value="${semesters[i]}">${semesters[i]}</option>`));
}
$("#semesters").append(sems);
}
}
/*Query the grades database*/
function getDistribution(course_data, sem) {
toggleChartLoading(true);
let query = buildQuery(course_data, sem);
chrome.runtime.sendMessage({
command: "gradesQuery",
query: query
}, function (response) {
var res = response.data;
if (!sem) {
openDialog(course_data, res);
} else {
var data = badData(course_data, res) ? [] : res.values[0];
setChart(data);
}
});
}
function buildTitle(course_data) {
return `${course_data["name"]} (${course_data["department"]} ${course_data["number"]})`
}
function buildTimeTitle(times) {
$("h2.dateTimePlace").remove();
var lines = []
for (var i = 0; i < times.length; i++) {
date = times[i].substring(0, times[i].indexOf(' ')).toUpperCase();
time = times[i].substring(times[i].indexOf(' ') + 1, times[i].lastIndexOf('-')).trim();
place = times[i].substring(times[i].lastIndexOf('-') + 1).trim();
lines.push($(`<h2 class="dateTimePlace">${makeLine(date, time, place)}</th>`));
}
return lines
}
function makeLine(date, time, place) {
var arr = separateDays(date)
var output = prettifyDaysText(arr)
var building = place.substring(0, place.search(/\d/) - 1);
building = building == "" ? "Undecided Location" : building;
return `${output} at ${time.replace(/\./g, '').replace(/\-/g, ' to ')} in <a style='font-size:medium' target='_blank' href='https://maps.utexas.edu/buildings/UTM/${building}'>${building}</>`;
}
function setChart(data) {
//set up the chart
toggleChartLoading(false);
chart = Highcharts.chart('chart', buildChartConfig(data), function (chart) { // on complete
if (data.length == 0) {
//if no data, then show the message and hide the series
chart.renderer.text('Could not find data for this Instructor teaching this Course.', 100, 120)
.css({
fontSize: '20px',
width: '300px',
align: 'center',
left: '160px'
})
.add();
$.each(chart.series, function (i, ser) {
ser.hide();
});
}
});
}
function standardizeName(department, number, name){
return `${department} ${number} ${name}`
}
function allowClosing() {
$('.close').click(function () {
close();
});
$('#myModal').click(function (event) {
if (event.target.id == 'myModal') {
close();
}
});
}
function close() {
$("#myModal").fadeOut(Timing.fade_time);
$("#snackbar").attr("class", "");
}
$(document).keydown(function (e) {
/*Close Modal when hit escape*/
if (e.keyCode == 27) {
close();
}
});

View File

@@ -1,386 +0,0 @@
const days = new Map([
["M", "Monday"],
["T", "Tuesday"],
["W", "Wednesday"],
["TH", "Thursday"],
["F", "Friday"]
]);
function getStatusColor(status, sub = false) {
let color = "black";
if (status.includes("open")) {
color = sub ? Colors.open_light : Colors.open;
} else if (status.includes("waitlisted")) {
color = sub ? Colors.waitlisted_light : Colors.waitlisted;
} else if (status.includes("closed") || status.includes("cancelled")) {
color = sub ? Colors.closed_light : Colors.closed;
} else {
color = sub ? Colors.no_status_light : Colors.no_status;
}
return color;
}
function buildQuery(course_data, sem) {
let query = !sem ? "select * from agg" : "select * from grades";
query += " where dept like '%" + course_data["department"] + "%'";
query += " and prof like '%" + course_data["prof_name"].replace(/'/g, "") + "%'";
query += " and course_nbr like '%" + course_data["number"] + "%'";
if (sem) {
query += "and sem like '%" + sem + "%'";
}
return query + "order by a1+a2+a3+b1+b2+b3+c1+c2+c3+d1+d2+d3+f desc";
}
/*Course object for passing to background*/
function Course(coursename, unique, profname, datetimearr, status, link, registerlink) {
this.coursename = coursename;
this.unique = unique;
this.profname = profname;
this.datetimearr = datetimearr;
this.status = status;
this.link = link;
this.registerlink = registerlink;
}
function capitalizeString(string) {
//if one word, and if multiple words:
let output = "";
words = string.split(/[. ,\/ -]/);
for (let i in words) {
word = words[i];
capitalizedWord = word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
output += capitalizedWord + " ";
}
return output.trim();
}
function separateCourseNameParts(name) {
let num_index = name.search(/\d/);
department = name.substring(0, num_index).trim();
number = name.substring(num_index, name.indexOf(" ", num_index)).trim();
name = capitalizeString(name.substring(name.indexOf(" ", num_index)).trim());
return {
name: name,
department: department,
number: number
}
}
function separateDays(date, simple=false) {
let arr = [];
for (var i = 0; i < date.length; i++) {
let letter = date.charAt(i);
let separated_letter = letter;
if (letter == "T" && i < date.length - 1 && date.charAt(i + 1) == "H") {
arr.push(simple ? "TH" : days.get("TH"));
} else {
if (letter != "H") {
arr.push(simple ? letter : days.get(letter));
}
}
}
return arr;
}
/*Convert time to 24hour format*/
function convertTime(time) {
var converted = time.replace(/\./g, '').split("-");
for (var i = 0; i < 2; i++) {
converted[i] = moment(converted[i], ["h:mm A"]).format("HH:mm");
}
return converted;
}
function prettifyDaysText(arr) {
var output = "";
if (arr.length > 2) {
for (var i = 0; i < arr.length; i++) {
if (i < arr.length - 1)
output += arr[i] + ", "
if (i == arr.length - 2)
output += "and ";
if (i == arr.length - 1)
output += arr[i];
}
} else if (arr.length == 2) {
output = arr[0] + " and " + arr[1];
} else {
output = arr[0];
}
return output
}
function isIndividualCoursePage(){
return $("#textbook_button").length != 0;
}
function updateAllTabsCourseList() {
chrome.tabs.query({}, function (tabs) {
for (var i = 0; i < tabs.length; i++) {
chrome.tabs.sendMessage(tabs[i].id, {
command: "updateCourseList"
});
}
});
}
function htmlToNode(response) {
return $('<div/>').html(response).contents();
}
function setCurrentTabUrl(link) {
chrome.tabs.query({
currentWindow: true,
active: true
}, function (tab) {
chrome.tabs.update(tab.id, {
url: link
});
});
}
function openMoreInfoWithOpenModal(link){
chrome.runtime.sendMessage({ command: "setOpen", url: link });
}
function semesterSort(semA, semB) {
let semOrder = {
"Spring": 0,
"Fall": 1,
"Summer": 2,
"Winter": 3
}
let aName = semA.split(' ')[0];
let aYear = parseInt(semA.split(' ')[1]);
let bName = semB.split(' ')[0];
let bYear = parseInt(semB.split(' ')[1]);
if (aYear < bYear)
return -1;
if (aYear > bYear)
return 1;
if (semOrder[aName] < semOrder[bName])
return -1;
if (semOrder[aName] > semOrder[bName])
return 1;
return 0;
}
/* convert from the dtarr and maek the time lines*/
function convertDateTimeArrToLine(datetimearr) {
var output = [];
var dtmap = makeDateTimeMap(datetimearr);
var timearr = Array.from(dtmap.keys());
var temporary = Array.from(dtmap.values())
var dayarr = []
var locarr = []
for(x in temporary) {
dayarr.push(temporary[x][0])
locarr.push(temporary[x][1])
}
for (var i = 0; i < dayarr.length; i++) {
//var place = findLocation(dayarr[i], timearr[i], datetimearr);
var place = locarr[i]
var building = place.substring(0, place.search(/\d/)).trim();
building = building ? building : "Undecided Location"
var timearrsplit = timearr[i].split(',')
output.push({
"days": dayarr[i],
"start_time": timearrsplit[0],
"end_time": timearrsplit[1],
"location_link": `https://maps.utexas.edu/buildings/UTM/${building}`,
"location_full": place
})
}
return output;
}
function makeDateTimeMap(datetimearr) {
var dtmap = new Map([]);
for (var i = 0; i < datetimearr.length; i++) {
datetimearr[i][1][0] = moment(datetimearr[i][1][0], ["HH:mm A"]).format("h:mm A");
datetimearr[i][1][1] = moment(datetimearr[i][1][1], ["HH:mm A"]).format("h:mm A");
}
for (var i = 0; i < datetimearr.length; i++) {
var instance = datetimearr[i]
var day = String(instance[0])
var timeslot = String(instance[1])
var location = String(instance[2])
var key = timeslot + "," + location
if (dtmap.has(key) && dtmap.get(key)[1] === location) {
dtmap.set(key, [dtmap.get(key)[0] + day, location]);
} else {
dtmap.set(key, [day, location]);
}
}
return dtmap
}
//find the location of a class given its days and timearrs.
function findLocation(day, timearr, datetimearr) {
for (let i = 0; i < datetimearr.length; i++) {
var dtl = datetimearr[i];
if (day.includes(dtl[0])) {
if (JSON.stringify(timearr) == JSON.stringify(reformatDateTime(dtl[1]))) {
return dtl[2];
}
}
}
}
function validateCourses(courses) {
for (var i = 0; i < courses.length; i++) {
if (!validateCourseObject(courses[i])) {
return false;
}
}
return true;
}
function validateCourseObject(course) {
var is_valid = true;
var props = ["coursename", "datetimearr", "link", "profname", "status", "unique"];
for (let j = 0; j < props.length; j++) {
is_valid &= course.hasOwnProperty(props[j]);
}
return is_valid;
}
function reformatDateTime(dtl1) {
let output = "";
for (let i = 0; i < dtl1.length; i++) {
output += dtl1[i];
if (i != dtl1.length - 1) {
output += ",";
}
}
return output;
}
function rgb2hex(rgb) {
rgb = rgb.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);
function hex(x) {
return ("0" + parseInt(x).toString(16)).slice(-2);
}
return "#" + hex(rgb[1]) + hex(rgb[2]) + hex(rgb[3]);
}
function buildChartConfig(data) {
return {
chart: {
type: 'column',
backgroundColor: ' #fefefe',
spacingLeft: 10
},
title: {
text: null
},
subtitle: {
text: null
},
legend: {
enabled: false
},
xAxis: {
title: {
text: 'Grades'
},
categories: [
'A',
'A-',
'B+',
'B',
'B-',
'C+',
'C',
'C-',
'D+',
'D',
'D-',
'F'
],
crosshair: true
},
yAxis: {
min: 0,
title: {
text: 'Students'
}
},
credits: {
enabled: false
},
lang: {
noData: "The professor hasn't taught this class :("
},
tooltip: {
headerFormat: '<span style="font-size:small; font-weight:bold">{point.key}</span><table>',
pointFormat: '<td style="color:{black};padding:0;font-size:small; font-weight:bold;"><b>{point.y:.0f} Students</b></td>',
footerFormat: '</table>',
shared: true,
useHTML: true
},
plotOptions: {
bar: {
pointPadding: 0.2,
borderWidth: 0
},
series: {
animation: {
duration: 700
}
}
},
series: [{
name: 'Grades',
data: [{
y: data[6],
color: '#4CAF50'
}, {
y: data[7],
color: '#8BC34A'
}, {
y: data[8],
color: '#CDDC39'
}, {
y: data[9],
color: '#FFEB3B'
}, {
y: data[10],
color: '#FFC107'
}, {
y: data[11],
color: '#FFA000'
}, {
y: data[12],
color: '#F57C00'
}, {
y: data[13],
color: '#FF5722'
}, {
y: data[14],
color: '#FF5252'
}, {
y: data[15],
color: '#E64A19'
}, {
y: data[16],
color: '#F44336'
}, {
y: data[17],
color: '#D32F2F'
}]
}]
}
}
function canNotRegister(status, register_link) {
return status.includes("closed") || status.includes("cancelled") || !status || !register_link
}

View File

@@ -1,70 +0,0 @@
{
"manifest_version": 2,
"name": "UT Registration Plus",
"version": "1.2.2.7",
"options_page": "options.html",
"description": "Improves the course registration process at the University of Texas at Austin!",
"permissions": [
"storage",
"*://*.utdirect.utexas.edu/apps/registrar/course_schedule/*",
"*://*.utexas.collegescheduler.com/*",
"*://*.catalog.utexas.edu/ribbit/",
"*://*.registrar.utexas.edu/schedules/*",
"*://*.login.utexas.edu/login/*"
],
"content_scripts": [
{
"css": ["css/styles.css"],
"js": [
"js/config.js",
"js/lib/moment.min.js",
"js/lib/highcharts.js",
"js/lib/jquery-3.3.1.min.js",
"js/lib/jquery.initialize.min.js",
"js/util.js",
"js/Template.js",
"js/courseCatalog.js"
],
"matches": ["https://utdirect.utexas.edu/apps/registrar/course_schedule/*"]
},
{
"css": ["css/styles.css"],
"js": [
"js/config.js",
"js/lib/moment.min.js",
"js/lib/highcharts.js",
"js/lib/jquery-3.3.1.min.js",
"js/lib/jquery.initialize.min.js",
"js/util.js",
"js/Template.js",
"js/utPlanner.js"
],
"matches": ["https://utexas.collegescheduler.com/*"]
},
{
"css": ["css/styles.css"],
"js": ["js/config.js", "js/lib/moment.min.js", "js/lib/highcharts.js", "js/lib/jquery-3.3.1.min.js", "js/Template.js", "js/util.js", "js/import.js"],
"matches": ["https://utdirect.utexas.edu/registrar/waitlist/wl_see_my_waitlists.WBX", "https://utdirect.utexas.edu/registration/classlist.WBX*"]
}
],
"web_accessible_resources": ["grades.db", "images/disticon.png"],
"background": {
"scripts": ["js/lib/jquery-3.3.1.min.js", "js/lib/sql-memory-growth.js", "js/lib/moment.min.js", "js/config.js", "js/util.js", "js/background.js"],
"persistent": true
},
"browser_action": {
"default_popup": "popup.html",
"default_icon": {
"16": "icons/icon16.png",
"32": "icons/icon32.png",
"48": "icons/icon48.png",
"128": "icons/icon128.png"
}
},
"icons": {
"16": "icons/icon16.png",
"32": "icons/icon32.png",
"48": "icons/icon48.png",
"128": "icons/icon128.png"
}
}

View File

@@ -1,30 +0,0 @@
<!DOCTYPE html>
<html>
<!-- This file is serving as the template for the options page -->
<head>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="css/styles.css" />
<link rel="stylesheet" href="css/options.css" />
</head>
<body>
<div id="version-container">
<p class="version">(v<span id="version"></span>)</p>
</div>
<div class="card options-card" id="header">
<h2 class="options-header"><u>Options</u></h2>
<div id="options_container"></div>
<p class="creator-tag"><a href="https://sghsri.github.io">Sriram Hariharan</a> (2018)</p>
</div>
<div class="card options-card" id="contributors_container">
<h3 class="contributor-title">Amazing people who've contributed to the extension!</h3>
<p class="creator-tag open-source-tag">Code is open source here <a href="https://github.com/sghsri/UT-Registration-Plus">here</a> :)</p>
<div id="contributor-list"></div>
</div>
<script src="js/config.js"></script>
<script src="js/lib/jquery-3.3.1.min.js"></script>
<script src="js/util.js"></script>
<script src="js/Template.js"></script>
<script src="js/options.js"></script>
</body>
</html>

123
package.json Normal file
View File

@@ -0,0 +1,123 @@
{
"name": "ut-registration-plus",
"displayName": "UT Registration Plus",
"version": "2.0.0-beta4",
"description": "UT Registration Plus is a Chrome extension that allows students to easily register for classes.",
"private": true,
"homepage": "https://github.com/Longhorn-Developers/UT-Registration-Plus",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"prettier": "prettier src --check",
"prettier:fix": "prettier src --write",
"lint": "eslint src --ext ts,tsx --report-unused-disable-directives",
"lint:fix": "eslint src --ext ts,tsx --report-unused-disable-directives --fix",
"check-types": "tsc --noEmit",
"test": "vitest",
"test:ui": "vitest --ui",
"coverage": "vitest run --coverage",
"preview": "vite preview",
"preinstall": "npx only-allow pnpm",
"storybook": "storybook dev -p 6006",
"build-storybook": "storybook build",
"prepare": "husky"
},
"dependencies": {
"@headlessui/react": "^2.0.3",
"@hello-pangea/dnd": "^16.5.0",
"@unocss/vite": "^0.58.6",
"@vitejs/plugin-react": "^4.2.1",
"chrome-extension-toolkit": "^0.0.54",
"clsx": "^2.1.0",
"highcharts": "^11.3.0",
"highcharts-react-official": "^3.2.1",
"html-to-image": "^1.11.11",
"husky": "^9.0.11",
"nanoid": "^5.0.6",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"sass": "^1.71.1",
"sql.js": "1.10.2"
},
"devDependencies": {
"@chromatic-com/storybook": "^1.4.0",
"@commitlint/cli": "^18.6.1",
"@commitlint/config-conventional": "^18.6.2",
"@commitlint/types": "^19.0.3",
"@crxjs/vite-plugin": "2.0.0-beta.21",
"@iconify-json/bi": "^1.1.23",
"@iconify-json/material-symbols": "^1.1.73",
"@iconify-json/ri": "^1.1.20",
"@storybook/addon-designs": "^8.0.1",
"@storybook/addon-essentials": "^8.1.1",
"@storybook/addon-links": "^8.1.1",
"@storybook/blocks": "^8.1.1",
"@storybook/react": "^8.1.1",
"@storybook/react-vite": "^8.1.1",
"@storybook/test": "^8.1.1",
"@svgr/core": "^8.1.0",
"@svgr/plugin-jsx": "^8.1.0",
"@types/chrome": "^0.0.268",
"@types/node": "^20.12.12",
"@types/prompts": "^2.4.9",
"@types/react": "^18.3.2",
"@types/react-dom": "^18.3.0",
"@types/semver": "^7.5.8",
"@types/sql.js": "^1.4.9",
"@typescript-eslint/eslint-plugin": "^6.21.0",
"@typescript-eslint/parser": "^6.21.0",
"@unocss/eslint-config": "^0.58.6",
"@unocss/postcss": "^0.58.6",
"@unocss/preset-uno": "^0.58.6",
"@unocss/preset-web-fonts": "^0.58.6",
"@unocss/reset": "^0.58.6",
"@unocss/transformer-directives": "^0.58.6",
"@unocss/transformer-variant-group": "^0.58.6",
"@vitejs/plugin-react-swc": "^3.6.0",
"@vitest/coverage-v8": "^1.3.1",
"@vitest/ui": "^1.3.1",
"chromatic": "^11.3.5",
"cssnano": "^6.0.5",
"cssnano-preset-advanced": "^6.0.5",
"dotenv": "^16.4.5",
"es-module-lexer": "^1.4.1",
"eslint": "^8.57.0",
"eslint-config-airbnb": "^19.0.4",
"eslint-config-airbnb-base": "^15.0.0",
"eslint-config-airbnb-typescript": "^17.1.0",
"eslint-config-prettier": "^9.1.0",
"eslint-import-resolver-typescript": "^3.6.1",
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-import-essentials": "^0.2.1",
"eslint-plugin-jsdoc": "^48.2.0",
"eslint-plugin-prettier": "^5.1.3",
"eslint-plugin-react": "^7.33.2",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-prefer-function-component": "^3.3.0",
"eslint-plugin-react-refresh": "^0.4.5",
"eslint-plugin-simple-import-sort": "^12.0.0",
"eslint-plugin-storybook": "^0.8.0",
"path": "^0.12.7",
"postcss": "^8.4.35",
"prettier": "^3.2.5",
"react-dev-utils": "^12.0.1",
"storybook": "^8.1.1",
"typescript": "^5.4.3",
"unocss": "^0.58.6",
"unocss-preset-primitives": "0.0.2-beta.0",
"unplugin-icons": "^0.18.5",
"vite": "^5.1.4",
"vite-plugin-inspect": "^0.8.3",
"vitest": "^1.3.1"
},
"pnpm": {
"patchedDependencies": {
"@crxjs/vite-plugin@2.0.0-beta.21": "patches/@crxjs__vite-plugin@2.0.0-beta.21.patch",
"@unocss/vite@0.58.6": "patches/@unocss__vite@0.58.6.patch"
},
"overrides": {
"es-module-lexer": "^1.4.1"
}
}
}

View File

@@ -0,0 +1,105 @@
diff --git a/dist/index.mjs b/dist/index.mjs
index 5c3f6291168987c56b816428080e6f1fe9de7107..abaf6290fe9454ae036a81eacbe7dc3be2fdfbc3 100644
--- a/dist/index.mjs
+++ b/dist/index.mjs
@@ -499,16 +499,43 @@ ${sourceMap}
}),
mergeMap(async ({ target, code, deps }) => {
await lexer.init;
- const [imports] = lexer.parse(code, fileName);
+ const [imports, exports] = lexer.parse(code, fileName);
const depSet = new Set(deps);
const magic = new MagicString(code);
- for (const i of imports)
+ for (const i of imports) {
if (i.n) {
depSet.add(i.n);
const fileName2 = getFileName({ type: "module", id: i.n });
const fullImport = code.substring(i.s, i.e);
- magic.overwrite(i.s, i.e, fullImport.replace(i.n, `/${fileName2}`));
+ const hmrTimestamp = fullImport.match(/\bt=\d{13}&?\b/);
+ magic.overwrite(
+ i.s,
+ i.e,
+ fullImport.replace(
+ i.n,
+ `/${fileName2}${hmrTimestamp ? `?${hmrTimestamp[0]}` : ""}`
+ )
+ );
+ }
+ }
+ for (const e of exports) {
+ if (e.n === "default") {
+ const regex = /\s+['"](.*)['"]/y;
+ regex.lastIndex = e.e;
+ const fullExport = regex.exec(code)?.[1];
+ if (!fullExport)
+ continue;
+ const start = regex.lastIndex - fullExport.length - 1;
+ const end = regex.lastIndex - 1;
+ if (fullExport.startsWith("/node_modules")) {
+ magic.overwrite(
+ start,
+ end,
+ `http://localhost:5173${fullExport}`
+ );
+ }
}
+ }
return { target, source: magic.toString(), deps: [...depSet] };
})
);
@@ -1229,10 +1256,14 @@ const pluginHMR = () => {
handleHotUpdate({ modules, server }) {
const { root } = server.config;
const relFiles = /* @__PURE__ */ new Set();
- for (const m of modules)
+ function getRelFile(file) {
+ return file.startsWith(root) ? file.slice(server.config.root.length) : file;
+ }
+ for (const m of modules) {
if (m.id?.startsWith(root)) {
relFiles.add(m.id.slice(server.config.root.length));
}
+ }
if (inputManifestFiles.background.length) {
const background = prefix$1("/", inputManifestFiles.background[0]);
if (relFiles.has(background) || modules.some(isImporter(join(server.config.root, background)))) {
@@ -1244,7 +1275,14 @@ const pluginHMR = () => {
for (const [key, script] of contentScripts)
if (key === script.id) {
if (relFiles.has(script.id) || modules.some(isImporter(join(server.config.root, script.id)))) {
- relFiles.forEach((relFile) => update(relFile));
+ modules.filter((mod) => mod.id?.startsWith(root)).forEach((mod) => {
+ update(getRelFile(mod.id));
+ if (mod.file?.endsWith(".scss")) {
+ mod.importers.forEach((imp) => {
+ update(getRelFile(imp.id));
+ });
+ }
+ });
}
}
}
@@ -1882,7 +1920,7 @@ const pluginWebAccessibleResources = () => {
if (contentScripts.size > 0) {
const viteManifest = parseJsonAsset(
bundle,
- "manifest.json"
+ ".vite/manifest.json"
);
const viteFiles = /* @__PURE__ */ new Map();
for (const [, file] of Object.entries(viteManifest))
diff --git a/package.json b/package.json
index e0c47ae66ff399ad3a78abf38d8d93d1f038c55d..f84eb09ffbb5c41094935dd06e04ffe831e2d05a 100644
--- a/package.json
+++ b/package.json
@@ -70,7 +70,7 @@
"connect-injector": "^0.4.4",
"convert-source-map": "^1.7.0",
"debug": "^4.3.3",
- "es-module-lexer": "^0.10.0",
+ "es-module-lexer": "^1.4.1",
"fast-glob": "^3.2.11",
"fs-extra": "^10.0.1",
"jsesc": "^3.0.2",

View File

@@ -0,0 +1,108 @@
diff --git a/dist/index.cjs b/dist/index.cjs
index 560f423a07f21b0c47abd494d77654de4c874481..35ae1fdca8bd5546f7e40a23edacb1dbbbd34b58 100644
--- a/dist/index.cjs
+++ b/dist/index.cjs
@@ -35,15 +35,15 @@ const VIRTUAL_ENTRY_ALIAS = [
/^(?:virtual:)?uno(?::(.+))?\.css(\?.*)?$/
];
const LAYER_MARK_ALL = "__ALL__";
-const RESOLVED_ID_WITH_QUERY_RE = /[\/\\]__uno(?:(_.*?))?\.css(\?.*)?$/;
-const RESOLVED_ID_RE = /[\/\\]__uno(?:_(.*?))?\.css$/;
+const RESOLVED_ID_WITH_QUERY_RE = /[\/\\]uno(?:(_.*?))?\.css(\?.*)?$/;
+const RESOLVED_ID_RE = /[\/\\]uno(?:_(.*?))?\.css$/;
function resolveId(id) {
if (id.match(RESOLVED_ID_WITH_QUERY_RE))
return id;
for (const alias of VIRTUAL_ENTRY_ALIAS) {
const match = id.match(alias);
if (match) {
- return match[1] ? `/__uno_${match[1]}.css` : "/__uno.css";
+ return match[1] ? `/uno_${match[1]}.css` : "/uno.css";
}
}
}
@@ -745,7 +745,7 @@ function GlobalModeDevPlugin({ uno, tokens, tasks, flushTasks, affectedModules,
const { hash, css } = await generateCSS(layer);
return {
// add hash to the chunk of CSS that it will send back to client to check if there is new CSS generated
- code: `__uno_hash_${hash}{--:'';}${css}`,
+ code: `uno_hash_${hash}{--:'';}${css}`,
map: { mappings: "" }
};
},
@@ -764,7 +764,7 @@ function GlobalModeDevPlugin({ uno, tokens, tasks, flushTasks, affectedModules,
if (layer && code.includes("import.meta.hot")) {
let hmr = `
try {
- let hash = __vite__css.match(/__uno_hash_(\\w{${HASH_LENGTH}})/)
+ let hash = __vite__css.match(/uno_hash_(\\w{${HASH_LENGTH}})/)
hash = hash && hash[1]
if (!hash)
console.warn('[unocss-hmr]', 'failed to get unocss hash, hmr might not work')
diff --git a/dist/index.mjs b/dist/index.mjs
index bbbccb7cad7421cbdb97223a451ec5853c0476cb..4bf6a08d94e562090a530308c0ab8337afdf8243 100644
--- a/dist/index.mjs
+++ b/dist/index.mjs
@@ -1,17 +1,17 @@
-import process$1 from 'node:process';
+import remapping from '@ampproject/remapping';
+import { createFilter } from '@rollup/pluginutils';
+import { loadConfig } from '@unocss/config';
+import { BetterMap, createGenerator, cssIdRE, notNull, toEscapedSelector } from '@unocss/core';
import UnocssInspector from '@unocss/inspector';
-import { resolve, isAbsolute, dirname } from 'node:path';
-import fs from 'node:fs/promises';
import fg from 'fast-glob';
import MagicString from 'magic-string';
-import remapping from '@ampproject/remapping';
-import { createHash } from 'node:crypto';
-import { cssIdRE, createGenerator, BetterMap, notNull, toEscapedSelector } from '@unocss/core';
import { Buffer } from 'node:buffer';
-import { createFilter } from '@rollup/pluginutils';
+import { createHash } from 'node:crypto';
import fs$1 from 'node:fs';
+import fs from 'node:fs/promises';
+import { dirname, isAbsolute, resolve } from 'node:path';
+import process$1 from 'node:process';
import { fileURLToPath } from 'node:url';
-import { loadConfig } from '@unocss/config';
const defaultPipelineExclude = [cssIdRE];
const defaultPipelineInclude = [/\.(vue|svelte|[jt]sx|mdx?|astro|elm|php|phtml|html)($|\?)/];
@@ -20,15 +20,15 @@ const VIRTUAL_ENTRY_ALIAS = [
/^(?:virtual:)?uno(?::(.+))?\.css(\?.*)?$/
];
const LAYER_MARK_ALL = "__ALL__";
-const RESOLVED_ID_WITH_QUERY_RE = /[\/\\]__uno(?:(_.*?))?\.css(\?.*)?$/;
-const RESOLVED_ID_RE = /[\/\\]__uno(?:_(.*?))?\.css$/;
+const RESOLVED_ID_WITH_QUERY_RE = /[\/\\]uno(?:(_.*?))?\.css(\?.*)?$/;
+const RESOLVED_ID_RE = /[\/\\]uno(?:_(.*?))?\.css$/;
function resolveId(id) {
if (id.match(RESOLVED_ID_WITH_QUERY_RE))
return id;
for (const alias of VIRTUAL_ENTRY_ALIAS) {
const match = id.match(alias);
if (match) {
- return match[1] ? `/__uno_${match[1]}.css` : "/__uno.css";
+ return match[1] ? `/uno_${match[1]}.css` : "/uno.css";
}
}
}
@@ -730,7 +730,7 @@ function GlobalModeDevPlugin({ uno, tokens, tasks, flushTasks, affectedModules,
const { hash, css } = await generateCSS(layer);
return {
// add hash to the chunk of CSS that it will send back to client to check if there is new CSS generated
- code: `__uno_hash_${hash}{--:'';}${css}`,
+ code: `uno_hash_${hash}{--:'';}${css}`,
map: { mappings: "" }
};
},
@@ -749,7 +749,7 @@ function GlobalModeDevPlugin({ uno, tokens, tasks, flushTasks, affectedModules,
if (layer && code.includes("import.meta.hot")) {
let hmr = `
try {
- let hash = __vite__css.match(/__uno_hash_(\\w{${HASH_LENGTH}})/)
+ let hash = __vite__css.match(/uno_hash_(\\w{${HASH_LENGTH}})/)
hash = hash && hash[1]
if (!hash)
console.warn('[unocss-hmr]', 'failed to get unocss hash, hmr might not work')

14024
pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,90 +0,0 @@
<!DOCTYPE html>
<html>
<!-- This file is serving as the page for the browser action popup -->
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="css/popup.css">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
</head>
<body>
<div class="card" id="card-header" class='header_container'>
<div id="buttons" class="header_buttons">
<button id="clear" class="material_button header_button clear_button">Clear All</button>
<button id="RIS" class="material_button header_button ris_button">Registrar Info </button>
<button id="calendar" class="material_button header_button schedule_button">My Schedule</button>
</div>
</div>
<div>
<ul id="courseList" class='course_list'></ul>
<h2 id="empty" class='empty_message'>
<div id="main">Doesn't Look Like Anything To Me.</div>
<span>(No Courses Saved)</span>
</h2>
<div class="settings_divider"></div>
<input type="file" id="import_input" accept=".json" class="hide" />
<div>
<div id="meta-data" class="meta-container">
<p class="meta"> <span class="meta-metric" id="meta-metric">17</span> hr</p>
</div>
<div class="settings">
<button title='Search' class="settings_button search_button" id='search'>
<i class="material-icons settings_icon">search</i>
</button>
<div id="search-popup" class="hide">
<div class="flex-container">
<div id='semcon' class="select-style item">
<label>
<select id="semesters"></select>
</label>
</div>
<div id='depcon' class="select-style item">
<label>
<select id="department"></select>
</label>
</div>
<div id='levcon' class="select-style item">
<label>
<select id="level">
<option value="L">Lower</option>
<option value="U">Upper</option>
<option value="G">Grad</option>
</select>
</label>
</div>
<div>
<input class = "input-box" placeholder="Course # (optional)" type="text" id="courseCode"></input>
</div>
</div>
<button id="search-class" class="material_button search-button">Search</button>
</div>
<button title='Import/Export' class="settings_button import_button" id='impexp'>
<i class="material-icons settings_icon">import_export</i>
</button>
<div id="import-export-popup" class="hide">
<div class="flex-container">
<button id="import-class" class="simple-menu-option">
<i class="material-icons">file_upload</i>Import Classes
</button>
<button id="export-class" class="simple-menu-option">
<i class="material-icons">file_download</i>Export Classes
</button>
</div>
</div>
<button title='Options' class="settings_button options_button" id='options_button'>
<i class="material-icons settings_icon">settings</i>
</button>
</div>
</div>
</div>
<script src="js/lib/jquery-3.3.1.min.js"></script>
<script src="js/lib/moment.min.js"></script>
<script src="js/Template.js"></script>
<script src="js/config.js"></script>
<script src="js/util.js"></script>
<script src="js/popup.js"></script>
</body>
</html>

10
postcss.config.cjs Normal file
View File

@@ -0,0 +1,10 @@
/* eslint-disable global-require */
/** @type {import('postcss-load-config').Config} */
const config = {
plugins: {
cssnano: process.env.NODE_ENV !== 'development' ? {} : false,
// '@unocss/postcss': {},
},
};
module.exports = config;

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg id="Beta" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024">
<defs>
<style>
.cls-1 {
fill: none;
stroke: #fff;
stroke-width: 70.06px;
}
.cls-2 {
fill: #005f86;
}
.cls-2, .cls-3 {
stroke-width: 0px;
}
.cls-3 {
fill: #fff;
}
</style>
</defs>
<rect class="cls-2" width="1024" height="1024"/>
<g>
<circle class="cls-1" cx="512" cy="512" r="362"/>
<rect class="cls-3" x="466.29" y="283.46" width="91.41" height="457.07"/>
<rect class="cls-3" x="283.46" y="466.29" width="457.07" height="91.41"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 684 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 429 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 791 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg id="Development" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024">
<defs>
<style>
.cls-1 {
fill: none;
stroke: #fff;
stroke-width: 70.06px;
}
.cls-2 {
fill: #bf2178;
}
.cls-2, .cls-3 {
stroke-width: 0px;
}
.cls-3 {
fill: #fff;
}
</style>
</defs>
<rect class="cls-2" x="0" width="1024" height="1024"/>
<g>
<circle class="cls-1" cx="512" cy="512" r="362"/>
<rect class="cls-3" x="466.29" y="283.46" width="91.41" height="457.07"/>
<rect class="cls-3" x="283.46" y="466.29" width="457.07" height="91.41"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 697 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 449 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 729 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg id="Production" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024">
<defs>
<style>
.cls-1 {
fill: #bf5700;
}
.cls-1, .cls-2 {
stroke-width: 0px;
}
.cls-3 {
fill: none;
stroke: #fff;
stroke-width: 70.06px;
}
.cls-2 {
fill: #fff;
}
</style>
</defs>
<rect class="cls-1" width="1024" height="1024"/>
<g>
<circle class="cls-3" cx="512" cy="512" r="362"/>
<rect class="cls-2" x="466.29" y="283.46" width="91.41" height="457.07"/>
<rect class="cls-2" x="283.46" y="466.29" width="457.07" height="91.41"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 690 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 421 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 706 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -0,0 +1,221 @@
[
"ACC",
"ADV",
"ASE",
"AFR",
"AFS",
"ASL",
"AMS",
"AHC",
"ANT",
"ALD",
"ARA",
"ARE",
"ARI",
"ARC",
"AED",
"ARH",
"ART",
"AET",
"AAS",
"ANS",
"AST",
"BSN",
"BEN",
"BCH",
"BIO",
"BME",
"BDP",
"B A",
"BAX",
"BGS",
"CHE",
"CH",
"CHI",
"C E",
"CLA",
"C C",
"CGS",
"COM",
"CLD",
"CMS",
"CRP",
"C L",
"COE",
"CSE",
"C S",
"CON",
"CTI",
"CRW",
"CDI",
"EDC",
"CZ",
"DAN",
"DSC",
"D S",
"DES",
"DEV",
"D B",
"DRS",
"DCH",
"ECO",
"ELP",
"EDP",
"E E",
"ECE",
"EER",
"EMA",
"ENM",
"E M",
"E S",
"E",
"ESL",
"ENS",
"EVE",
"EVS",
"EUP",
"EUS",
"FIN",
"F A",
"FLU",
"FR",
"F H",
"G E",
"GRG",
"GEO",
"GER",
"GSD",
"GOV",
"GRS",
"GK",
"GUI",
"HAR",
"H S",
"HCT",
"HED",
"HEB",
"HIN",
"HIS",
"HDF",
"HDO",
"H E",
"HMN",
"ILA",
"I",
"ISP",
"INF",
"ITD",
"I B",
"IRG",
"ISL",
"ITL",
"ITC",
"JPN",
"J S",
"J",
"KIN",
"KOR",
"LAR",
"LTC",
"LAT",
"LAL",
"LAS",
"LAW",
"LEB",
"L A",
"LAH",
"LIN",
"MAL",
"MAN",
"MIS",
"MFG",
"MNS",
"MKT",
"MSE",
"M",
"M E",
"MDV",
"MAS",
"MEL",
"MES",
"M S",
"MOL",
"MUS",
"NSC",
"N S",
"NEU",
"NOR",
"N",
"NTR",
"OBO",
"OPR",
"O M",
"ORI",
"ORG",
"PER",
"PRS",
"PGE",
"PGS",
"PHM",
"PHL",
"PED",
"P S",
"PHY",
"PIA",
"POL",
"POR",
"PRC",
"PSY",
"P A",
"PBH",
"P R",
"RIM",
"RTF",
"R E",
"R S",
"RHE",
"R M",
"RUS",
"REE",
"SAN",
"SAX",
"STC",
"STM",
"S C",
"SEL",
"S S",
"S W",
"SOC",
"SPN",
"SPC",
"SED",
"SLH",
"STA",
"SDS",
"SUS",
"SWE",
"TAM",
"TXA",
"T D",
"TRO",
"TRU",
"TBA",
"TUR",
"T C",
"UKR",
"UGS",
"UDN",
"URB",
"URD",
"UTS",
"UTL",
"VIA",
"VIO",
"V C",
"VAS",
"VOI",
"WGS",
"WRT",
"YID",
"YOR"
]

View File

@@ -0,0 +1,54 @@
const splashText: string[] = [
"You can't fail classes you're not in, that's for sure.",
'Steer clear of O-Chem, unless you fancy a challenge.',
'Rec Sports fills up fast, even before the sun reaches its peak.',
"Ah, Jendy's! A taste ever so refined.",
'Fine dining at Jester City Limits, eh?',
'Rec Sports fills up fast, even before the sun reaches its peak.',
'RIP Domino, you beloved campus feline.',
"The year is 2055 and Welch still isn't finished.",
'Motivation dropping faster than ur GPA',
'No Work Happens On PCL 5th Floor.',
'I may be a sophomore in name, but my credit count screams freshman!',
'Pain is temporary, GPA is forever.',
"You've Yee'd Your Last Haw.",
'lol everything is already waitlisted.',
"At Least You're Not At A&M.",
'TeXAs iS BaCK GuYZ',
'mAke iT yOuR tExAS',
"'Academically Challenged'",
'Does McCombs teach Parseltongue?',
'No Cruce Enfrente Del Bus.',
'Omae Wa Mou Shindeiru...',
'Bevo Bucks are the new Bitcoin',
'Every day another brick disappears from Speedway',
'The GDC will annex the EER one day',
'Just you wait. Our CNS operatives will topple the EER regime',
'To hike to Kins or not to hike to Kins...',
'The road to Kinsolving is long, but their delicacies makes it worth every step.',
"C'mon you Longhorns! You want to study forever?",
'HOW BOUT A NICE CUP OF LIBER TEA',
"It's called the quiet floor of the PCL for a reason",
"'Whose car is this and why is it attempting to enter Welch?'",
"'I really like one of my TAs and I wanna ask her out after this semester ends'",
'CaN YoU aSk OuT a tA aFtEr tHe SeMeStEr Is oVeR AnD gRaDeS ArE DoNe?',
"The Block of Butter incident of '22",
"Arrows of Christ vs Church of Scientology was the crossover we didn't know we needed",
'THE WALK SIGN IS ON TO CROSS GUADALUPE AND 21ST',
'Days since last GDC door alarm: 0',
'Finding a parking spot is like winning the lottery... if the lottery required parallel parking skills.',
'The squirrels are more ambitious than most freshmen during finals week.',
'The line at the on-campus Starbucks is longer than your course waitlist.',
'The weather changes more often than your class schedule.',
"'Sorry, the PCL is full' is the most heartbreaking message you'll ever receive.",
'Getting to class on time is like navigating a maze of construction zones.',
"'studying' often means refreshing Canvas every five minutes to see if the professor posted lecture slides.",
"'I'll just skip this lecture' often turns into a semester-long habit.",
'The libraries are full of students pretending to study but actually napping with their eyes open.',
"The 'campus loop' refers to both the bus route and the endless cycle of trying to find your way around campus.",
'The squirrels have mastered the art of begging for food better than most students during finals week.',
"'going for a run' often means 'running to get food from food trucks'.",
'The struggle to fit all your classes into one schedule is as real as the struggle to fit all your groceries into a mini-fridge.',
];
export default splashText;

3
src/assets/robots.txt Normal file
View File

@@ -0,0 +1,3 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:

157
src/debug/index.tsx Normal file
View File

@@ -0,0 +1,157 @@
import { DevStore } from '@shared/storage/DevStore';
import React, { useEffect } from 'react';
import { createRoot } from 'react-dom/client';
const manifest = chrome.runtime.getManifest();
/**
* Handles editing the storage for a specific area.
*
* @param {string} areaName - The name of the storage area.
* @returns {Function} - A function that accepts changes and sets them in the storage.
*/
const handleEditStorage = (areaName: 'local' | 'sync' | 'session') => (changes: Record<string, unknown>) => {
chrome.storage[areaName].set(changes);
};
interface JSONEditorProps {
data: unknown;
onChange: ReturnType<typeof handleEditStorage>;
}
function JSONEditor(props: JSONEditorProps) {
const { data, onChange } = props;
const [isEditing, setIsEditing] = React.useState(false);
const [json, setJson] = React.useState(JSON.stringify(data, null, 2));
useEffect(() => {
setJson(JSON.stringify(data, null, 2));
}, [data]);
const handleChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
setJson(e.target.value);
};
const handleSave = () => {
try {
const updates = JSON.parse(json);
onChange(updates);
setIsEditing(false);
} catch (e) {
console.error(e);
// eslint-disable-next-line no-alert
alert('Invalid JSON');
}
};
return (
<div>
{isEditing ? (
<div>
<div style={{ flex: 1, marginBottom: 10, gap: 10, display: 'flex' }}>
<button style={{ color: 'green' }} onClick={handleSave}>
Save
</button>
<button style={{ color: 'red' }} onClick={() => setIsEditing(false)}>
Cancel
</button>
</div>
<textarea style={{ width: '100%', height: '300px' }} value={json} onChange={handleChange} />
</div>
) : (
<div>
<pre onClick={() => setIsEditing(true)}>{json}</pre>
</div>
)}
</div>
);
}
// const PrettyPrintJson = React.memo(({ data }: any) => (
// <div>
// <pre>{JSON.stringify(data, null, 2)}</pre>
// </div>
// ));
function DevDashboard() {
const [localStorage, setLocalStorage] = React.useState<Record<string, unknown>>({});
const [syncStorage, setSyncStorage] = React.useState<Record<string, unknown>>({});
const [sessionStorage, setSessionStorage] = React.useState<Record<string, unknown>>({});
useEffect(() => {
const onVisibilityChange = () => {
DevStore.set('wasDebugTabVisible', document.visibilityState === 'visible');
};
document.addEventListener('visibilitychange', onVisibilityChange);
return () => {
document.removeEventListener('visibilitychange', onVisibilityChange);
};
}, []);
useEffect(() => {
chrome.storage.local.get(null, result => {
setLocalStorage(result);
});
chrome.storage.sync.get(null, result => {
setSyncStorage(result);
});
chrome.storage.session.get(null, result => {
setSessionStorage(result);
});
}, []);
// listen for changes to the chrome storage to update the local storage state displayed in the dashboard
useEffect(() => {
const onChanged = (changes: chrome.storage.StorageChange, areaName: chrome.storage.AreaName) => {
let copy: Record<string, unknown> = {};
if (areaName === 'local') {
copy = { ...localStorage };
} else if (areaName === 'sync') {
copy = { ...syncStorage };
} else if (areaName === 'session') {
copy = { ...sessionStorage };
}
Object.keys(changes).forEach((key: string) => {
copy[key] = changes[key as keyof typeof changes].newValue;
});
if (areaName === 'local') {
setLocalStorage(copy);
}
if (areaName === 'sync') {
setSyncStorage(copy);
}
if (areaName === 'session') {
setSessionStorage(copy);
}
};
chrome.storage.onChanged.addListener(onChanged);
return () => {
chrome.storage.onChanged.removeListener(onChanged);
};
}, [localStorage, syncStorage, sessionStorage]);
return (
<div>
<h1>
{manifest.name} {manifest.version} - {process.env.NODE_ENV}
</h1>
<p>This tab is used for hot reloading and debugging. We will update this tab further in the future.</p>
<h2>Local Storage</h2>
<JSONEditor data={localStorage} onChange={handleEditStorage('local')} />
<h2>Sync Storage</h2>
<JSONEditor data={syncStorage} onChange={handleEditStorage('sync')} />
<h2>Session Storage</h2>
<JSONEditor data={sessionStorage} onChange={handleEditStorage('session')} />
<br />
</div>
);
}
createRoot(document.getElementById('root')!).render(<DevDashboard />);

14
src/global.d.ts vendored Normal file
View File

@@ -0,0 +1,14 @@
declare module '*.jpg' {
const content: string;
export default content;
}
declare module '*.png' {
const content: string;
export default content;
}
declare module '*.json' {
const content: string;
export default content;
}

64
src/manifest.ts Normal file
View File

@@ -0,0 +1,64 @@
import { defineManifest } from '@crxjs/vite-plugin';
import packageJson from '../package.json';
// Convert from Semver (example: 0.1.0-beta6)
const [major, minor, patch, label = '0'] = packageJson.version
// can only contain digits, dots, or dash
.replace(/[^\d.-]+/g, '')
// split into version parts
.split(/[.-]/);
const isBeta = !!process.env.BETA;
const mode = isBeta ? 'beta' : process.env.NODE_ENV;
if (isBeta && process.env.NODE_ENV !== 'production') throw new Error('Cannot have beta non-production build');
// eslint-disable-next-line no-nested-ternary
const nameSuffix = isBeta ? ' (beta)' : mode === 'development' ? ' (dev)' : '';
const HOST_PERMISSIONS: string[] = [
'*://*.utdirect.utexas.edu/apps/registrar/course_schedule/*',
'*://*.utexas.collegescheduler.com/*',
'*://*.catalog.utexas.edu/ribbit/',
'*://*.registrar.utexas.edu/schedules/*',
'*://*.login.utexas.edu/login/*',
'https://utexas.bluera.com/*',
];
const manifest = defineManifest(async () => ({
manifest_version: 3,
name: `${packageJson.displayName ?? packageJson.name}${nameSuffix}`,
version: `${major}.${minor}.${patch}.${label}`,
description: packageJson.description,
options_page: 'src/pages/options/index.html',
background: { service_worker: 'src/pages/background/background.ts' },
permissions: ['storage', 'unlimitedStorage', 'background', 'scripting'],
host_permissions: process.env.MODE === 'development' ? [...HOST_PERMISSIONS, '<all_urls>'] : HOST_PERMISSIONS,
action: {
default_popup: 'src/pages/popup/index.html',
default_icon: `icons/icon_${mode}_32.png`,
},
icons: {
'16': `icons/icon_${mode}_16.png`,
'32': `icons/icon_${mode}_32.png`,
'48': `icons/icon_${mode}_48.png`,
'128': `icons/icon_${mode}_128.png`,
},
content_scripts: [
{
matches: HOST_PERMISSIONS,
js: ['src/pages/content/index.tsx'],
},
],
web_accessible_resources: [
{
resources: ['assets/js/*.js', 'assets/css/*.css', 'assets/img/*'],
matches: ['*://*/*'],
},
],
content_security_policy: {
extension_pages: "script-src 'self' 'wasm-unsafe-eval'; object-src 'self'",
},
}));
export default manifest;

View File

@@ -0,0 +1,59 @@
import type { BACKGROUND_MESSAGES } from '@shared/messages';
import { UserScheduleStore } from '@shared/storage/UserScheduleStore';
import updateBadgeText from '@shared/util/updateBadgeText';
import { MessageListener } from 'chrome-extension-toolkit';
import onInstall from './events/onInstall';
import onServiceWorkerAlive from './events/onServiceWorkerAlive';
import onUpdate from './events/onUpdate';
import browserActionHandler from './handler/browserActionHandler';
import calendarBackgroundHandler from './handler/calendarBackgroundHandler';
import CESHandler from './handler/CESHandler';
import tabManagementHandler from './handler/tabManagementHandler';
import userScheduleHandler from './handler/userScheduleHandler';
onServiceWorkerAlive();
/**
* will be triggered on either install or update
* (will also be triggered on a user's sync'd browsers (on other devices)))
*/
chrome.runtime.onInstalled.addListener(details => {
switch (details.reason) {
case 'install':
onInstall();
break;
case 'update':
onUpdate();
break;
default:
break;
}
});
// initialize the message listener that will listen for messages from the content script
const messageListener = new MessageListener<BACKGROUND_MESSAGES>({
...browserActionHandler,
...tabManagementHandler,
...userScheduleHandler,
...CESHandler,
...calendarBackgroundHandler,
});
messageListener.listen();
UserScheduleStore.listen('schedules', async schedules => {
const index = await UserScheduleStore.get('activeIndex');
const numCourses = schedules.newValue[index]?.courses?.length;
if (!numCourses) return;
updateBadgeText(numCourses);
});
UserScheduleStore.listen('activeIndex', async ({ newValue }) => {
const schedules = await UserScheduleStore.get('schedules');
const numCourses = schedules[newValue]?.courses?.length;
if (!numCourses) return;
updateBadgeText(numCourses);
});

Some files were not shown because too many files have changed in this diff Show More