diff --git a/README.md b/README.md
index 6520c93e..3a353d44 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,8 @@
-# Sriram's Sexy Scheduling Script
-[Try it for yourself on the Chrome Web Store](https://chrome.google.com/webstore/detail/hboadpjkoaieogjimneceaahlppnipaa/publish-accepted?authuser=0&hl=en-US)
+
+# 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.
@@ -18,20 +21,20 @@ This extension tries to streamline most of the unnecessary steps and headaches o
- Display's a weekly schedule based on your saved courses
+
+
×
+
+
+
+
+ More Info
+ Register
+ Remove
+
+
+
`;
+ }
+};
+Template.Popup = class {
+ static list_item(i, list_tile_color, unique, department, number, profname, list_sub_color, line) {
+ return `
+
+
+
+ content_copy
+
+
+ ${department} ${number} with ${profname} (${unique})
+
+
►
+
+
+
+
${line}
+
+ Remove
+ Register
+ More Info
+
+
+ `;
+ }
+
+ static conflict_message(conflict_message) {
+ return `
${conflict_message}>`;
+ }
+
+ static line(line) {
+ let { days, start_time, end_time, location_link, location_full } = line;
+
+ return `${days}:
+ ${start_time} to ${end_time}
+
+ ${location_full}
+
+ `;
+ }
+};
+Template.Import = class {
+ static import_button() {
+ return `${Text.button_text_default} `;
+ }
+
+ static waitlist_import_button() {
+ return `${Text.waitlist_button_text_default} `;
+ }
+
+ static store_waitlist_message() {
+ return `
`;
+ }
+};
+
+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 `
+ ${label_text}
+
+
+ ${button_text}
+
+
`;
+ }
+
+ static contributor_card(username, name, image_url, profile_url) {
+ return `
+
+ ${name ? `
${name}
` : ""}
+
${username}
+
`;
+ }
+};
diff --git a/js/background.js b/js/background.js
index b80ea0f9..b30850f8 100644
--- a/js/background.js
+++ b/js/background.js
@@ -1,230 +1,464 @@
+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) {
- if (request.command == "courseStorage") {
- if (request.action == "add") {
- add(request, sender, response);
- }
- if (request.action == "remove") {
- remove(request, sender, response);
- }
- } else if (request.command == "isSingleConflict") {
- isSingleConflict(request.dtarr, request.unique, response);
- } else if (request.command == "checkConflicts") {
- checkConflicts(response);
- } else if (request.command == "updateStatus") {
- updateStatus();
- } else if (request.command == "alreadyContains") {
- alreadyContains(request.unique, response);
- } else if (request.command == "updateTabs") {
- updateTabs();
- } else if(request.command == "updateCourseList"){
- chrome.tabs.query({}, function (tabs) {
- for (var i = 0; i < tabs.length; i++) {
- chrome.tabs.sendMessage(tabs[i].id, {
- command: "updateCourseList"
- });
- }
- });
- } else {
- const xhr = new XMLHttpRequest();
- const method = request.method ? request.method.toUpperCase() : "GET";
- xhr.open(method, request.url, true);
- xhr.onload = () => response(xhr.responseText);
- xhr.onerror = () => response(xhr.statusText);
- if (method == "POST") {
- xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
- }
- xhr.send(request.data);
- }
- return true;
+ 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" || details.reason == "update") {
- var arr = new Array();
- chrome.storage.sync.set({
- savedCourses: arr
- }, function () {
- console.log('initial course list');
- });
- chrome.storage.sync.set({
- courseConflictHighlight: true
- }, function () {
- console.log('initial highlighting: true');
- });
- }
+ 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);
+ }
+ );
+ }
+ });
+}
+
+// scrape the registrar schedules page for caching the current active semesters
+function getCurrentSemesters() {
+ $.get("https://registrar.utexas.edu/schedules", function (response) {
+ if (response) {
+ htmlToNode(response)
+ .find(".callout2>ul>li>a")
+ .each(function (i) {
+ // only show as many semesters as we want to display
+ if (i < Popup.num_semesters) {
+ let sem_name = $(this).text().trim();
+ if (sem_name != "Course Schedule Archive") {
+ // $("#semesters").append(`
${sem_name} `);
+ current_semesters[sem_name] = "code";
+ $.get($(this).attr("href"), function (response) {
+ if (response) {
+ let response_node = htmlToNode(response);
+ let name = response_node.find(".page-title").text().substring(17).trim();
+ response_node.find(".gobutton>a").each(function () {
+ let link = $(this).attr("href");
+ var sem_num = link.substring(link.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://www.utexasreview.com/api/get_major", function (response) {
+ if (response) {
+ let { majors } = response;
+ let indices = Object.keys(majors);
+ let new_departments = [];
+ for (let i = 0; i < indices.length; i++) {
+ new_departments.push(majors[i].abr);
+ }
+ departments = new_departments;
+ }
+ });
+}
+
+// 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 (var i = 0; i < courses.length; i++) {
- for (var j = i + 1; j < courses.length; j++) {
- if (isConflict(courses[i].datetimearr, courses[j].datetimearr)) {
- console.log("conflict");
- conflicts.push([courses[i], courses[j]]);
- }
- }
- }
- if (conflicts.length == 0) {
- sendResponse({
- isConflict: false
- });
- } else {
- console.log(conflicts);
- sendResponse({
- isConflict: true,
- between: conflicts
- });
- }
- });
+ 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 = false;
- for (var i = 0; i < courses.length; i++) {
- if (isConflict(currdatearr, courses[i].datetimearr)) {
- conflict = true;
- break;
- }
- }
- var contains = false;
- var i = 0;
- while (i < courses.length && !contains) {
- if (courses[i].unique == unique) {
- contains = true;
- }
- i++;
- }
- sendResponse({
- isConflict: conflict,
- alreadyContains: contains
- });
- });
+ 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 currday = adtarr[i][0];
- var currtimes = adtarr[i][1];
- for (var j = 0; j < bdtarr.length; j++) {
- var nextday = bdtarr[j][0];
- var nextimes = bdtarr[j][1];
- if (nextday == currday) {
- if (currtimes[0] < nextimes[1] && currtimes[1] > nextimes[0]) {
- return true;
- }
- }
- }
- }
- return false;
+ 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 -"
- });
- });
+ 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 +"
- });
- });
+ 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)
- });
- });
+ 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 (courses[i].unique == unique) {
- return true;
- }
- i++;
- }
- return false;
+ 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"
- });
- }
- });
+ 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 * 15 // 15 mins
-setInterval(updateStatus, UPDATE_INTERVAL);
+// const UPDATE_INTERVAL = 1000 * 60 * 16;
+// setInterval(updateStatus, UPDATE_INTERVAL);
+// // updateStatus();
-function updateStatus() {
- chrome.storage.sync.get('savedCourses', function (data) {
- var courses = data.savedCourses;
- for (let i = 0; i < courses.length; i++) {
- try {
- let c = courses[i];
- var xhr = new XMLHttpRequest();
- xhr.open("GET", c.link, false);
- xhr.send();
- let result = xhr.responseText;
- let dummy = document.createElement('html');
- dummy.innerHTML = result;
- let newstatus = dummy.querySelector('[data-th="Status"]').textContent;
- let registerlink = dummy.querySelector('td[data-th="Add"] a');
- if(registerlink){
- registerlink = registerlink.getAttribute('href');
- }
- c.registerlink = registerlink;
- c.status = newstatus;
- } catch (e) {
- console.log('Not logged into UT Coursebook. Could not update class statuses.');
- }
- }
- chrome.storage.sync.set({
- savedCourses: courses
- });
- // console.log("updated status' and registerlinks");
- });
-}
\ No newline at end of file
+// 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 = $("
").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();
+}
diff --git a/js/calendar.js b/js/calendar.js
index 01b9bdd9..82f1c451 100644
--- a/js/calendar.js
+++ b/js/calendar.js
@@ -1,296 +1,283 @@
-$(function () {
- const materialColors = ['#4CAF50', '#CDDC39',
- '#FFC107', '#2196F3', '#F57C00', '#9C27B0', '#FF5722', '#673AB7',
- '#FF5252', '#E91E63', '#009688', '#00BCD4',
- '#4E342E', '#424242', '#9E9E9E'
- ];
- const days = new Map([
- ["M", "Monday"],
- ["T", "Tuesday"],
- ["W", "Wednesday"],
- ["TH", "Thursday"],
- ["F", "Friday"]
- ]);
- const fadetime = 150;
- const butdelay = 75;
- $("#calendar").prepend('
');
- // Counter to iterate through material colors to avoid duplicates
- var colorCounter = 0;
- // Each schedule needs to store 'TITLE - START TIME - END TIME - COLOR'
- var classSchedules = [];
- var savedCourses = [];
- var currindex = 0;
- chrome.storage.sync.get("savedCourses", function (data) {
- // Iterate through each saved course and add to 'event'
- savedCourses = data.savedCourses;
- setAllEvents(savedCourses);
+var color_counter = 0;
+var {
+ calendar_fade_time,
+ button_delay
+} = Timing;
- $("#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: classSchedules,
- slotLabelFormat: [
- 'h:mm A' // lower level of text
- ],
- eventRender: function (event, element, view) {
- $(element).css("padding", "5px");
- $(element).css("margin-bottom", "5px");
- },
- eventClick: function (data, event, view) {
- $("#myModal").fadeIn(fadetime);
- $("#colorStrip").css('background-color', data.color);
- currindex = data.index;
- $("#classname").html(`${savedCourses[currindex].coursename}
(${savedCourses[currindex].unique}) `);
- $("#timelines").append(makeLine(savedCourses[currindex].datetimearr));
- var uncapProf = prettifyName(savedCourses[currindex].profname);
- if (uncapProf == "") {
- uncapProf = "Undecided";
- }
- $("#prof").html(`with
${uncapProf} `);
+var saved_courses = [];
+var curr_course = {}
- let status = savedCourses[currindex].status;
-
- let registerlink = savedCourses[currindex].registerlink;
- if (status.includes("closed") || status.includes("cancelled") || !status || !registerlink) {
- $("#register").text("Can't Register").css("background-color", "#FF5722");
- } else if (status.includes("waitlisted")) {
- $("#register").text("Join Waitlist").css("background-color", "#FF9800");
- } else {
- $("#register").text("Register").css("background-color", "#4CAF50");
- }
- var cal = ics();
- for (i in savedCourses) {
- let course = savedCourses[i];
- var dtmap = makeMap(course.datetimearr);
- var timearr = Array.from(dtmap.keys());
- var dayarr = Array.from(dtmap.values());
- console.log(timearr);
- console.log(dayarr);
- for (var i = 0; i < dayarr.length; i++) {
- var place = findLoc(dayarr[i], timearr[i], course.datetimearr);
- var building = place.substring(0, place.search(/\d/) - 1);
- if (building == "") {
- building = "Undecided Location";
- }
- //cal.addEvent(subject, description, location, begin, end, rrule)
- let rrurle = {
- freq: "WEEKLY",
- interval: 1,
- byday: [""]
-
- }
- // cal.addEvent(course.coursename, `with${course.profname}`, building, '', '', '');
- // output += `
${dayarr[i]} : ${timearr[i].split(",")[0]} to ${timearr[i].split(",")[1]}${place}
`;
- }
-
- }
- cal.download();
- }
- });
- });
-
- // When the user clicks on
(x), close the modal
- $(".close").click(() => {
- $("#myModal").fadeOut(fadetime);
- });
- $("#info").click(() => {
- var currLink = savedCourses[currindex].link;
- setTimeout(() => {
- window.open(currLink);
- }, butdelay);
- });
- $("#remove").click(() => {
- setTimeout(() => {
- chrome.runtime.sendMessage({
- command: "courseStorage",
- course: savedCourses[currindex],
- action: "remove"
- }, function (response) {
- $("#myModal").fadeOut(fadetime);
- updateCalendar();
- chrome.tabs.query({}, function (tabs) {
- for (var i = 0; i < tabs.length; i++) {
- chrome.tabs.sendMessage(tabs[i].id, {
- command: "updateCourseList"
- });
- }
- });
- });
- }, butdelay);
- });
- $("#register").click(function () {
- let registerlink = savedCourses[currindex].registerlink;
- let status = savedCourses[currindex].status;
- if (!(status.includes("closed") || status.includes("cancelled") || !status || !registerlink)) {
- setTimeout(() => {
- window.open(registerlink);
- }, butdelay);
- }
- });
-
- /* convert from the dtarr and maek the time lines*/
- function makeLine(datetimearr) {
- $(".time").remove();
- //converted times back
- var output = "";
- var dtmap = makeMap(datetimearr);
- var timearr = Array.from(dtmap.keys());
- var dayarr = Array.from(dtmap.values());
- for (var i = 0; i < dayarr.length; i++) {
- var place = findLoc(dayarr[i], timearr[i], datetimearr);
- var building = place.substring(0, place.search(/\d/) - 1);
- if (building == "") {
- building = "Undecided Location";
- }
- output += `${dayarr[i]} : ${timearr[i].split(",")[0]} to ${timearr[i].split(",")[1]}${place}
`;
- }
- return output;
- }
-
- function makeMap(datetimearr) {
- var dtmap = new Map([]);
- for (var i = 0; i < datetimearr.length; i++) {
- //console.log(datetimearr[i][1][0]);
- 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++) {
- if (dtmap.has(String(datetimearr[i][1]))) {
- dtmap.set(String(datetimearr[i][1]), dtmap.get(String(datetimearr[i][1])) + datetimearr[i][0]);
- } else {
- dtmap.set(String(datetimearr[i][1]), datetimearr[i][0]);
- }
- }
- return dtmap
- }
-
- /*Close Modal when hit escape*/
- $(document).keydown((e) => {
- if (e.keyCode == 27) {
- $("#myModal").fadeOut(fadetime);
- }
- $("#snackbar").attr("class", "");
- });
- // When the user clicks anywhere outside of the modal, close it
- window.onclick = (event) => {
- var modal = document.getElementById("myModal");
- if (event.target == modal) {
- $("#myModal").fadeOut(fadetime);
- }
- }
- //find the location of a class given its days and timearrs.
- function findLoc(day, timearr, datetimearr) {
- for (let i = 0; i < datetimearr.length; i++) {
- var dtl = datetimearr[i];
- // console.log(dtl[1]);
- // console.log(timearr);
- if (day.includes(dtl[0])) {
- if (JSON.stringify(timearr) == JSON.stringify(fixDtl1(dtl[1]))) {
- return dtl[2];
- }
- }
- }
- }
-
- function fixDtl1(dtl1) {
- let output = "";
- for (let i = 0; i < dtl1.length; i++) {
- output += dtl1[i];
- if (i != dtl1.length - 1) {
- output += ",";
- }
- }
- return output;
- }
+$("#calendar").after(Template.Calendar.modal());
+chrome.storage.sync.get("savedCourses", function (data) {
// Iterate through each saved course and add to 'event'
- function setAllEvents(savedCourses) {
- colorCounter = 0;
- classSchedules = [];
- for (let i = 0; i < savedCourses.length; i++) {
- for (let j = 0; j < savedCourses[i].datetimearr.length; j++) {
- let session = savedCourses[i].datetimearr[j]; // One single session for a class
- setEventForSection(session, colorCounter, i);
- }
- colorCounter++;
+ 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} (${unique}) `);
+ buildTimeTitle(datetimearr);
+ $("#prof").html(`with ${capitalizeString(profname)} `);
+ 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(`${output}
{
+ openMoreInfoWithOpenModal(curr_course.link);
+});
- function updateCalendar() {
- chrome.storage.sync.get("savedCourses", function (data) {
- savedCourses = data.savedCourses
- setAllEvents(data.savedCourses);
- // console.log(classSchedules);
- $('#calendar').fullCalendar('removeEventSources');
- $("#calendar").fullCalendar('addEventSource', classSchedules, true);
+
+$("#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);
}
- /* Format the Professor Name */
- function prettifyName(profname) {
- return profname.replace(/\w\S*/g, function (txt) {
- return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
- });
+});
+
+$("#export").click(function () {
+ var cal = ics();
+ var calendarEvents = $('#calendar').fullCalendar('clientEvents');
+ for (i in calendarEvents) {
+ var event = calendarEvents[i];
+ buildICSFile(cal, event);
}
-});
\ No newline at end of file
+ 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();
+ var height = $("#calendar").height();
+ 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);
+}
\ No newline at end of file
diff --git a/js/config.js b/js/config.js
new file mode 100644
index 00000000..b47695ae
--- /dev/null
+++ b/js/config.js
@@ -0,0 +1,87 @@
+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 = "Import to UT Reg +";
+Text.waitlist_button_text_default = "Import Waitlists to UT Reg +";
+Text.button_success = "Courses Saved!";
diff --git a/js/content.js b/js/content.js
deleted file mode 100644
index 2a9ddf85..00000000
--- a/js/content.js
+++ /dev/null
@@ -1,570 +0,0 @@
-var grades;
-var rmpLink;
-var eCISLink;
-var textbookLink;
-var coursename;
-var profname;
-var profinit;
-var uniquenum;
-var profurl;
-var registerlink;
-var department;
-var course_nbr;
-var datetimearr = [];
-var chart;
-var description;
-var status;
-var semesterCode;
-var isIndividual = false;
-
-const days = new Map([
- ["M", "Monday"],
- ["T", "Tuesday"],
- ["W", "Wednesday"],
- ["TH", "Thursday"],
- ["F", "Friday"]
-]);
-const fadetime = 150;
-const butdelay = 75;
-//This extension may be super lit, but you know what's even more lit?
-//Matthew Tran's twitter and insta: @MATTHEWTRANN and @matthew.trann
-$(function () {
- loadDataBase();
- //make heading and modal
- $("table thead th:last-child").after(' Plus ');
- var modhtml = '
× Computer Fluency (C S 302) with Bruce Porter RMP eCIS Textbook Past Syllabi Save Course +
';
- $("#container").prepend(modhtml);
- $("#myModal").prepend("
defaultmessage..
");
- //go through all the rows in the list
- $('table').find('tr').each(function () {
- if (!($(this).find('td').hasClass("course_header")) && $(this).has('th').length == 0) {
- //if a course row, then add the extension button
- $(this).append(`
`);
- }
- });
- //update the conflicts
- update();
- /*Handle the button clicks*/
- $(".distButton").click(function () {
- var row = $(this).closest('tr');
- getCourseInfo(row);
- getDistribution();
- });
-
- $("#saveCourse").click(function () {
- saveCourse();
- });
-
- $("#Syllabi").click(function () {
- setTimeout(function () {
- window.open(`https://utdirect.utexas.edu/apps/student/coursedocs/nlogon/?semester=&department=${department}&course_number=${course_nbr}&course_title=&unique=&instructor_first=&instructor_last=${profname}&course_type=In+Residence&search=Search`);
- }, butdelay);
- });
- $("#rateMyProf").click(function () {
- setTimeout(function () {
- window.open(rmpLink);
- }, butdelay);
- });
- $("#eCIS").click(function () {
- setTimeout(function () {
- window.open(eCISLink);
- }, butdelay);
- });
- $("#textbook").click(function () {
- setTimeout(function () {
- window.open(textbookLink);
- }, butdelay);
- });
- $(document).keydown(function (e) {
- /*Close Modal when hit escape*/
- if (e.keyCode == 27) {
- $("#myModal").fadeOut(fadetime);
- $("#snackbar").attr("class", "");
- } else if (e.keyCode == 13 && $('#myModal').is(':visible')) {
- /*save course when hit enter*/
- saveCourse();
- }
- });
- /*Listen for update mssage coming from popup*/
- chrome.runtime.onMessage.addListener(
- function (request, sender, sendResponse) {
- if (request.command == "updateCourseList") {
- // console.log("hello");
- update();
- }
- });
-});
-
-function saveCourse() {
- var c = new Course(coursename, uniquenum, profname, datetimearr, status, profurl, registerlink);
- chrome.runtime.sendMessage({
- command: "courseStorage",
- course: c,
- action: $("#saveCourse").text().substring(0, $("#saveCourse").text().indexOf(" ")).toLowerCase()
- }, function (response) {
- $("#saveCourse").text(response.label);
- $("#snackbar").text(response.done);
- $("#snackbar").attr("class", "show");
- setTimeout(function () {
- $("#snackbar").attr("class", "");
- }, 3000);
- chrome.runtime.sendMessage({
- command: "updateCourseList"
- }, function () {
- update();
- });
- });
-}
-
-/* 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 update() {
- chrome.storage.sync.get('courseConflictHighlight', function (data) {
- $('table').find('tr').each(function () {
- if (!($(this).find('td').hasClass("course_header")) && $(this).has('th').length == 0) {
- var thisForm = this;
- var uniquenum = $(this).find('td[data-th="Unique"]').text();
- chrome.runtime.sendMessage({
- command: "isSingleConflict",
- dtarr: getDtarr(this),
- unique: uniquenum
- }, function (response) {
- if (response.isConflict && data.courseConflictHighlight) {
- $(thisForm).find('td').each(function () {
- $(this).css('color', '#F44336').css('text-decoration', 'line-through').css('font-weight', 'normal');
- });
- } else {
- $(thisForm).find('td').each(function () {
- $(this).css('color', 'black').css('text-decoration', 'none').css('font-weight', 'normal');
- });
- }
- if (response.alreadyContains) {
- $(thisForm).find('td').each(function () {
- $(this).css('color', '#4CAF50').css('text-decoration', 'none').css('font-weight', 'bold');
- });
- }
- });
- }
- });
- });
-}
-
-/* For a row, get the date-time-array for checking conflicts*/
-function getDtarr(row) {
- var numlines = $(row).find('td[data-th="Days"]>span').length;
- var dtarr = [];
- for (var i = 0; i < numlines; i++) {
- var date = $(row).find('td[data-th="Days"]>span:eq(' + i + ')').text();
- var time = $(row).find('td[data-th="Hour"]>span:eq(' + i + ')').text();
- var place = $(row).find('td[data-th="Room"]>span:eq(' + i + ')').text();
- for (var j = 0; j < date.length; j++) {
- var letter = date.charAt(j);
- var day = "";
- if (letter == "T" && j < date.length - 1 && date.charAt(j + 1) == "H") {
- dtarr.push(["TH", convertTime(time), place]);
- } else {
- if (letter != "H") {
- dtarr.push([letter, convertTime(time), place]);
- }
- }
- }
- }
- return dtarr;
-}
-
-/*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;
-}
-
-/*For a row, get all the course information and add the date-time-lines*/
-function getCourseInfo(row) {
- semesterCode = new URL(window.location.href).pathname.split('/')[4];
- $(".dateTimePlace").remove();
- $('table').find('tr').each(function () {
- if ($(this).find('td').hasClass("course_header")) {
- coursename = $(this).find('td').text() + "";
- }
- if ($(this).is(row)) {
- profurl = $(this).find('td[data-th="Unique"] a').prop('href');
- registerlink = $(this).find('td[data-th="Add"] a').prop('href');
- // console.log(registerlink);
- uniquenum = $(this).find('td[data-th="Unique"]').text();
- status = $(this).find('td[data-th="Status"]').text();
- profname = $(this).find('td[data-th="Instructor"]').text().split(', ')[0];
- profinit = $(this).find('td[data-th="Instructor"]').text().split(', ')[1];
- if (profname.indexOf(" ") == 0) {
- profname = profname.substring(1);
- }
- var numlines = $(this).find('td[data-th="Days"]>span').length;
- datetimearr = [];
- for (var i = 0; i < numlines; i++) {
- var date = $(this).find('td[data-th="Days"]>span:eq(' + i + ')').text();
- var time = $(this).find('td[data-th="Hour"]>span:eq(' + i + ')').text();
- var place = $(this).find('td[data-th="Room"]>span:eq(' + i + ')').text();
- $("#topbuttons").before(`
");
- $("#import").prepend("
defaultmessage..
");
+ 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("
import snackbar..
");
+
$("#import").click(function () {
- $(".tbg").last().find(".tbon>td:first-child").each(function () {
- let unique = $(this).text().replace(/\s/g, '');
- link = `https://utdirect.utexas.edu/apps/registrar/course_schedule/${sem}/${unique}/`;
- getInfo();
- });
- $("#import").text("Courses Saved!").css("background-color", "#4CAF50");
- setTimeout(function () {
- $("#import").html("
Import into UT Reg Plus").css("background-color", "#FF9800");
- }, 1000);
+ search_nodes = waitlist ? $(".tbg").last().find(".tbon>td:first-child") : $("tr>td:first-child");
+ $(search_nodes).each(function () {
+ importCourse($(this));
+ })
+ importButtonAnimation($(this));
});
+ $("#import_waitlist").click(function () {
+ search_nodes = $("tr.tb span:first-child");
+ $(search_nodes).each(function () {
+ importCourse($(this));
+ })
+ importButtonAnimation($(this));
+ });
});
-/*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 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];
-function getInfo(classurl) {
- var xhr = new XMLHttpRequest();
- xhr.open("GET", link, false);
- xhr.send();
- var response = xhr.responseText;
- if (response) {
- var output = "";
- var object = $('
').html(response).contents();
- var c = getCourseObject(object);
- console.log(c);
- chrome.runtime.sendMessage({
- command: "courseStorage",
- course: c,
- action: "add"
- }, function () {
- chrome.runtime.sendMessage({
- command: "updateTabs"
- });
+ 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) {
+ let unique = $(unique_node).text().replace(/\s/g, '');
+ link = `https://utdirect.utexas.edu/apps/registrar/course_schedule/${sem}/${unique}/`;
+ buildAddCourse(link);
+}
+
+
+function buildAddCourse(link) {
+ $.get(link, function (response) {
+ if (response) {
+ let simp_course = buildSimplifiedCourseObject(response, link);
+ chrome.runtime.sendMessage({
+ command: "courseStorage",
+ course: simp_course,
+ action: "add"
+ }, function () {
+ chrome.runtime.sendMessage({
+ command: "updateCourseList"
+ });
+ });
+ }
+ })
+}
+
+
+function buildSimplifiedCourseObject(response, link) {
+ let imported_course = getCourseObject(htmlToNode(response), link);
+ let {
+ full_name,
+ unique,
+ prof_name,
+ individual,
+ status,
+ register
+ } = curr_course;
+ let dtarr = getDayTimeArray(undefined, curr_course);
+
+ 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(object) {
- let coursename = object.find("#details h2").text();
- let uniquenum = object.find('td[data-th="Unique"]').text();
- let profname = object.find("td[data-th='Instructor']").text().split(', ')[0];
- if (profname.indexOf(" ") == 0) {
- profname = profname.substring(1);
+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
+ } = seperateCourseNameParts(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": {}
}
- let datetimearr = getDtarr(object);
- let status = object.find('td[data-th="Status"]').text();
- let indlink = link;
- let registerlink = object.find('td[data-th="Add"] a').prop('href');
- return new Course(coursename, uniquenum, profname, datetimearr, status, indlink, registerlink);
+ return course_info;
}
/* For a row, get the date-time-array for checking conflicts*/
-function getDtarr(object) {
- var numlines = object.find('td[data-th="Days"]>span').length;
- var dtarr = [];
- for (var i = 0; i < numlines; i++) {
- var date = object.find('td[data-th="Days"]>span:eq(' + i + ')').text();
- var time = object.find('td[data-th="Hour"]>span:eq(' + i + ')').text();
- var place = object.find('td[data-th="Room"]>span:eq(' + i + ')').text();
+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++) {
- var letter = date.charAt(j);
- var day = "";
+ let letter = date.charAt(j);
if (letter == "T" && j < date.length - 1 && date.charAt(j + 1) == "H") {
- dtarr.push(["TH", convertTime(time), place]);
+ day_time_array.push(["TH", convertTime(time), place]);
} else {
- if (letter != "H") {
- dtarr.push([letter, convertTime(time), place]);
- }
+ if (letter != "H")
+ day_time_array.push([letter, convertTime(time), place]);
}
}
}
- return dtarr;
+ return day_time_array;
}
-
-/*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;
-}
\ No newline at end of file
diff --git a/js/fullcalendar.min.js b/js/lib/fullcalendar.min.js
similarity index 100%
rename from js/fullcalendar.min.js
rename to js/lib/fullcalendar.min.js
diff --git a/js/highcharts.js b/js/lib/highcharts.js
similarity index 100%
rename from js/highcharts.js
rename to js/lib/highcharts.js
diff --git a/js/lib/html2canvas.min.js b/js/lib/html2canvas.min.js
new file mode 100644
index 00000000..be7d72af
--- /dev/null
+++ b/js/lib/html2canvas.min.js
@@ -0,0 +1,6 @@
+/*!
+ * html2canvas 1.0.0-alpha.11
+ * Copyright (c) 2018 Niklas von Hertzen
+ * Released under MIT License
+ */
+!function(A,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.html2canvas=e():A.html2canvas=e()}(this,function(){return function(A){var e={};function t(r){if(e[r])return e[r].exports;var n=e[r]={i:r,l:!1,exports:{}};return A[r].call(n.exports,n,n.exports,t),n.l=!0,n.exports}return t.m=A,t.c=e,t.d=function(A,e,r){t.o(A,e)||Object.defineProperty(A,e,{configurable:!1,enumerable:!0,get:r})},t.n=function(A){var e=A&&A.__esModule?function(){return A.default}:function(){return A};return t.d(e,"a",e),e},t.o=function(A,e){return Object.prototype.hasOwnProperty.call(A,e)},t.p="",t(t.s=27)}([function(A,e,t){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=function(){return function(A,e){if(Array.isArray(A))return A;if(Symbol.iterator in Object(A))return function(A,e){var t=[],r=!0,n=!1,B=void 0;try{for(var a,s=A[Symbol.iterator]();!(r=(a=s.next()).done)&&(t.push(a.value),!e||t.length!==e);r=!0);}catch(A){n=!0,B=A}finally{try{!r&&s.return&&s.return()}finally{if(n)throw B}}return t}(A,e);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}(),n=function(){function A(A,e){for(var t=0;t4)&&[Number(e[1]),Number(e[2]),Number(e[3]),Number(e[4])]},Q=function(A){return[Math.min(A[0],255),Math.min(A[1],255),Math.min(A[2],255),A.length>3?A[3]:null]},w=function(A){var e=g[A.toLowerCase()];return e||!1},U=function(){function A(e){!function(A,e){if(!(A instanceof e))throw new TypeError("Cannot call a class as a function")}(this,A);var t=Array.isArray(e)?Q(e):a(e)||c(e)||u(e)||w(e)||o(e)||[0,0,0,null],n=r(t,4),B=n[0],s=n[1],i=n[2],l=n[3];this.r=B,this.g=s,this.b=i,this.a=l}return n(A,[{key:"isTransparent",value:function(){return 0===this.a}},{key:"toString",value:function(){return null!==this.a&&1!==this.a?"rgba("+this.r+","+this.g+","+this.b+","+this.a+")":"rgb("+this.r+","+this.g+","+this.b+")"}}]),A}();e.default=U;var g={transparent:[0,0,0,0],aliceblue:[240,248,255,null],antiquewhite:[250,235,215,null],aqua:[0,255,255,null],aquamarine:[127,255,212,null],azure:[240,255,255,null],beige:[245,245,220,null],bisque:[255,228,196,null],black:[0,0,0,null],blanchedalmond:[255,235,205,null],blue:[0,0,255,null],blueviolet:[138,43,226,null],brown:[165,42,42,null],burlywood:[222,184,135,null],cadetblue:[95,158,160,null],chartreuse:[127,255,0,null],chocolate:[210,105,30,null],coral:[255,127,80,null],cornflowerblue:[100,149,237,null],cornsilk:[255,248,220,null],crimson:[220,20,60,null],cyan:[0,255,255,null],darkblue:[0,0,139,null],darkcyan:[0,139,139,null],darkgoldenrod:[184,134,11,null],darkgray:[169,169,169,null],darkgreen:[0,100,0,null],darkgrey:[169,169,169,null],darkkhaki:[189,183,107,null],darkmagenta:[139,0,139,null],darkolivegreen:[85,107,47,null],darkorange:[255,140,0,null],darkorchid:[153,50,204,null],darkred:[139,0,0,null],darksalmon:[233,150,122,null],darkseagreen:[143,188,143,null],darkslateblue:[72,61,139,null],darkslategray:[47,79,79,null],darkslategrey:[47,79,79,null],darkturquoise:[0,206,209,null],darkviolet:[148,0,211,null],deeppink:[255,20,147,null],deepskyblue:[0,191,255,null],dimgray:[105,105,105,null],dimgrey:[105,105,105,null],dodgerblue:[30,144,255,null],firebrick:[178,34,34,null],floralwhite:[255,250,240,null],forestgreen:[34,139,34,null],fuchsia:[255,0,255,null],gainsboro:[220,220,220,null],ghostwhite:[248,248,255,null],gold:[255,215,0,null],goldenrod:[218,165,32,null],gray:[128,128,128,null],green:[0,128,0,null],greenyellow:[173,255,47,null],grey:[128,128,128,null],honeydew:[240,255,240,null],hotpink:[255,105,180,null],indianred:[205,92,92,null],indigo:[75,0,130,null],ivory:[255,255,240,null],khaki:[240,230,140,null],lavender:[230,230,250,null],lavenderblush:[255,240,245,null],lawngreen:[124,252,0,null],lemonchiffon:[255,250,205,null],lightblue:[173,216,230,null],lightcoral:[240,128,128,null],lightcyan:[224,255,255,null],lightgoldenrodyellow:[250,250,210,null],lightgray:[211,211,211,null],lightgreen:[144,238,144,null],lightgrey:[211,211,211,null],lightpink:[255,182,193,null],lightsalmon:[255,160,122,null],lightseagreen:[32,178,170,null],lightskyblue:[135,206,250,null],lightslategray:[119,136,153,null],lightslategrey:[119,136,153,null],lightsteelblue:[176,196,222,null],lightyellow:[255,255,224,null],lime:[0,255,0,null],limegreen:[50,205,50,null],linen:[250,240,230,null],magenta:[255,0,255,null],maroon:[128,0,0,null],mediumaquamarine:[102,205,170,null],mediumblue:[0,0,205,null],mediumorchid:[186,85,211,null],mediumpurple:[147,112,219,null],mediumseagreen:[60,179,113,null],mediumslateblue:[123,104,238,null],mediumspringgreen:[0,250,154,null],mediumturquoise:[72,209,204,null],mediumvioletred:[199,21,133,null],midnightblue:[25,25,112,null],mintcream:[245,255,250,null],mistyrose:[255,228,225,null],moccasin:[255,228,181,null],navajowhite:[255,222,173,null],navy:[0,0,128,null],oldlace:[253,245,230,null],olive:[128,128,0,null],olivedrab:[107,142,35,null],orange:[255,165,0,null],orangered:[255,69,0,null],orchid:[218,112,214,null],palegoldenrod:[238,232,170,null],palegreen:[152,251,152,null],paleturquoise:[175,238,238,null],palevioletred:[219,112,147,null],papayawhip:[255,239,213,null],peachpuff:[255,218,185,null],peru:[205,133,63,null],pink:[255,192,203,null],plum:[221,160,221,null],powderblue:[176,224,230,null],purple:[128,0,128,null],rebeccapurple:[102,51,153,null],red:[255,0,0,null],rosybrown:[188,143,143,null],royalblue:[65,105,225,null],saddlebrown:[139,69,19,null],salmon:[250,128,114,null],sandybrown:[244,164,96,null],seagreen:[46,139,87,null],seashell:[255,245,238,null],sienna:[160,82,45,null],silver:[192,192,192,null],skyblue:[135,206,235,null],slateblue:[106,90,205,null],slategray:[112,128,144,null],slategrey:[112,128,144,null],snow:[255,250,250,null],springgreen:[0,255,127,null],steelblue:[70,130,180,null],tan:[210,180,140,null],teal:[0,128,128,null],thistle:[216,191,216,null],tomato:[255,99,71,null],turquoise:[64,224,208,null],violet:[238,130,238,null],wheat:[245,222,179,null],white:[255,255,255,null],whitesmoke:[245,245,245,null],yellow:[255,255,0,null],yellowgreen:[154,205,50,null]};e.TRANSPARENT=new U([0,0,0,0])},function(A,e,t){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.calculateLengthFromValueWithUnit=e.LENGTH_TYPE=void 0;var r,n=function(){function A(A,e){for(var t=0;t1&&(r/=U,B/=U,a/=U,s/=U,o/=U,l/=U,u/=U,Q/=U);var g=A.width-a,C=A.height-l,d=A.width-o,F=A.height-Q;return{topLeftOuter:r>0||B>0?c(A.left,A.top,r,B,i.TOP_LEFT):new n.default(A.left,A.top),topLeftInner:r>0||B>0?c(A.left+e[3].borderWidth,A.top+e[0].borderWidth,Math.max(0,r-e[3].borderWidth),Math.max(0,B-e[0].borderWidth),i.TOP_LEFT):new n.default(A.left+e[3].borderWidth,A.top+e[0].borderWidth),topRightOuter:a>0||s>0?c(A.left+g,A.top,a,s,i.TOP_RIGHT):new n.default(A.left+A.width,A.top),topRightInner:a>0||s>0?c(A.left+Math.min(g,A.width+e[3].borderWidth),A.top+e[0].borderWidth,g>A.width+e[3].borderWidth?0:a-e[3].borderWidth,s-e[0].borderWidth,i.TOP_RIGHT):new n.default(A.left+A.width-e[1].borderWidth,A.top+e[0].borderWidth),bottomRightOuter:o>0||l>0?c(A.left+d,A.top+C,o,l,i.BOTTOM_RIGHT):new n.default(A.left+A.width,A.top+A.height),bottomRightInner:o>0||l>0?c(A.left+Math.min(d,A.width-e[3].borderWidth),A.top+Math.min(C,A.height+e[0].borderWidth),Math.max(0,o-e[1].borderWidth),l-e[2].borderWidth,i.BOTTOM_RIGHT):new n.default(A.left+A.width-e[1].borderWidth,A.top+A.height-e[2].borderWidth),bottomLeftOuter:u>0||Q>0?c(A.left,A.top+F,u,Q,i.BOTTOM_LEFT):new n.default(A.left,A.top+A.height),bottomLeftInner:u>0||Q>0?c(A.left+e[3].borderWidth,A.top+F,Math.max(0,u-e[3].borderWidth),Q-e[2].borderWidth,i.BOTTOM_LEFT):new n.default(A.left+e[3].borderWidth,A.top+A.height-e[2].borderWidth)}},{TOP_LEFT:0,TOP_RIGHT:1,BOTTOM_RIGHT:2,BOTTOM_LEFT:3}),c=function(A,e,t,r,a){var s=(Math.sqrt(2)-1)/3*4,o=t*s,c=r*s,l=A+t,u=e+r;switch(a){case i.TOP_LEFT:return new B.default(new n.default(A,u),new n.default(A,u-c),new n.default(l-o,e),new n.default(l,e));case i.TOP_RIGHT:return new B.default(new n.default(A,e),new n.default(A+o,e),new n.default(l,u-c),new n.default(l,u));case i.BOTTOM_RIGHT:return new B.default(new n.default(l,e),new n.default(l,e+c),new n.default(A+o,u),new n.default(A,u));case i.BOTTOM_LEFT:default:return new B.default(new n.default(l,u),new n.default(l-o,u),new n.default(A,e+c),new n.default(A,e))}}},function(A,e,t){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r,n=function(){function A(A,e){for(var t=0;t0&&this.style.visibility===I.VISIBILITY.VISIBLE}},{key:"isAbsolutelyPositioned",value:function(){return this.style.position!==f.POSITION.STATIC&&this.style.position!==f.POSITION.RELATIVE}},{key:"isPositioned",value:function(){return this.style.position!==f.POSITION.STATIC}},{key:"isFloating",value:function(){return this.style.float!==u.FLOAT.NONE}},{key:"isRootElement",value:function(){return null===this.parent}},{key:"isTransformed",value:function(){return null!==this.style.transform}},{key:"isPositionedWithZIndex",value:function(){return this.isPositioned()&&!this.style.zIndex.auto}},{key:"isInlineLevel",value:function(){return(0,s.contains)(this.style.display,l.DISPLAY.INLINE)||(0,s.contains)(this.style.display,l.DISPLAY.INLINE_BLOCK)||(0,s.contains)(this.style.display,l.DISPLAY.INLINE_FLEX)||(0,s.contains)(this.style.display,l.DISPLAY.INLINE_GRID)||(0,s.contains)(this.style.display,l.DISPLAY.INLINE_LIST_ITEM)||(0,s.contains)(this.style.display,l.DISPLAY.INLINE_TABLE)}},{key:"isInlineBlockOrInlineTable",value:function(){return(0,s.contains)(this.style.display,l.DISPLAY.INLINE_BLOCK)||(0,s.contains)(this.style.display,l.DISPLAY.INLINE_TABLE)}}]),A}();e.default=S;var L=function(A,e){if(A instanceof A.ownerDocument.defaultView.SVGSVGElement||A instanceof SVGSVGElement){var t=new XMLSerializer;return e.loadImage("data:image/svg+xml,"+encodeURIComponent(t.serializeToString(A)))}switch(A.tagName){case"IMG":var r=A;return e.loadImage(r.currentSrc||r.src);case"CANVAS":var n=A;return e.loadCanvas(n);case"IFRAME":var B=A.getAttribute("data-html2canvas-internal-iframe-key");if(B)return B}return null}},function(A,e,t){"use strict";Object.defineProperty(e,"__esModule",{value:!0});e.contains=function(A,e){return 0!=(A&e)},e.distance=function(A,e){return Math.sqrt(A*A+e*e)},e.copyCSSStyles=function(A,e){for(var t=A.length-1;t>=0;t--){var r=A.item(t);"content"!==r&&e.style.setProperty(r,A.getPropertyValue(r))}return e},e.SMALL_IMAGE="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"},function(A,e,t){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.parseBackgroundImage=e.parseBackground=e.calculateBackgroundRepeatPath=e.calculateBackgroundPosition=e.calculateBackgroungPositioningArea=e.calculateBackgroungPaintingArea=e.calculateGradientBackgroundSize=e.calculateBackgroundSize=e.BACKGROUND_ORIGIN=e.BACKGROUND_CLIP=e.BACKGROUND_SIZE=e.BACKGROUND_REPEAT=void 0;var r=i(t(0)),n=i(t(1)),B=i(t(31)),a=i(t(7)),s=t(2),o=t(17);function i(A){return A&&A.__esModule?A:{default:A}}var c=e.BACKGROUND_REPEAT={REPEAT:0,NO_REPEAT:1,REPEAT_X:2,REPEAT_Y:3},l=e.BACKGROUND_SIZE={AUTO:0,CONTAIN:1,COVER:2,LENGTH:3},u=e.BACKGROUND_CLIP={BORDER_BOX:0,PADDING_BOX:1,CONTENT_BOX:2},Q=e.BACKGROUND_ORIGIN=u,w=function A(e){switch(function(A,e){if(!(A instanceof e))throw new TypeError("Cannot call a class as a function")}(this,A),e){case"contain":this.size=l.CONTAIN;break;case"cover":this.size=l.COVER;break;case"auto":this.size=l.AUTO;break;default:this.value=new n.default(e)}},U=(e.calculateBackgroundSize=function(A,e,t){var r=0,n=0,a=A.size;if(a[0].size===l.CONTAIN||a[0].size===l.COVER){var s=t.width/t.height,o=e.width/e.height;return s0&&(A=n.substr(0,e).toLowerCase(),n=n.substr(e)),"none"!==(n=n.toLowerCase())&&t.push({prefix:A,method:n,args:r})}r=[],n=a=""};return A.split("").forEach(function(A){if(0!==s||!e.test(A)){switch(A){case'"':B?B===A&&(B=null):B=A;break;case"(":if(B)break;if(0===s)return void(s=1);o++;break;case")":if(B)break;if(1===s){if(0===o)return s=0,void i();o--}break;case",":if(B)break;if(0===s)return void i();if(1===s&&0===o&&!n.match(/^url$/i))return r.push(a.trim()),void(a="")}0===s?n+=A:a+=A}}),i(),t}},function(A,e,t){"use strict";Object.defineProperty(e,"__esModule",{value:!0});e.PATH={VECTOR:0,BEZIER_CURVE:1,CIRCLE:2}},function(A,e,t){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=t(6);e.default=function A(e,t){!function(A,e){if(!(A instanceof e))throw new TypeError("Cannot call a class as a function")}(this,A),this.type=r.PATH.VECTOR,this.x=e,this.y=t}},function(A,e,t){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.parseListStyle=e.parseListStyleType=e.LIST_STYLE_TYPE=e.LIST_STYLE_POSITION=void 0;var r=t(5),n=e.LIST_STYLE_POSITION={INSIDE:0,OUTSIDE:1},B=e.LIST_STYLE_TYPE={NONE:-1,DISC:0,CIRCLE:1,SQUARE:2,DECIMAL:3,CJK_DECIMAL:4,DECIMAL_LEADING_ZERO:5,LOWER_ROMAN:6,UPPER_ROMAN:7,LOWER_GREEK:8,LOWER_ALPHA:9,UPPER_ALPHA:10,ARABIC_INDIC:11,ARMENIAN:12,BENGALI:13,CAMBODIAN:14,CJK_EARTHLY_BRANCH:15,CJK_HEAVENLY_STEM:16,CJK_IDEOGRAPHIC:17,DEVANAGARI:18,ETHIOPIC_NUMERIC:19,GEORGIAN:20,GUJARATI:21,GURMUKHI:22,HEBREW:22,HIRAGANA:23,HIRAGANA_IROHA:24,JAPANESE_FORMAL:25,JAPANESE_INFORMAL:26,KANNADA:27,KATAKANA:28,KATAKANA_IROHA:29,KHMER:30,KOREAN_HANGUL_FORMAL:31,KOREAN_HANJA_FORMAL:32,KOREAN_HANJA_INFORMAL:33,LAO:34,LOWER_ARMENIAN:35,MALAYALAM:36,MONGOLIAN:37,MYANMAR:38,ORIYA:39,PERSIAN:40,SIMP_CHINESE_FORMAL:41,SIMP_CHINESE_INFORMAL:42,TAMIL:43,TELUGU:44,THAI:45,TIBETAN:46,TRAD_CHINESE_FORMAL:47,TRAD_CHINESE_INFORMAL:48,UPPER_ARMENIAN:49,DISCLOSURE_OPEN:50,DISCLOSURE_CLOSED:51},a=e.parseListStyleType=function(A){switch(A){case"disc":return B.DISC;case"circle":return B.CIRCLE;case"square":return B.SQUARE;case"decimal":return B.DECIMAL;case"cjk-decimal":return B.CJK_DECIMAL;case"decimal-leading-zero":return B.DECIMAL_LEADING_ZERO;case"lower-roman":return B.LOWER_ROMAN;case"upper-roman":return B.UPPER_ROMAN;case"lower-greek":return B.LOWER_GREEK;case"lower-alpha":return B.LOWER_ALPHA;case"upper-alpha":return B.UPPER_ALPHA;case"arabic-indic":return B.ARABIC_INDIC;case"armenian":return B.ARMENIAN;case"bengali":return B.BENGALI;case"cambodian":return B.CAMBODIAN;case"cjk-earthly-branch":return B.CJK_EARTHLY_BRANCH;case"cjk-heavenly-stem":return B.CJK_HEAVENLY_STEM;case"cjk-ideographic":return B.CJK_IDEOGRAPHIC;case"devanagari":return B.DEVANAGARI;case"ethiopic-numeric":return B.ETHIOPIC_NUMERIC;case"georgian":return B.GEORGIAN;case"gujarati":return B.GUJARATI;case"gurmukhi":return B.GURMUKHI;case"hebrew":return B.HEBREW;case"hiragana":return B.HIRAGANA;case"hiragana-iroha":return B.HIRAGANA_IROHA;case"japanese-formal":return B.JAPANESE_FORMAL;case"japanese-informal":return B.JAPANESE_INFORMAL;case"kannada":return B.KANNADA;case"katakana":return B.KATAKANA;case"katakana-iroha":return B.KATAKANA_IROHA;case"khmer":return B.KHMER;case"korean-hangul-formal":return B.KOREAN_HANGUL_FORMAL;case"korean-hanja-formal":return B.KOREAN_HANJA_FORMAL;case"korean-hanja-informal":return B.KOREAN_HANJA_INFORMAL;case"lao":return B.LAO;case"lower-armenian":return B.LOWER_ARMENIAN;case"malayalam":return B.MALAYALAM;case"mongolian":return B.MONGOLIAN;case"myanmar":return B.MYANMAR;case"oriya":return B.ORIYA;case"persian":return B.PERSIAN;case"simp-chinese-formal":return B.SIMP_CHINESE_FORMAL;case"simp-chinese-informal":return B.SIMP_CHINESE_INFORMAL;case"tamil":return B.TAMIL;case"telugu":return B.TELUGU;case"thai":return B.THAI;case"tibetan":return B.TIBETAN;case"trad-chinese-formal":return B.TRAD_CHINESE_FORMAL;case"trad-chinese-informal":return B.TRAD_CHINESE_INFORMAL;case"upper-armenian":return B.UPPER_ARMENIAN;case"disclosure-open":return B.DISCLOSURE_OPEN;case"disclosure-closed":return B.DISCLOSURE_CLOSED;case"none":default:return B.NONE}},s=(e.parseListStyle=function(A){var e=(0,r.parseBackgroundImage)(A.getPropertyValue("list-style-image"));return{listStyleType:a(A.getPropertyValue("list-style-type")),listStyleImage:e.length?e[0]:null,listStylePosition:s(A.getPropertyValue("list-style-position"))}},function(A){switch(A){case"inside":return n.INSIDE;case"outside":default:return n.OUTSIDE}})},function(A,e,t){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=function(){function A(A,e){for(var t=0;t0?e+t.toUpperCase():A}},function(A,e,t){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=t(23),n=function(A){return 0===A[0]&&255===A[1]&&0===A[2]&&255===A[3]},B={get SUPPORT_RANGE_BOUNDS(){var A=function(A){if(A.createRange){var e=A.createRange();if(e.getBoundingClientRect){var t=A.createElement("boundtest");t.style.height="123px",t.style.display="block",A.body.appendChild(t),e.selectNode(t);var r=e.getBoundingClientRect(),n=Math.round(r.height);if(A.body.removeChild(t),123===n)return!0}}return!1}(document);return Object.defineProperty(B,"SUPPORT_RANGE_BOUNDS",{value:A}),A},get SUPPORT_SVG_DRAWING(){var A=function(A){var e=new Image,t=A.createElement("canvas"),r=t.getContext("2d");e.src="data:image/svg+xml, ";try{r.drawImage(e,0,0),t.toDataURL()}catch(A){return!1}return!0}(document);return Object.defineProperty(B,"SUPPORT_SVG_DRAWING",{value:A}),A},get SUPPORT_BASE64_DRAWING(){return function(A){var e=function(A,e){var t=new Image,r=A.createElement("canvas"),n=r.getContext("2d");return new Promise(function(A){t.src=e;var B=function(){try{n.drawImage(t,0,0),r.toDataURL()}catch(e){return A(!1)}return A(!0)};t.onload=B,t.onerror=function(){return A(!1)},!0===t.complete&&setTimeout(function(){B()},500)})}(document,A);return Object.defineProperty(B,"SUPPORT_BASE64_DRAWING",{value:function(){return e}}),e}},get SUPPORT_FOREIGNOBJECT_DRAWING(){var A="function"==typeof Array.from&&"function"==typeof window.fetch?function(A){var e=A.createElement("canvas");e.width=100,e.height=100;var t=e.getContext("2d");t.fillStyle="rgb(0, 255, 0)",t.fillRect(0,0,100,100);var B=new Image,a=e.toDataURL();B.src=a;var s=(0,r.createForeignObjectSVG)(100,100,0,0,B);return t.fillStyle="red",t.fillRect(0,0,100,100),(0,r.loadSerializedSVG)(s).then(function(e){t.drawImage(e,0,0);var B=t.getImageData(0,0,100,100).data;t.fillStyle="red",t.fillRect(0,0,100,100);var s=A.createElement("div");return s.style.backgroundImage="url("+a+")",s.style.height="100px",n(B)?(0,r.loadSerializedSVG)((0,r.createForeignObjectSVG)(100,100,0,0,s)):Promise.reject(!1)}).then(function(A){return t.drawImage(A,0,0),n(t.getImageData(0,0,100,100).data)}).catch(function(A){return!1})}(document):Promise.resolve(!1);return Object.defineProperty(B,"SUPPORT_FOREIGNOBJECT_DRAWING",{value:A}),A},get SUPPORT_CORS_IMAGES(){var A=void 0!==(new Image).crossOrigin;return Object.defineProperty(B,"SUPPORT_CORS_IMAGES",{value:A}),A},get SUPPORT_RESPONSE_TYPE(){var A="string"==typeof(new XMLHttpRequest).responseType;return Object.defineProperty(B,"SUPPORT_RESPONSE_TYPE",{value:A}),A},get SUPPORT_CORS_XHR(){var A="withCredentials"in new XMLHttpRequest;return Object.defineProperty(B,"SUPPORT_CORS_XHR",{value:A}),A}};e.default=B},function(A,e,t){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.parseTextDecoration=e.TEXT_DECORATION_LINE=e.TEXT_DECORATION=e.TEXT_DECORATION_STYLE=void 0;var r,n=t(0),B=(r=n)&&r.__esModule?r:{default:r};var a=e.TEXT_DECORATION_STYLE={SOLID:0,DOUBLE:1,DOTTED:2,DASHED:3,WAVY:4},s=e.TEXT_DECORATION={NONE:null},o=e.TEXT_DECORATION_LINE={UNDERLINE:1,OVERLINE:2,LINE_THROUGH:3,BLINK:4},i=function(A){switch(A){case"underline":return o.UNDERLINE;case"overline":return o.OVERLINE;case"line-through":return o.LINE_THROUGH}return o.BLINK};e.parseTextDecoration=function(A){var e,t="none"===(e=A.textDecorationLine?A.textDecorationLine:A.textDecoration)?null:e.split(" ").map(i);return null===t?s.NONE:{textDecorationLine:t,textDecorationColor:A.textDecorationColor?new B.default(A.textDecorationColor):null,textDecorationStyle:function(A){switch(A){case"double":return a.DOUBLE;case"dotted":return a.DOTTED;case"dashed":return a.DASHED;case"wavy":return a.WAVY}return a.SOLID}(A.textDecorationStyle)}}},function(A,e,t){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.parseBorder=e.BORDER_SIDES=e.BORDER_STYLE=void 0;var r,n=t(0),B=(r=n)&&r.__esModule?r:{default:r};var a=e.BORDER_STYLE={NONE:0,SOLID:1},s=e.BORDER_SIDES={TOP:0,RIGHT:1,BOTTOM:2,LEFT:3},o=Object.keys(s).map(function(A){return A.toLowerCase()});e.parseBorder=function(A){return o.map(function(e){var t=new B.default(A.getPropertyValue("border-"+e+"-color")),r=function(A){switch(A){case"none":return a.NONE}return a.SOLID}(A.getPropertyValue("border-"+e+"-style")),n=parseFloat(A.getPropertyValue("border-"+e+"-width"));return{borderColor:t,borderStyle:r,borderWidth:isNaN(n)?0:n}})}},function(A,e,t){"use strict";Object.defineProperty(e,"__esModule",{value:!0});e.toCodePoints=function(A){for(var e=[],t=0,r=A.length;t=55296&&n<=56319&&t>10),n%1024+56320)),(t+1===A||e.length>16384)&&(r+=String.fromCharCode.apply(String,e),e.length=0)}return r};for(var r="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",n="undefined"==typeof Uint8Array?[]:new Uint8Array(256),B=0;B>4,l[B++]=(15&s)<<4|o>>2,l[B++]=(3&o)<<6|63&i;return c},e.polyUint16Array=function(A){for(var e=A.length,t=[],r=0;rt?F(A,n,B.length>0):r.integers.reduce(function(e,t,n){for(;A>=t;)A-=t,e+=r.values[n];return e},"")+B},U=function(A,e,t,r){var n="";do{t||A--,n=r(A)+n,A/=e}while(A*e>=e);return n},g=function(A,e,t,r,n){var B=t-e+1;return(A<0?"-":"")+(U(Math.abs(A),B,r,function(A){return(0,s.fromCodePoint)(Math.floor(A%B)+e)})+n)},C=function(A,e){var t=arguments.length>2&&void 0!==arguments[2]?arguments[2]:". ",r=e.length;return U(Math.abs(A),r,!1,function(A){return e[Math.floor(A%r)]})+t},d=function(A,e,t,n,B,s){if(A<-9999||A>9999)return F(A,a.LIST_STYLE_TYPE.CJK_DECIMAL,B.length>0);var o=Math.abs(A),i=B;if(0===o)return e[0]+i;for(var c=0;o>0&&c<=4;c++){var l=o%10;0===l&&(0,r.contains)(s,1)&&""!==i?i=e[l]+i:l>1||1===l&&0===c||1===l&&1===c&&(0,r.contains)(s,2)||1===l&&1===c&&(0,r.contains)(s,4)&&A>100||1===l&&c>1&&(0,r.contains)(s,8)?i=e[l]+(c>0?t[c-1]:"")+i:1===l&&c>0&&(i=t[c-1]+i),o=Math.floor(o/10)}return(A<0?n:"")+i},F=e.createCounterText=function(A,e,t){var r=t?". ":"",n=t?"、":"",B=t?", ":"";switch(e){case a.LIST_STYLE_TYPE.DISC:return"•";case a.LIST_STYLE_TYPE.CIRCLE:return"◦";case a.LIST_STYLE_TYPE.SQUARE:return"◾";case a.LIST_STYLE_TYPE.DECIMAL_LEADING_ZERO:var s=g(A,48,57,!0,r);return s.length<4?"0"+s:s;case a.LIST_STYLE_TYPE.CJK_DECIMAL:return C(A,"〇一二三四五六七八九",n);case a.LIST_STYLE_TYPE.LOWER_ROMAN:return w(A,1,3999,c,a.LIST_STYLE_TYPE.DECIMAL,r).toLowerCase();case a.LIST_STYLE_TYPE.UPPER_ROMAN:return w(A,1,3999,c,a.LIST_STYLE_TYPE.DECIMAL,r);case a.LIST_STYLE_TYPE.LOWER_GREEK:return g(A,945,969,!1,r);case a.LIST_STYLE_TYPE.LOWER_ALPHA:return g(A,97,122,!1,r);case a.LIST_STYLE_TYPE.UPPER_ALPHA:return g(A,65,90,!1,r);case a.LIST_STYLE_TYPE.ARABIC_INDIC:return g(A,1632,1641,!0,r);case a.LIST_STYLE_TYPE.ARMENIAN:case a.LIST_STYLE_TYPE.UPPER_ARMENIAN:return w(A,1,9999,l,a.LIST_STYLE_TYPE.DECIMAL,r);case a.LIST_STYLE_TYPE.LOWER_ARMENIAN:return w(A,1,9999,l,a.LIST_STYLE_TYPE.DECIMAL,r).toLowerCase();case a.LIST_STYLE_TYPE.BENGALI:return g(A,2534,2543,!0,r);case a.LIST_STYLE_TYPE.CAMBODIAN:case a.LIST_STYLE_TYPE.KHMER:return g(A,6112,6121,!0,r);case a.LIST_STYLE_TYPE.CJK_EARTHLY_BRANCH:return C(A,"子丑寅卯辰巳午未申酉戌亥",n);case a.LIST_STYLE_TYPE.CJK_HEAVENLY_STEM:return C(A,"甲乙丙丁戊己庚辛壬癸",n);case a.LIST_STYLE_TYPE.CJK_IDEOGRAPHIC:case a.LIST_STYLE_TYPE.TRAD_CHINESE_INFORMAL:return d(A,"零一二三四五六七八九","十百千萬","負",n,14);case a.LIST_STYLE_TYPE.TRAD_CHINESE_FORMAL:return d(A,"零壹貳參肆伍陸柒捌玖","拾佰仟萬","負",n,15);case a.LIST_STYLE_TYPE.SIMP_CHINESE_INFORMAL:return d(A,"零一二三四五六七八九","十百千萬","负",n,14);case a.LIST_STYLE_TYPE.SIMP_CHINESE_FORMAL:return d(A,"零壹贰叁肆伍陆柒捌玖","拾佰仟萬","负",n,15);case a.LIST_STYLE_TYPE.JAPANESE_INFORMAL:return d(A,"〇一二三四五六七八九","十百千万","マイナス",n,0);case a.LIST_STYLE_TYPE.JAPANESE_FORMAL:return d(A,"零壱弐参四伍六七八九","拾百千万","マイナス",n,7);case a.LIST_STYLE_TYPE.KOREAN_HANGUL_FORMAL:return d(A,"영일이삼사오육칠팔구","십백천만","마이너스 ",B,7);case a.LIST_STYLE_TYPE.KOREAN_HANJA_INFORMAL:return d(A,"零一二三四五六七八九","十百千萬","마이너스 ",B,0);case a.LIST_STYLE_TYPE.KOREAN_HANJA_FORMAL:return d(A,"零壹貳參四五六七八九","拾百千","마이너스 ",B,7);case a.LIST_STYLE_TYPE.DEVANAGARI:return g(A,2406,2415,!0,r);case a.LIST_STYLE_TYPE.GEORGIAN:return w(A,1,19999,Q,a.LIST_STYLE_TYPE.DECIMAL,r);case a.LIST_STYLE_TYPE.GUJARATI:return g(A,2790,2799,!0,r);case a.LIST_STYLE_TYPE.GURMUKHI:return g(A,2662,2671,!0,r);case a.LIST_STYLE_TYPE.HEBREW:return w(A,1,10999,u,a.LIST_STYLE_TYPE.DECIMAL,r);case a.LIST_STYLE_TYPE.HIRAGANA:return C(A,"あいうえおかきくけこさしすせそたちつてとなにぬねのはひふへほまみむめもやゆよらりるれろわゐゑをん");case a.LIST_STYLE_TYPE.HIRAGANA_IROHA:return C(A,"いろはにほへとちりぬるをわかよたれそつねならむうゐのおくやまけふこえてあさきゆめみしゑひもせす");case a.LIST_STYLE_TYPE.KANNADA:return g(A,3302,3311,!0,r);case a.LIST_STYLE_TYPE.KATAKANA:return C(A,"アイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワヰヱヲン",n);case a.LIST_STYLE_TYPE.KATAKANA_IROHA:return C(A,"イロハニホヘトチリヌルヲワカヨタレソツネナラムウヰノオクヤマケフコエテアサキユメミシヱヒモセス",n);case a.LIST_STYLE_TYPE.LAO:return g(A,3792,3801,!0,r);case a.LIST_STYLE_TYPE.MONGOLIAN:return g(A,6160,6169,!0,r);case a.LIST_STYLE_TYPE.MYANMAR:return g(A,4160,4169,!0,r);case a.LIST_STYLE_TYPE.ORIYA:return g(A,2918,2927,!0,r);case a.LIST_STYLE_TYPE.PERSIAN:return g(A,1776,1785,!0,r);case a.LIST_STYLE_TYPE.TAMIL:return g(A,3046,3055,!0,r);case a.LIST_STYLE_TYPE.TELUGU:return g(A,3174,3183,!0,r);case a.LIST_STYLE_TYPE.THAI:return g(A,3664,3673,!0,r);case a.LIST_STYLE_TYPE.TIBETAN:return g(A,3872,3881,!0,r);case a.LIST_STYLE_TYPE.DECIMAL:default:return g(A,48,57,!0,r)}}},function(A,e,t){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=function(){function A(A,e){for(var t=0;tA.height?(A.left+=(A.width-A.height)/2,A.width=A.height):A.width0&&B){var a=e.ownerDocument.createElement("html2canvaswrapper");(0,c.copyCSSStyles)(e.ownerDocument.defaultView.getComputedStyle(e,null),a),a.style.position="absolute",a.style.left=t.bounds.left+"px",a.style.top=t.bounds.top+"px",n||(a.style.whiteSpace="nowrap");var s=e.ownerDocument.createTextNode(A);a.appendChild(s),B.appendChild(a),t.childNodes.push(r.default.fromTextNode(s,t)),B.removeChild(a)}}),h=function(A){var e="password"===A.type?new Array(A.value.length+1).join("•"):A.value;return 0===e.length?A.placeholder||"":e}},function(A,e,t){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.parseTextBounds=e.TextBounds=void 0;var r,n=t(2),B=t(11),a=t(10),s=(r=a)&&r.__esModule?r:{default:r},o=t(24);var i=e.TextBounds=function A(e,t){!function(A,e){if(!(A instanceof e))throw new TypeError("Cannot call a class as a function")}(this,A),this.text=e,this.bounds=t},c=(e.parseTextBounds=function(A,e,t){for(var r=0!==e.style.letterSpacing?(0,o.toCodePoints)(A).map(function(A){return(0,o.fromCodePoint)(A)}):(0,o.breakWords)(A,e),n=r.length,a=t.parentNode?t.parentNode.ownerDocument.defaultView:null,u=a?a.pageXOffset:0,Q=a?a.pageYOffset:0,w=[],U=0,g=0;g0)if(s.default.SUPPORT_RANGE_BOUNDS)w.push(new i(C,l(t,U,C.length,u,Q)));else{var d=t.splitText(C.length);w.push(new i(C,c(t,u,Q))),t=d}else s.default.SUPPORT_RANGE_BOUNDS||(t=t.splitText(C.length));U+=C.length}return w},function(A,e,t){var r=A.ownerDocument.createElement("html2canvaswrapper");r.appendChild(A.cloneNode(!0));var B=A.parentNode;if(B){B.replaceChild(r,A);var a=(0,n.parseBounds)(r,e,t);return r.firstChild&&B.replaceChild(r.firstChild,r),a}return new n.Bounds(0,0,0,0)}),l=function(A,e,t,r,B){var a=A.ownerDocument.createRange();return a.setStart(A,e),a.setEnd(A,e+t),n.Bounds.fromClientRect(a.getBoundingClientRect(),r,B)}},function(A,e,t){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=function(){function A(A,e){for(var t=0;t0&&t.childNodes.push(B.default.fromTextNode(g,t));else if(g instanceof C.HTMLElement||g instanceof HTMLElement||C.parent&&g instanceof C.parent.HTMLElement){if(-1===c.indexOf(g.nodeName)){var d=new n.default(g,t,l,w++);if(d.isVisible()){"INPUT"===g.tagName?(0,a.inlineInputElement)(g,d):"TEXTAREA"===g.tagName?(0,a.inlineTextAreaElement)(g,d):"SELECT"===g.tagName?(0,a.inlineSelectElement)(g,d):d.style.listStyle&&d.style.listStyle.listStyleType!==o.LIST_STYLE_TYPE.NONE&&(0,s.inlineListItemElement)(g,d,l);var F="TEXTAREA"!==g.tagName,E=u(d,g);if(E||Q(d)){var f=E||d.isPositioned()?i.getRealParentStackingContext():i,h=new r.default(d,f,E);f.contexts.push(h),F&&A(g,d,h,l,w)}else i.children.push(d),F&&A(g,d,i,l,w)}}}else if(g instanceof C.SVGSVGElement||g instanceof SVGSVGElement||C.parent&&g instanceof C.parent.SVGSVGElement){var H=new n.default(g,t,l,w++),p=u(H,g);if(p||Q(H)){var N=p||H.isPositioned()?i.getRealParentStackingContext():i,I=new r.default(H,N,p);N.contexts.push(I)}else i.children.push(H)}}},u=function(A,e){return A.isRootElement()||A.isPositionedWithZIndex()||A.style.opacity<1||A.isTransformed()||w(A,e)},Q=function(A){return A.isPositioned()||A.isFloating()},w=function(A,e){return"BODY"===e.nodeName&&A.parent instanceof n.default&&A.parent.style.background.backgroundColor.isTransparent()}},function(A,e,t){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r,n=function(){function A(A,e){for(var t=0;t1&&void 0!==arguments[1]?arguments[1]:"strict",t=[],r=[],n=[];return A.forEach(function(A,B){var a=O.get(A);if(a>c?(n.push(!0),a-=c):n.push(!1),-1!==["normal","auto","loose"].indexOf(e)&&-1!==[8208,8211,12316,12448].indexOf(A))return r.push(B),t.push(16);if(4===a||11===a){if(0===B)return r.push(B),t.push(N);var s=t[B-1];return-1===x.indexOf(s)?(r.push(r[B-1]),t.push(s)):(r.push(B),t.push(N))}return r.push(B),31===a?t.push("strict"===e?d:v):a===L?t.push(N):29===a?t.push(N):43===a?A>=131072&&A<=196605||A>=196608&&A<=262141?t.push(v):t.push(N):void t.push(a)}),[r,t,n]},G=function(A,e,t,r){var n=r[t];if(Array.isArray(A)?-1!==A.indexOf(n):A===n)for(var B=t;B<=r.length;){var a=r[++B];if(a===e)return!0;if(a!==l)break}if(n===l)for(var s=t;s>0;){var o=r[--s];if(Array.isArray(A)?-1!==A.indexOf(o):A===o)for(var i=t;i<=r.length;){var c=r[++i];if(c===e)return!0;if(c!==l)break}if(o!==l)break}return!1},Y=function(A,e){for(var t=A;t>=0;){var r=e[t];if(r!==l)return r;t--}return 0},W=function(A,e,t,r,n){if(0===t[r])return D;var B=r-1;if(Array.isArray(n)&&!0===n[B])return D;var a=B-1,s=B+1,o=e[B],i=a>=0?e[a]:0,c=e[s];if(2===o&&3===c)return D;if(-1!==P.indexOf(o))return _;if(-1!==P.indexOf(c))return D;if(-1!==X.indexOf(c))return D;if(8===Y(B,e))return M;if(11===O.get(A[B])&&(c===v||c===I||c===K))return D;if(7===o||7===c)return D;if(9===o)return D;if(-1===[l,u,Q].indexOf(o)&&9===c)return D;if(-1!==[w,U,g,E,p].indexOf(c))return D;if(Y(B,e)===F)return D;if(G(23,F,B,e))return D;if(G([w,U],d,B,e))return D;if(G(12,12,B,e))return D;if(o===l)return M;if(23===o||23===c)return D;if(16===c||16===o)return M;if(-1!==[u,Q,d].indexOf(c)||14===o)return D;if(36===i&&-1!==k.indexOf(o))return D;if(o===p&&36===c)return D;if(c===C&&-1!==R.concat(C,g,f,v,I,K).indexOf(o))return D;if(-1!==R.indexOf(c)&&o===f||-1!==R.indexOf(o)&&c===f)return D;if(o===H&&-1!==[v,I,K].indexOf(c)||-1!==[v,I,K].indexOf(o)&&c===h)return D;if(-1!==R.indexOf(o)&&-1!==z.indexOf(c)||-1!==z.indexOf(o)&&-1!==R.indexOf(c))return D;if(-1!==[H,h].indexOf(o)&&(c===f||-1!==[F,Q].indexOf(c)&&e[s+1]===f)||-1!==[F,Q].indexOf(o)&&c===f||o===f&&-1!==[f,p,E].indexOf(c))return D;if(-1!==[f,p,E,w,U].indexOf(c))for(var N=B;N>=0;){var L=e[N];if(L===f)return D;if(-1===[p,E].indexOf(L))break;N--}if(-1!==[H,h].indexOf(c))for(var x=-1!==[w,U].indexOf(o)?a:B;x>=0;){var J=e[x];if(J===f)return D;if(-1===[p,E].indexOf(J))break;x--}if(y===o&&-1!==[y,b,T,m].indexOf(c)||-1!==[b,T].indexOf(o)&&-1!==[b,S].indexOf(c)||-1!==[S,m].indexOf(o)&&c===S)return D;if(-1!==V.indexOf(o)&&-1!==[C,h].indexOf(c)||-1!==V.indexOf(c)&&o===H)return D;if(-1!==R.indexOf(o)&&-1!==R.indexOf(c))return D;if(o===E&&-1!==R.indexOf(c))return D;if(-1!==R.concat(f).indexOf(o)&&c===F||-1!==R.concat(f).indexOf(c)&&o===U)return D;if(41===o&&41===c){for(var W=t[B],j=1;W>0&&41===e[--W];)j++;if(j%2!=0)return D}return o===I&&c===K?D:M},j=(e.lineBreakAtIndex=function(A,e){if(0===e)return D;if(e>=A.length)return _;var t=J(A),r=B(t,2),n=r[0],a=r[1];return W(A,a,n,e)},function(A,e){e||(e={lineBreak:"normal",wordBreak:"normal"});var t=J(A,e.lineBreak),r=B(t,3),n=r[0],a=r[1],s=r[2];return"break-all"!==e.wordBreak&&"break-word"!==e.wordBreak||(a=a.map(function(A){return-1!==[f,N,L].indexOf(A)?v:A})),[n,a,"keep-all"===e.wordBreak?s.map(function(e,t){return e&&A[t]>=19968&&A[t]<=40959}):null]}),q=(e.inlineBreakOpportunities=function(A,e){var t=(0,i.toCodePoints)(A),r=D,n=j(t,e),a=B(n,3),s=a[0],o=a[1],c=a[2];return t.forEach(function(A,e){r+=(0,i.fromCodePoint)(A)+(e>=t.length-1?_:W(t,o,s,e+1,c))}),r},function(){function A(e,t,r,n){!function(A,e){if(!(A instanceof e))throw new TypeError("Cannot call a class as a function")}(this,A),this._codePoints=e,this.required=t===_,this.start=r,this.end=n}return n(A,[{key:"slice",value:function(){return i.fromCodePoint.apply(void 0,function(A){if(Array.isArray(A)){for(var e=0,t=Array(A.length);e=c)return{done:!0};for(var A=D;u>B,c=e.UTRIE2_DATA_BLOCK_LENGTH=1<>B,Q=e.UTRIE2_INDEX_2_BMP_LENGTH=i+u,w=e.UTRIE2_UTF8_2B_INDEX_2_OFFSET=Q,U=e.UTRIE2_UTF8_2B_INDEX_2_LENGTH=32,g=e.UTRIE2_INDEX_1_OFFSET=w+U,C=e.UTRIE2_OMITTED_BMP_INDEX_1_LENGTH=65536>>a,d=e.UTRIE2_INDEX_2_BLOCK_LENGTH=1<=0){if(A<55296||A>56319&&A<=65535)return e=((e=this.index[A>>B])<>B)])<>a),e=this.index[e],e+=A>>B&F,e=((e=this.index[e])<0?t.width:r.width,B="number"==typeof t.height&&t.height>0?t.height:r.height;n>0&&B>0&&e.target.clip([(0,a.calculatePaddingBoxPath)(A.curvedBounds)],function(){e.target.drawImage(t,new a.Bounds(0,0,n,B),r)})}}},r=A.getClipPaths();r.length?this.target.clip(r,t):t()}},{key:"renderNodeBackgroundAndBorders",value:function(A){var e=this,t=!A.style.background.backgroundColor.isTransparent()||A.style.background.backgroundImage.length,r=A.style.border.some(function(A){return A.borderStyle!==l.BORDER_STYLE.NONE&&!A.borderColor.isTransparent()}),n=function(){var r=(0,c.calculateBackgroungPaintingArea)(A.curvedBounds,A.style.background.backgroundClip);t&&e.target.clip([r],function(){A.style.background.backgroundColor.isTransparent()||e.target.fill(A.style.background.backgroundColor),e.renderBackgroundImage(A)}),A.style.border.forEach(function(t,r){t.borderStyle===l.BORDER_STYLE.NONE||t.borderColor.isTransparent()||e.renderBorder(t,r,A.curvedBounds)})};if(t||r){var B=A.parent?A.parent.getClipPaths():[];B.length?this.target.clip(B,n):n()}}},{key:"renderBackgroundImage",value:function(A){var e=this;A.style.background.backgroundImage.slice(0).reverse().forEach(function(t){"url"===t.source.method&&t.source.args.length?e.renderBackgroundRepeat(A,t):/gradient/i.test(t.source.method)&&e.renderBackgroundGradient(A,t)})}},{key:"renderBackgroundRepeat",value:function(A,e){var t=this.options.imageStore.get(e.source.args[0]);if(t){var r=(0,c.calculateBackgroungPositioningArea)(A.style.background.backgroundOrigin,A.bounds,A.style.padding,A.style.border),n=(0,c.calculateBackgroundSize)(e,t,r),B=(0,c.calculateBackgroundPosition)(e.position,n,r),a=(0,c.calculateBackgroundRepeatPath)(e,B,n,r,A.bounds),s=Math.round(r.left+B.x),o=Math.round(r.top+B.y);this.target.renderRepeat(a,t,n,s,o)}}},{key:"renderBackgroundGradient",value:function(A,e){var t=(0,c.calculateBackgroungPositioningArea)(A.style.background.backgroundOrigin,A.bounds,A.style.padding,A.style.border),r=(0,c.calculateGradientBackgroundSize)(e,t),n=(0,c.calculateBackgroundPosition)(e.position,r,t),B=new a.Bounds(Math.round(t.left+n.x),Math.round(t.top+n.y),r.width,r.height),o=(0,s.parseGradient)(A,e.source,B);if(o)switch(o.type){case s.GRADIENT_TYPE.LINEAR_GRADIENT:this.target.renderLinearGradient(B,o);break;case s.GRADIENT_TYPE.RADIAL_GRADIENT:this.target.renderRadialGradient(B,o)}}},{key:"renderBorder",value:function(A,e,t){this.target.drawShape((0,a.parsePathForBorder)(t,e),A.borderColor)}},{key:"renderStack",value:function(A){var e=this;if(A.container.isVisible()){var t=A.getOpacity();t!==this._opacity&&(this.target.setOpacity(A.getOpacity()),this._opacity=t);var r=A.container.style.transform;null!==r?this.target.transform(A.container.bounds.left+r.transformOrigin[0].value,A.container.bounds.top+r.transformOrigin[1].value,r.transform,function(){return e.renderStackContent(A)}):this.renderStackContent(A)}}},{key:"renderStackContent",value:function(A){var e=w(A),t=n(e,5),r=t[0],B=t[1],a=t[2],s=t[3],o=t[4],i=Q(A),c=n(i,2),l=c[0],u=c[1];this.renderNodeBackgroundAndBorders(A.container),r.sort(U).forEach(this.renderStack,this),this.renderNodeContent(A.container),u.forEach(this.renderNode,this),s.forEach(this.renderStack,this),o.forEach(this.renderStack,this),l.forEach(this.renderNode,this),B.forEach(this.renderStack,this),a.sort(U).forEach(this.renderStack,this)}},{key:"render",value:function(A){this.options.backgroundColor&&this.target.rectangle(this.options.x,this.options.y,this.options.width,this.options.height,this.options.backgroundColor),this.renderStack(A);var e=this.target.getTarget();return e}}]),A}();e.default=u;var Q=function(A){for(var e=[],t=[],r=A.children.length,n=0;n0?r.push(o):t.push(o):o.container.isFloating()?n.push(o):B.push(o)}return[e,t,r,n,B]},U=function(A,e){return A.container.style.zIndex.order>e.container.style.zIndex.order?1:A.container.style.zIndex.ordere.container.index?1:-1}},function(A,e,t){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.transformWebkitRadialGradientArgs=e.parseGradient=e.RadialGradient=e.LinearGradient=e.RADIAL_GRADIENT_SHAPE=e.GRADIENT_TYPE=void 0;var r=function(){return function(A,e){if(Array.isArray(A))return A;if(Symbol.iterator in Object(A))return function(A,e){var t=[],r=!0,n=!1,B=void 0;try{for(var a,s=A[Symbol.iterator]();!(r=(a=s.next()).done)&&(t.push(a.value),!e||t.length!==e);r=!0);}catch(A){n=!0,B=A}finally{try{!r&&s.return&&s.return()}finally{if(n)throw B}}return t}(A,e);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}(),n=(i(t(3)),t(53)),B=i(t(0)),a=t(1),s=i(a),o=t(4);function i(A){return A&&A.__esModule?A:{default:A}}function c(A,e){if(!(A instanceof e))throw new TypeError("Cannot call a class as a function")}var l=/^(to )?(left|top|right|bottom)( (left|top|right|bottom))?$/i,u=/^([+-]?\d*\.?\d+)% ([+-]?\d*\.?\d+)%$/i,Q=/(px)|%|( 0)$/i,w=/^(from|to|color-stop)\((?:([\d.]+)(%)?,\s*)?(.+?)\)$/i,U=/^\s*(circle|ellipse)?\s*((?:([\d.]+)(px|r?em|%)\s*(?:([\d.]+)(px|r?em|%))?)|closest-side|closest-corner|farthest-side|farthest-corner)?\s*(?:at\s*(?:(left|center|right)|([\d.]+)(px|r?em|%))\s+(?:(top|center|bottom)|([\d.]+)(px|r?em|%)))?(?:\s|$)/i,g=e.GRADIENT_TYPE={LINEAR_GRADIENT:0,RADIAL_GRADIENT:1},C=e.RADIAL_GRADIENT_SHAPE={CIRCLE:0,ELLIPSE:1},d={left:new s.default("0%"),top:new s.default("0%"),center:new s.default("50%"),right:new s.default("100%"),bottom:new s.default("100%")},F=e.LinearGradient=function A(e,t){c(this,A),this.type=g.LINEAR_GRADIENT,this.colorStops=e,this.direction=t},E=e.RadialGradient=function A(e,t,r,n){c(this,A),this.type=g.RADIAL_GRADIENT,this.colorStops=e,this.shape=t,this.center=r,this.radius=n},f=(e.parseGradient=function(A,e,t){var r=e.args,n=e.method,B=e.prefix;return"linear-gradient"===n?h(r,t,!!B):"gradient"===n&&"linear"===r[0]?h(["to bottom"].concat(y(r.slice(3))),t,!!B):"radial-gradient"===n?H(A,"-webkit-"===B?v(r):r,t):"gradient"===n&&"radial"===r[0]?H(A,y(v(r.slice(1))),t):void 0},function(A,e,t){for(var r=[],n=e;nA.optimumDistance)?{optimumCorner:n,optimumDistance:B}:A},{optimumDistance:r?1/0:-1/0,optimumCorner:null}).optimumCorner},m=function(A,e,t,r,n){var B=t.x,a=t.y,s=0,i=0;switch(A){case"closest-side":e===C.CIRCLE?s=i=Math.min(Math.abs(B),Math.abs(B-n.width),Math.abs(a),Math.abs(a-n.height)):e===C.ELLIPSE&&(s=Math.min(Math.abs(B),Math.abs(B-n.width)),i=Math.min(Math.abs(a),Math.abs(a-n.height)));break;case"closest-corner":if(e===C.CIRCLE)s=i=Math.min((0,o.distance)(B,a),(0,o.distance)(B,a-n.height),(0,o.distance)(B-n.width,a),(0,o.distance)(B-n.width,a-n.height));else if(e===C.ELLIPSE){var c=Math.min(Math.abs(a),Math.abs(a-n.height))/Math.min(Math.abs(B),Math.abs(B-n.width)),l=T(n,B,a,!0);i=c*(s=(0,o.distance)(l.x-B,(l.y-a)/c))}break;case"farthest-side":e===C.CIRCLE?s=i=Math.max(Math.abs(B),Math.abs(B-n.width),Math.abs(a),Math.abs(a-n.height)):e===C.ELLIPSE&&(s=Math.max(Math.abs(B),Math.abs(B-n.width)),i=Math.max(Math.abs(a),Math.abs(a-n.height)));break;case"farthest-corner":if(e===C.CIRCLE)s=i=Math.max((0,o.distance)(B,a),(0,o.distance)(B,a-n.height),(0,o.distance)(B-n.width,a),(0,o.distance)(B-n.width,a-n.height));else if(e===C.ELLIPSE){var u=Math.max(Math.abs(a),Math.abs(a-n.height))/Math.max(Math.abs(B),Math.abs(B-n.width)),Q=T(n,B,a,!1);i=u*(s=(0,o.distance)(Q.x-B,(Q.y-a)/u))}break;default:s=r.x||0,i=void 0!==r.y?r.y:s}return{x:s,y:i}},v=e.transformWebkitRadialGradientArgs=function(A){var e="",t="",r="",n="",B=0,a=/^(left|center|right|\d+(?:px|r?em|%)?)(?:\s+(top|center|bottom|\d+(?:px|r?em|%)?))?$/i,s=/^\d+(px|r?em|%)?(?:\s+\d+(px|r?em|%)?)?$/i,o=A[B].match(a);o&&B++;var i=A[B].match(/^(circle|ellipse)?\s*(closest-side|closest-corner|farthest-side|farthest-corner|contain|cover)?$/i);i&&(e=i[1]||"","contain"===(r=i[2]||"")?r="closest-side":"cover"===r&&(r="farthest-corner"),B++);var c=A[B].match(s);c&&B++;var l=A[B].match(a);l&&B++;var u=A[B].match(s);u&&B++;var Q=l||o;Q&&Q[1]&&(n=Q[1]+(/^\d+$/.test(Q[1])?"px":""),Q[2]&&(n+=" "+Q[2]+(/^\d+$/.test(Q[2])?"px":"")));var w=u||c;return w&&(t=w[0],w[1]||(t+="px")),!n||e||t||r||(t=n,n=""),n&&(n="at "+n),[[e,r,t,n].filter(function(A){return!!A}).join(" ")].concat(A.slice(B))},y=function(A){return A.map(function(A){return A.match(w)}).map(function(e,t){if(!e)return A[t];switch(e[1]){case"from":return e[4]+" 0%";case"to":return e[4]+" 100%";case"color-stop":return"%"===e[3]?e[4]+" "+e[2]:e[4]+" "+100*parseFloat(e[2])+"%"}})}},function(A,e,t){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=/([+-]?\d*\.?\d+)(deg|grad|rad|turn)/i;e.parseAngle=function(A){var e=A.match(r);if(e){var t=parseFloat(e[1]);switch(e[2].toLowerCase()){case"deg":return Math.PI*t/180;case"grad":return Math.PI/200*t;case"rad":return t;case"turn":return 2*Math.PI*t}}return null}},function(A,e,t){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.cloneWindow=e.DocumentCloner=void 0;var r=function(){return function(A,e){if(Array.isArray(A))return A;if(Symbol.iterator in Object(A))return function(A,e){var t=[],r=!0,n=!1,B=void 0;try{for(var a,s=A[Symbol.iterator]();!(r=(a=s.next()).done)&&(t.push(a.value),!e||t.length!==e);r=!0);}catch(A){n=!0,B=A}finally{try{!r&&s.return&&s.return()}finally{if(n)throw B}}return t}(A,e);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}(),n=function(){function A(A,e){for(var t=0;t1&&(t.backgroundColor=""),t.backgroundImage=A.join(",")}),A instanceof HTMLImageElement&&this.resourceLoader.inlineImage(A.src).then(function(e){if(e&&A instanceof HTMLImageElement&&A.parentNode){var t=A.parentNode,r=(0,o.copyCSSStyles)(A.style,e.cloneNode(!1));t.replaceChild(r,A)}}).catch(function(A){0})}}},{key:"inlineFonts",value:function(A){var e=this;return Promise.all(Array.from(A.styleSheets).map(function(e){return e.href?fetch(e.href).then(function(A){return A.text()}).then(function(A){return U(A,e.href)}).catch(function(A){return[]}):w(e,A)})).then(function(A){return A.reduce(function(A,e){return A.concat(e)},[])}).then(function(A){return Promise.all(A.map(function(A){return fetch(A.formats[0].src).then(function(A){return A.blob()}).then(function(A){return new Promise(function(e,t){var r=new FileReader;r.onerror=t,r.onload=function(){var A=r.result;e(A)},r.readAsDataURL(A)})}).then(function(e){return A.fontFace.setProperty("src",'url("'+e+'")'),"@font-face {"+A.fontFace.cssText+" "})}))}).then(function(t){var r=A.createElement("style");r.textContent=t.join("\n"),e.documentElement.appendChild(r)})}},{key:"createElementClone",value:function(A){var e=this;if(this.copyStyles&&A instanceof HTMLCanvasElement){var t=A.ownerDocument.createElement("img");try{return t.src=A.toDataURL(),t}catch(A){0}}if(A instanceof HTMLIFrameElement){var r=A.cloneNode(!1),n=N();r.setAttribute("data-html2canvas-internal-iframe-key",n);var a=(0,B.parseBounds)(A,0,0),s=a.width,i=a.height;return this.resourceLoader.cache[n]=K(A,this.options).then(function(A){return e.renderer(A,{async:e.options.async,allowTaint:e.options.allowTaint,backgroundColor:"#ffffff",canvas:null,imageTimeout:e.options.imageTimeout,logging:e.options.logging,proxy:e.options.proxy,removeContainer:e.options.removeContainer,scale:e.options.scale,foreignObjectRendering:e.options.foreignObjectRendering,useCORS:e.options.useCORS,target:new c.default,width:s,height:i,x:0,y:0,windowWidth:A.ownerDocument.defaultView.innerWidth,windowHeight:A.ownerDocument.defaultView.innerHeight,scrollX:A.ownerDocument.defaultView.pageXOffset,scrollY:A.ownerDocument.defaultView.pageYOffset},e.logger.child(n))}).then(function(e){return new Promise(function(t,n){var B=document.createElement("img");B.onload=function(){return t(e)},B.onerror=n,B.src=e.toDataURL(),r.parentNode&&r.parentNode.replaceChild((0,o.copyCSSStyles)(A.ownerDocument.defaultView.getComputedStyle(A),B),r)})}),r}if(A instanceof HTMLStyleElement&&A.sheet&&A.sheet.cssRules){var l=[].slice.call(A.sheet.cssRules,0).reduce(function(A,t){try{return t&&t.cssText?A+t.cssText:A}catch(r){return e.logger.log("Unable to access cssText property",t.name),A}},""),u=A.cloneNode(!1);return u.textContent=l,u}return A.cloneNode(!1)}},{key:"cloneNode",value:function(A){var e=A.nodeType===Node.TEXT_NODE?document.createTextNode(A.nodeValue):this.createElementClone(A),t=A.ownerDocument.defaultView,r=A instanceof t.HTMLElement?t.getComputedStyle(A):null,n=A instanceof t.HTMLElement?t.getComputedStyle(A,":before"):null,B=A instanceof t.HTMLElement?t.getComputedStyle(A,":after"):null;this.referenceElement===A&&e instanceof t.HTMLElement&&(this.clonedReferenceElement=e),e instanceof t.HTMLBodyElement&&h(e);for(var a=(0,l.parseCounterReset)(r,this.pseudoContentData),s=(0,l.resolvePseudoContent)(A,n,this.pseudoContentData),i=A.firstChild;i;i=i.nextSibling)i.nodeType===Node.ELEMENT_NODE&&("SCRIPT"===i.nodeName||i.hasAttribute("data-html2canvas-ignore")||"function"==typeof this.options.ignoreElements&&this.options.ignoreElements(i))||this.copyStyles&&"STYLE"===i.nodeName||e.appendChild(this.cloneNode(i));var c=(0,l.resolvePseudoContent)(A,B,this.pseudoContentData);if((0,l.popCounters)(a,this.pseudoContentData),A instanceof t.HTMLElement&&e instanceof t.HTMLElement)switch(n&&this.inlineAllImages(C(A,e,n,s,d)),B&&this.inlineAllImages(C(A,e,B,c,F)),!r||!this.copyStyles||A instanceof HTMLIFrameElement||(0,o.copyCSSStyles)(r,e),this.inlineAllImages(e),0===A.scrollTop&&0===A.scrollLeft||this.scrolledElements.push([e,A.scrollLeft,A.scrollTop]),A.nodeName){case"CANVAS":this.copyStyles||g(A,e);break;case"TEXTAREA":case"SELECT":e.value=A.value}return e}}]),A}(),w=function(A,e){return(A.cssRules?Array.from(A.cssRules):[]).filter(function(A){return A.type===CSSRule.FONT_FACE_RULE}).map(function(A){for(var t=(0,i.parseBackgroundImage)(A.style.getPropertyValue("src")),r=[],n=0;n0&&"complete"===t.readyState&&(clearInterval(e),r(A))},50)}})},v=(e.cloneWindow=function(A,e,t,r,n,B){var a=new Q(t,r,n,!1,B),s=A.defaultView.pageXOffset,o=A.defaultView.pageYOffset;return T(A,e).then(function(n){var B=n.contentWindow,i=B.document,c=m(n).then(function(){a.scrolledElements.forEach(p),B.scrollTo(e.left,e.top),!/(iPad|iPhone|iPod)/g.test(navigator.userAgent)||B.scrollY===e.top&&B.scrollX===e.left||(i.documentElement.style.top=-e.top+"px",i.documentElement.style.left=-e.left+"px",i.documentElement.style.position="absolute");var t=Promise.resolve([n,a.clonedReferenceElement,a.resourceLoader]),s=r.onclone;return a.clonedReferenceElement instanceof B.HTMLElement||a.clonedReferenceElement instanceof A.defaultView.HTMLElement||a.clonedReferenceElement instanceof HTMLElement?"function"==typeof s?Promise.resolve().then(function(){return s(i)}).then(function(){return t}):t:Promise.reject("")});return i.open(),i.write(v(document.doctype)+""),function(A,e,t){!A.defaultView||e===A.defaultView.pageXOffset&&t===A.defaultView.pageYOffset||A.defaultView.scrollTo(e,t)}(t.ownerDocument,s,o),i.replaceChild(i.adoptNode(a.documentElement),i.documentElement),i.close(),c})},function(A){var e="";return A&&(e+=""),e})},function(A,e,t){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.ResourceStore=void 0;var r,n=function(){function A(A,e){for(var t=0;t0&&t.push({type:s.ATTRIBUTE,value:l[0]});break;case"counter":if(l.length>0){var w={type:s.COUNTER,name:l[0]};l.length>1&&(w.format=l[1]),t.push(w)}break;case"counters":if(l.length>0){var U={type:s.COUNTERS,name:l[0]};l.length>1&&(U.glue=l[1]),l.length>2&&(U.format=l[2]),t.push(U)}break;case"url":l.length>0&&t.push({type:s.URL,value:l[0]})}a=!1,o=""}break;case",":n?o+=Q:a&&(l.push(o),o="");break;case" ":case"\t":n?o+=Q:o&&(i(t,o),o="");break;default:o+=Q}"\\"!==Q&&(B=!1)}return o&&i(t,o),e&&(e[A]=t),t}),i=function(A,e){switch(e){case"open-quote":A.push({type:s.OPENQUOTE});break;case"close-quote":A.push({type:s.CLOSEQUOTE})}},c=function(A,e,t){var r=A.quotes?A.quotes.split(/\s+/):["'\"'","'\"'"],n=2*t;return n>=r.length&&(n=r.length-2),e||++n,r[n].replace(/^["']|["']$/g,"")},l=function(A,e,t){for(var r=A.length,a="",s=0;s0&&(a+=e||""),a+=(0,n.createCounterText)(A[s],(0,B.parseListStyleType)(t||"decimal"),!1);return a}}])});
\ No newline at end of file
diff --git a/js/ics.min.js b/js/lib/ics.min.js
similarity index 100%
rename from js/ics.min.js
rename to js/lib/ics.min.js
diff --git a/js/jquery-3.3.1.min.js b/js/lib/jquery-3.3.1.min.js
similarity index 100%
rename from js/jquery-3.3.1.min.js
rename to js/lib/jquery-3.3.1.min.js
diff --git a/js/lib/jquery.initialize.min.js b/js/lib/jquery.initialize.min.js
new file mode 100644
index 00000000..9ea59e98
--- /dev/null
+++ b/js/lib/jquery.initialize.min.js
@@ -0,0 +1 @@
+(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 {
+ let enabled = data.options[key];
+ $("#options_container").append(Template.Options.options_row(key, enabled));
+ });
+ }
});
-$("#toggleConflictHighlighting").click(function(){
- var action = $("#toggleConflictHighlighting").text();
- if(action == "Turn Off"){
- chrome.storage.sync.set({courseConflictHighlight: false}, function() {
- on();
- });
- } else{
- chrome.storage.sync.set({courseConflictHighlight: true}, function() {
- off();
- });
- }
- chrome.tabs.query({}, function(tabs) {
- for(var i = 0; i {
+ 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;
+ $("#contributor-list").append(Template.Options.contributor_card(login, name, avatar_url, html_url));
+ });
+ }
+});
-function on(){
- $("#toggleConflictHighlighting").text("Turn On");
- $("#toggleConflictHighlighting").css("background","#4CAF50");
+$("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);
}
-function off(){
- $("#toggleConflictHighlighting").text("Turn Off");
- $("#toggleConflictHighlighting").css("background","#F44336");
-}
\ No newline at end of file
diff --git a/js/popup.js b/js/popup.js
index 6a9ebb8e..4075c705 100644
--- a/js/popup.js
+++ b/js/popup.js
@@ -1,246 +1,422 @@
var courses;
-// get the courses from storage
-chrome.storage.sync.get('savedCourses', function (data) {
- //find, build, and show the messages for the conflicts in the saved courses
- chrome.runtime.sendMessage({
- command: "checkConflicts"
- }, function (response) {
- var isConflicted = [];
- if (response.isConflict) {
- var between = response.between;
- var text = "";
- for (var i = 0; i < between.length; i++) {
- text += "CONFLICT: " + getSimpleName(between[i][0].coursename, between[i][0].unique) + " and " + getSimpleName(between[i][1].coursename, between[i][1].unique);
- isConflicted.push(between[i][0].unique);
- isConflicted.push(between[i][1].unique);
- if (i != between.length - 1) {
- text += " ";
- }
- }
- $("#courseList").prepend("" + text + ">");
- }
- });
- courses = data.savedCourses;
- if (courses.length != 0) {
- $("#empty").hide();
- }
- // build and append the course list element
- for (var i = 0; i < courses.length; i++) {
- var color;
- status = courses[i].status;
- if (status.includes("open")) {
- color = "#4CAF50";
- } else if (status.includes("waitlisted")) {
- color = "#FF9800"
- } else if (status.includes("closed") || status.includes("cancelled")) {
- color = "#FF5722";
- }
- var department = courses[i].coursename.substring(0, courses[i].coursename.search(/\d/) - 2);
- var course_nbr = courses[i].coursename.substring(courses[i].coursename.search(/\d/), courses[i].coursename.indexOf(" ", courses[i].coursename.search(/\d/)));
- var profname = prettifyName(courses[i].profname);
- if (profname == "") {
- profname = "Undecided Professor";
- }
- var listhtml = "
" + department + " " + course_nbr + "" + " with " + profname + " (" + courses[i].unique + ")" + " ►
" + makeLine(i) + "
Remove Register More Info
";
- $("#courseList").append(listhtml);
- }
-});
-/* prettify the name for the conflict messages*/
-function getSimpleName(coursename, unique) {
- var department = coursename.substring(0, coursename.search(/\d/) - 2);
- var course_nbr = coursename.substring(coursename.search(/\d/), coursename.indexOf(" ", coursename.search(/\d/)));
- return department + " " + course_nbr + " (" + unique + ")";
+setCourseList();
+getSemesters();
+getDepartments();
+
+var can_remove = true;
+
+function setCourseList() {
+ $("#courseList").empty();
+ chrome.storage.sync.get("savedCourses", function (data) {
+ updateConflicts();
+ courses = data.savedCourses;
+ handleEmpty();
+ let num_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 } = seperateCourseNameParts(coursename);
+ num_hours += parseInt(number.substring(0, 1));
+
+ 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(num_hours);
+ });
}
-/* Format the Professor Name */
-function prettifyName(profname) {
- return profname.replace(/\w\S*/g, function (txt) {
- return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
- });
+/* convert from the dtarr and maek the time lines*/
+function buildTimeLines(datetimearr) {
+ let lines = convertDateTimeArrToLine(datetimearr);
+ let output = "";
+ if (lines.length == 0) {
+ output = "This class has no meeting times. ";
+ } 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) {
- var isConflicted = [];
- if (response.isConflict) {
- var between = response.between;
- var text = "";
- for (var i = 0; i < between.length; i++) {
- text += "CONFLICT: " + getSimpleName(between[i][0].coursename, between[i][0].unique) + " and " + getSimpleName(between[i][1].coursename, between[i][1].unique);
- isConflicted.push(between[i][0].unique);
- isConflicted.push(between[i][1].unique);
- if (i != between.length - 1) {
- text += " ";
- }
- }
- $("#courseList").prepend("" + text + ">");
- }
- });
+ chrome.runtime.sendMessage(
+ {
+ command: "checkConflicts",
+ },
+ function (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 += " ";
+ }
+ $(Template.Popup.conflict_message(conflict_message)).prependTo("#courseList").hide().fadeIn(200);
+ }
+ }
+ );
}
-/* Handle the button clicks */
-$(document).ready(function () {
- $("#courseList li").click(function () {
- $(this).find("#listMoreInfo").click(function () {
- window.open(courses[$(this).closest("li").attr("id")].link);
- });
- let status = courses[$(this).closest("li").attr("id")].status;
- let registerlink = courses[$(this).closest("li").attr("id")].registerlink;
- if (status.includes("closed") || status.includes("cancelled") || !status || !registerlink) {
- $(this).find("#register").text("Can't Register").css("background-color", "#FF5722");
- } else {
- if (status.includes("waitlisted")) {
- $(this).find("#register").text("Join Waitlist").css("background-color", "#FF9800");
- } else {
- $(this).find("#register").text("Register").css("background-color", "#4CAF50");
- }
- $(this).find("#register").click(function () {
- chrome.tabs.query({
- currentWindow: true,
- active: true
- }, function (tab) {
- chrome.tabs.update(tab.id, {
- url: registerlink
- });
- });
- })
- }
- /* clear the conflict messages, then remove the course and updateConflicts. update the tabs*/
- $(this).find("#listRemove").click(function () {
- var thisForm = this;
- $(thisForm).closest("ul").find(">p").remove();
- chrome.runtime.sendMessage({
- command: "courseStorage",
- course: courses[$(thisForm).closest("li").attr("id")],
- action: "remove"
- }, function (response) {
- $(thisForm).closest("li").fadeOut(200);
- if ($(thisForm).closest("ul").children(':visible').length === 1) {
- $("#courseList").fadeOut(300, function () {
- $("#empty").fadeIn(200);
- });
- }
- updateConflicts();
- chrome.tabs.query({}, function (tabs) {
- for (var i = 0; i < tabs.length; i++) {
- chrome.tabs.sendMessage(tabs[i].id, {
- command: "updateCourseList"
- });
- }
- });
- });
- });
- /* Show the times popout and more info options*/
- if ($(this).find("#moreInfo").is(":hidden")) {
- $(this).find("#moreInfo").fadeIn(200);
- $(this).find('#arrow').css('transform', 'rotate(90deg)');
+/* prettify the name for the conflict messages*/
+function formatShortenedCourseName(course) {
+ let { number, department } = seperateCourseNameParts(course.coursename);
+ return `${department} ${number} (${course.unique})`;
+}
- } else {
- $(this).find("#moreInfo").fadeOut(200);
- $(this).find('#arrow').css('transform', '');
+$(document).click(function (event) {
+ $target = $(event.target);
- }
- });
- $("#clear").click(function () {
- clear();
- });
- $("#schedule").click(function () {
- chrome.tabs.create({
- 'url': 'https://registrar.utexas.edu/schedules'
- });
- });
- $("#open").click(function () {
- chrome.tabs.create({
- 'url': "options.html"
- });
- });
- $("#calendar").click(function () {
- chrome.tabs.create({
- 'url': "calendar.html"
- });
- });
+ // 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();
+ }
});
-/* convert from the dtarr and maek the time lines*/
-function makeLine(index) {
- var datetimearr = courses[index].datetimearr;
- //converted times back
- var dtmap = new Map([]);
- for (var i = 0; i < datetimearr.length; i++) {
- datetimearr[i][1][0] = moment(datetimearr[i][1][0], ["HH:mm"]).format("h:mm a");
- datetimearr[i][1][1] = moment(datetimearr[i][1][1], ["HH:mm"]).format("h:mm a");
- }
- for (var i = 0; i < datetimearr.length; i++) {
- if (dtmap.has(String(datetimearr[i][1]))) {
- dtmap.set(String(datetimearr[i][1]), dtmap.get(String(datetimearr[i][1])) + datetimearr[i][0]);
- } else {
- dtmap.set(String(datetimearr[i][1]), datetimearr[i][0]);
- }
- }
- var output = "";
- var timearr = Array.from(dtmap.keys());
- var dayarr = Array.from(dtmap.values());
+$("#clear").click(function () {
+ chrome.storage.sync.set({
+ savedCourses: [],
+ });
+ $("#courseList").empty();
+ updateAllTabsCourseList();
+ showEmpty();
+});
- if (timearr.length == 0) {
- output = "This class has no meeting times. "
- } else {
- for (var i = 0; i < dayarr.length; i++) {
- var place = findLoc(dayarr[i], timearr[i], datetimearr);
- var building = place.substring(0, place.search(/\d/) - 1);
- if (building == "") {
- building = "Undecided Location";
- }
- output += `${dayarr[i]}: ${timearr[i].split(",")[0]} to ${timearr[i].split(",")[1]} ${place} `;
- }
- }
- return output;
+$("#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]);
+});
-//find the location of a class given its days and timearrs.
-function findLoc(day, timearr, datetimearr) {
- for (let i = 0; i < datetimearr.length; i++) {
- var dtl = datetimearr[i];
- // console.log(dtl[1]);
- // console.log(timearr);
- if (day.includes(dtl[0])) {
- if (JSON.stringify(timearr) == JSON.stringify(fixDtl1(dtl[1]))) {
- return dtl[2];
- }
- }
- }
+function exportCourses(url) {
+ var exportlink = document.createElement("a");
+ exportlink.setAttribute("href", url);
+ exportlink.setAttribute("download", "my_courses.json");
+ exportlink.click();
}
-function fixDtl1(dtl1) {
- let output = "";
- for (let i = 0; i < dtl1.length; i++) {
- output += dtl1[i];
- if (i != dtl1.length - 1) {
- output += ",";
- }
- }
- return output;
+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();
+ });
+});
-/*Clear the list and the storage of courses*/
-function clear() {
- chrome.storage.sync.set({
- savedCourses: []
- });
- chrome.tabs.query({}, function (tabs) {
- for (var i = 0; i < tabs.length; i++) {
- chrome.tabs.sendMessage(tabs[i].id, {
- command: "updateCourseList"
- });
- }
- });
- console.log("cleared");
- $("#courseList").fadeOut(300, function () {
- $("#empty").fadeIn(200);
- });
-}
\ No newline at end of file
+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 = $(" ");
+ $("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 = seperateCourseNameParts(curr_course.coursename).number;
+ let curr_individual_hours = parseInt(curr_course_number.substring(0, 1));
+ $("#meta-metric").text(curr_total_hours - curr_individual_hours);
+}
+
+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(`${name} `);
+ }
+ }
+ );
+}
+
+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(`${abv} `);
+ }
+ // $("#department").val('C S');
+ }
+ );
+}
diff --git a/js/utPlanner.js b/js/utPlanner.js
new file mode 100644
index 00000000..0d03afe1
--- /dev/null
+++ b/js/utPlanner.js
@@ -0,0 +1,242 @@
+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('
Plus 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/?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("No Data ")
+ } 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($(`${semesters[i]} `));
+ }
+ $("#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($(`${makeLine(date, time, place)}`));
+ }
+ return lines
+}
+
+function makeLine(date, time, place) {
+ var arr = seperateDays(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 ${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();
+ }
+});
diff --git a/js/util.js b/js/util.js
new file mode 100644
index 00000000..dc9ad6c5
--- /dev/null
+++ b/js/util.js
@@ -0,0 +1,375 @@
+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 seperateCourseNameParts(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 seperateDays(date, simple=false) {
+ let arr = [];
+ for (var i = 0; i < date.length; i++) {
+ let letter = date.charAt(i);
+ let seperated_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 $('
').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 = [];
+ console.log(datetimearr)
+ var dtmap = makeDateTimeMap(datetimearr);
+ var timearr = Array.from(dtmap.keys());
+ var dayarr = Array.from(dtmap.values());
+ console.log(timearr);
+ console.log(dayarr);
+ for (var i = 0; i < dayarr.length; i++) {
+ var place = findLocation(dayarr[i], timearr[i], datetimearr);
+ var building = place.substring(0, place.search(/\d/)).trim();
+ building = building ? building : "Undecided Location"
+ output.push({
+ "days": dayarr[i],
+ "start_time": timearr[i].split(",")[0],
+ "end_time": timearr[i].split(',')[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++) {
+ if (dtmap.has(String(datetimearr[i][1]))) {
+ dtmap.set(String(datetimearr[i][1]), dtmap.get(String(datetimearr[i][1])) + datetimearr[i][0]);
+ } else {
+ dtmap.set(String(datetimearr[i][1]), datetimearr[i][0]);
+ }
+ }
+ 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: '{point.key} ',
+ pointFormat: '{point.y:.0f} Students ',
+ footerFormat: '
',
+ 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
+}
diff --git a/manifest.json b/manifest.json
index 0bc38607..735abacd 100644
--- a/manifest.json
+++ b/manifest.json
@@ -1,47 +1,77 @@
-{
- "manifest_version": 2,
-
- "name": "UT Registration Plus",
- "version": "0.2.2.1",
- "options_page": "options.html",
- "description": "Improves the course registration process at the University of Texas at Austin!",
- "permissions": ["tabs",
- "declarativeContent",
- "storage",
- "*://*.utdirect.utexas.edu/apps/registrar/course_schedule/*",
- "*://*.catalog.utexas.edu/ribbit/"
- ],
- "content_scripts": [{
- "css": ["css/styles.css"],
- "js": ["js/moment.min.js", "js/sql-memory-growth.js", "js/highcharts.js", "js/jquery-3.3.1.min.js", "js/content.js"],
- "matches": ["https://utdirect.utexas.edu/apps/registrar/course_schedule/*"]
- }, {
- "css": ["css/styles.css"],
- "js": ["js/moment.min.js", "js/sql-memory-growth.js", "js/highcharts.js", "js/jquery-3.3.1.min.js", "js/import.js"],
- "matches": ["https://utdirect.utexas.edu/registrar/waitlist/wl_see_my_waitlists.WBX"]
- }
-
- ],
- "web_accessible_resources": [
- "grades.db", "images/disticon.png"
- ],
- "background": {
- "scripts": ["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"
- }
-}
\ No newline at end of file
+{
+ "manifest_version": 2,
+ "name": "UT Registration Plus",
+ "version": "1.2.2.6",
+ "options_page": "options.html",
+ "description": "Improves the course registration process at the University of Texas at Austin!",
+ "permissions": [
+ "tabs",
+ "declarativeContent",
+ "storage",
+ "*://*.utdirect.utexas.edu/apps/registrar/course_schedule/*",
+ "*://*.utexas.collegescheduler.com/*",
+ "*://*.catalog.utexas.edu/ribbit/",
+ "*://*.registrar.utexas.edu/schedules/*",
+ "*://*.login.utexas.edu/login/*"
+ ],
+ "browser_specific_settings": {
+ "gecko": {
+ "id": "sghsri@gmail.com"
+ }
+ },
+ "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"
+ }
+}
diff --git a/options.html b/options.html
index ad10a216..7c5a9db2 100644
--- a/options.html
+++ b/options.html
@@ -1,17 +1,30 @@
-
-
-
-
-
-