deleted everything lmao
This commit is contained in:
40
README.md
40
README.md
@@ -1,40 +0,0 @@
|
||||
|
||||
# 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, 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.
|
||||
|
||||
|
||||
- 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.
|
||||
|
||||
- Gets the course description and highlight the important information like prerequisites, restrictions, etc.
|
||||
|
||||
- Shows an aggregate graph of the grade distributions for when the professor taught the class in the past.
|
||||
|
||||
- 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.
|
||||
|
||||
- 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.
|
||||
|
||||
- Display's a weekly schedule based on your saved courses
|
||||
|
||||
<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>
|
||||
|
||||
# 2.0 coming soon....
|
||||
|
||||
@@ -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>
|
||||
@@ -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;
|
||||
}
|
||||
5
css/fullcalendar.min.css
vendored
5
css/fullcalendar.min.css
vendored
File diff suppressed because one or more lines are too long
@@ -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;
|
||||
}
|
||||
563
css/popup.css
563
css/popup.css
@@ -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);
|
||||
}
|
||||
308
css/styles.css
308
css/styles.css
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
theme: jekyll-theme-cayman
|
||||
@@ -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"]
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 19 KiB |
BIN
icons/icon16.png
BIN
icons/icon16.png
Binary file not shown.
|
Before Width: | Height: | Size: 18 KiB |
BIN
icons/icon32.png
BIN
icons/icon32.png
Binary file not shown.
|
Before Width: | Height: | Size: 18 KiB |
BIN
icons/icon48.png
BIN
icons/icon48.png
Binary file not shown.
|
Before Width: | Height: | Size: 17 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 8.5 KiB |
196
js/Template.js
196
js/Template.js
@@ -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">×</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'>►</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>`;
|
||||
}
|
||||
};
|
||||
487
js/background.js
487
js/background.js
@@ -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();
|
||||
}
|
||||
291
js/calendar.js
291
js/calendar.js
@@ -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);
|
||||
}
|
||||
87
js/config.js
87
js/config.js
@@ -1,87 +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;
|
||||
|
||||
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 🥺",
|
||||
"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",
|
||||
];
|
||||
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!";
|
||||
@@ -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);
|
||||
});
|
||||
164
js/import.js
164
js/import.js
@@ -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;
|
||||
}
|
||||
1
js/lib/fullcalendar.min.js
vendored
1
js/lib/fullcalendar.min.js
vendored
File diff suppressed because one or more lines are too long
8737
js/lib/highcharts.js
8737
js/lib/highcharts.js
File diff suppressed because it is too large
Load Diff
6
js/lib/html2canvas.min.js
vendored
6
js/lib/html2canvas.min.js
vendored
File diff suppressed because one or more lines are too long
231
js/lib/ics.min.js
vendored
231
js/lib/ics.min.js
vendored
@@ -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")
|
||||
}
|
||||
};
|
||||
2
js/lib/jquery-3.3.1.min.js
vendored
2
js/lib/jquery-3.3.1.min.js
vendored
File diff suppressed because one or more lines are too long
1
js/lib/jquery.initialize.min.js
vendored
1
js/lib/jquery.initialize.min.js
vendored
@@ -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);
|
||||
1
js/lib/moment.min.js
vendored
1
js/lib/moment.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -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);
|
||||
}
|
||||
437
js/popup.js
437
js/popup.js
@@ -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');
|
||||
}
|
||||
);
|
||||
}
|
||||
242
js/utPlanner.js
242
js/utPlanner.js
@@ -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();
|
||||
}
|
||||
});
|
||||
386
js/util.js
386
js/util.js
@@ -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
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
30
options.html
30
options.html
@@ -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>
|
||||
90
popup.html
90
popup.html
@@ -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>
|
||||
42
todo.md
42
todo.md
@@ -1,42 +0,0 @@
|
||||
# To Do
|
||||
*Last Updated: 11/28/19*
|
||||
|
||||
## Features:
|
||||
- [ ] work on UT Planner & Coursicle
|
||||
- [ ] work on multiple schedules
|
||||
- [ ] polish
|
||||
- [ ] more 'at a glance info'
|
||||
- [ ] Rearrange Courses
|
||||
- [ ] Firefox Support
|
||||
- [ ] RMP (cumulative score for class based off CIS, RMP, grade dist., etc.)
|
||||
- [ ] Conflict indicator ( Possible different section? )
|
||||
- [ ] Compare classes mode
|
||||
|
||||
## Known Bugs:
|
||||
- [ ] Screenshotting calendar on zoomed browser cuts off image.
|
||||
|
||||
## Completed:
|
||||
- [x] unneeded Logout message on individual course pages
|
||||
- [x] RMP not working on individual course pages
|
||||
- [x] Textbook button (amber)
|
||||
- [x] Calendar popup
|
||||
- [x] online classes no times in popup link
|
||||
- [x] load all courses on first pages
|
||||
- [x] Location in popup extra info
|
||||
- [x] Export calendar format
|
||||
- [x] import courses from class schedule screen
|
||||
- [x] all semester's grade distribution
|
||||
- [x] import into and export from UT registration plus
|
||||
- [x] Showing the icon on the flags pages
|
||||
- [x] Easter egg, no course messages
|
||||
- [x] clean upcalendar export and add more options to screen
|
||||
- [x] search by unique number
|
||||
- [x] Fix the update/install bug
|
||||
- [x] Redesign footer menu
|
||||
|
||||
for FIXING DB:
|
||||
|
||||
ALTER TABLE agg ADD semesters
|
||||
|
||||
update agg
|
||||
set semesters = (select group_concat(sem) from grades where agg.prof = grades.prof and agg.course_nbr = grades.course_nbr and agg.dept = grades.dept);
|
||||
Reference in New Issue
Block a user