Advertisement
Guest User

initWITHgrade.js

a guest
Sep 20th, 2019
248
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 148.58 KB | None | 0 0
  1. /* Power+ v2.0.0 (beta)
  2. (c) 2018 - 2019 jottocraft
  3. https://github.com/jottocraft/dtps */
  4.  
  5. //Basic global Power+ configuration. All global Power+ variables go under dtps
  6. var dtps = {
  7. ver: 200,
  8. readableVer: "v2.0.0 (beta)",
  9. trackSuffix: " (beta)",
  10. trackColor: "#ec9b06",
  11. showLetters: false,
  12. fullNames: false,
  13. classes: [],
  14. latestStream: [],
  15. explorer: [],
  16. embedded: window.location.href.includes("instructure.com"),
  17. auth: {
  18. accessToken: window.localStorage.accessToken ? window.localStorage.accessToken : undefined,
  19. canvasURL: "https://dtechhs.instructure.com",
  20. client_id: "146080000000000005",
  21. uri: "https://powerplus.app/app",
  22. scope: [
  23. "url:GET|/api/v1/courses/:course_id/outcome_rollups",
  24. "url:GET|/api/v1/users/:id",
  25. "url:GET|/api/v1/users/:id/colors",
  26. "url:GET|/api/v1/users/:id/dashboard_positions",
  27. "url:GET|/api/v1/courses",
  28. "url:GET|/api/v1/courses/:course_id/pages",
  29. "url:GET|/api/v1/courses/:course_id/discussion_topics",
  30. "url:GET|/api/v1/courses/:course_id/discussion_topics/:topic_id/view",
  31. "url:GET|/api/v1/courses/:course_id/outcome_results",
  32. "url:GET|/api/v1/courses/:course_id/assignment_groups",
  33. "url:GET|/api/v1/courses/:course_id/modules",
  34. "url:GET|/api/v1/courses/:course_id/front_page",
  35. "url:GET|/api/v1/courses/:course_id/pages/:url",
  36. "url:GET|/api/v1/outcomes/:id",
  37. "url:GET|/api/v1/courses/:course_id/outcome_alignments",
  38. "url:GET|/api/v1/announcements",
  39. //not currently used in dtps, might be used later on
  40. "url:GET|/api/v1/courses/:course_id/assignments",
  41. "url:GET|/api/v1/courses/:course_id/assignment_groups/:assignment_group_id/assignments"
  42. ]
  43. },
  44. chromaProfile: {
  45. title: "Power+",
  46. description: "Razer Chroma effects for Power+ (beta)",
  47. author: "jottocraft",
  48. domain: "dtps.js.org"
  49. }
  50. };
  51.  
  52. //alert all errors if dtpsDebug enabled (for chromebooks w/o devtools)
  53. window.onerror = function myErrorHandler(errorMsg, url, lineNumber) {
  54. if (window.localStorage.dtpsDebug == "true") alert("[DTPS] ERROR: " + errorMsg + "\n\n" + url + ":" + lineNumber);
  55. return false;
  56. }
  57.  
  58. //Load a better version of jQuery as soon as possible because of Canvas's weird included version of jQuery that breaks a lot of important things
  59. if (dtps.embedded) jQuery.getScript("https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js")
  60.  
  61. //Embedded Status Updates (load this before AND after rendering Power+ just in case something breaks in dtps.render)
  62. //REMOVED 8.13.2019
  63. //jQuery.getScript("https://fnxldqd4m5fr.statuspage.io/embed/script.js")
  64.  
  65. //Shows the Power+ changelog modal
  66. dtps.changelog = function () {
  67. fluid.cards.close(".card.focus")
  68. fluid.modal(".card.changelog");
  69. };
  70.  
  71. //get url paramaters
  72. dtps.getParams = function () {
  73. var vars = {};
  74. var parts = window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi, function (m, key, value) {
  75. vars[key] = value;
  76. });
  77. return vars;
  78. }
  79.  
  80. //dtps.authenticate and dtps.webReq work together to ensure the user is authenticated
  81. //the dtps client must not be embedded for dtps.authenticate to work
  82. dtps.authenticate = function (cb) {
  83. if (window.localStorage.accessToken) {
  84. //manually defined access token
  85. cb();
  86. } else {
  87. var params = dtps.getParams();
  88. window.history.replaceState(null, null, window.location.pathname);
  89. if (params.code && (params.state == window.localStorage.authState)) {
  90. //get tokens
  91. dtps.webReq("backend", "/login/oauth2/token?client_id=" + dtps.auth.client_id + "&redirect_uri=" + dtps.auth.uri + "&grant_type=authorization_code&code=" + params.code, function (res) {
  92. var data = JSON.parse(res);
  93. window.localStorage.setItem("access_token", data.access_token);
  94. window.localStorage.setItem("refresh_token", data.refresh_token);
  95. window.localStorage.setItem("expires_at", new Date(new Date().getTime() + (1000 * data.expires_in)));
  96. dtps.accessToken = data.access_token;
  97. cb();
  98. });
  99. } else {
  100. if (window.localStorage.refresh_token) {
  101. //refresh token exists
  102. if (window.localStorage.expires_at > new Date().getTime()) {
  103. //access token not expired
  104. dtps.auth.accessToken = window.localStorage.access_token;
  105. } else {
  106. //get new access token
  107. //COMING SOON
  108. }
  109. } else {
  110. //show login screen
  111. var state = "WinterCreek" + Math.floor(1000 + Math.random() * 9000);
  112. window.localStorage.authState = state;
  113. window.location.href = dtps.auth.canvasURL + "/login/oauth2/auth?client_id=" + dtps.auth.client_id + "&scope=" + dtps.auth.scope.join(" ") + "&purpose=" + encodeURI(navigator.platform) + "&response_type=code&state=" + state + "&redirect_uri=" + dtps.auth.uri
  114.  
  115. }
  116. }
  117. }
  118. }
  119.  
  120. //Logs debugging messags to both the normal JS console and also Power+'s included debugging log
  121. dtps.log = function (msg) {
  122. console.log("[DTPS" + dtps.trackSuffix + "] ", msg);
  123. if (typeof msg !== "object") { try { jQuery("span.log").html(`<p>[DTPS` + dtps.trackSuffix + `] ` + msg + `</p>` + jQuery("span.log").html()); } catch (e) { } }
  124. }
  125.  
  126. //Renders Power+ first run stuff
  127. dtps.firstrun = function () {
  128. if (dtps.embedded) {
  129. jQuery("body").append(`<div id="dtpsNativeAlert" class="ui-dialog ui-widget ui-widget-content ui-corner-all ui-dialog-buttons" tabindex="-1" aria-hidden="false" style="outline: 0px; z-index: 5000; height: auto; width: 500px; margin-top: 100px; top: 0; margin-left: calc(50% - 250px); display: block;">
  130. <div class="ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix"><span id="ui-id-1" class="ui-dialog-title" role="heading">Welcome to Power+` + dtps.trackSuffix + `</span><button onclick="jQuery('#dtpsNativeAlert').remove();jQuery('#dtpsNativeOverlay').remove();" class="ui-dialog-titlebar-close ui-corner-all"><span class="ui-icon ui-icon-closethick">close</span></button></div>
  131. <form id="new_course_form" class="bootstrap-form form-horizontal ui-dialog-content ui-widget-content" data-turn-into-dialog="{&quot;width&quot;:500,&quot;resizable&quot;:false}" style="width: auto; min-height: 0px; height: auto; display: block;" action="/courses" accept-charset="UTF-8" method="post" aria-expanded="true" scrolltop="0" scrollleft="0">
  132. <h5>` + dtps.readableVer + `</h5>
  133. <p>Things to keep in mind when testing Power+` + dtps.trackSuffix + `</p>
  134. <li>Power+` + dtps.trackSuffix + ` can't fully replace Canvas yet. Many Canvas features are not included in Power+` + dtps.trackSuffix + `.</li>
  135. <li>The Power+ gradebook is being temporarily disabled and will return later this year.</li>
  136. <li style="color: red;"><b>Power+` + dtps.trackSuffix + ` is still in development. There will be a lot of bugs and missing features, especially in the first month of the school year.</b></li>
  137. <li><b>Power+` + dtps.trackSuffix + ` may have bugs that cause it to display inaccurate information. Use Power+` + dtps.trackSuffix + ` at your own risk.</b></li>
  138. </form><div class="ui-dialog-buttonpane ui-widget-content ui-helper-clearfix"><div class="ui-dialog-buttonset"><button onclick="jQuery('#dtpsNativeAlert').remove();jQuery('#dtpsNativeOverlay').remove();" type="button" data-text-while-loading="Cancel" class="btn dialog_closer ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only" role="button" aria-disabled="false"><span class="ui-button-text">Cancel</span></button><button onclick="localStorage.setItem('dtpsInstalled', 'true'); dtps.render();" type="button" data-text-while-loading="Loading Power+..." class="btn btn-primary button_type_submit ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only" role="button" aria-disabled="false"><span class="ui-button-text">Continue</span></button></div></div></div>
  139. <div id="dtpsNativeOverlay" class="ui-widget-overlay" style="width: 100%; height: 100%; z-index: 500;"></div>`)
  140. } else {
  141. $("#welcomeVer").html(dtps.readableVer)
  142. fluid.splash("#welcomeToDtps")
  143. }
  144. };
  145.  
  146. //Displays a native Canvas alert (cannot be used after Power+ is rendered / dtps.render)
  147. //On non-embedded clients, this displays a Fluid UI Alert
  148. dtps.nativeAlert = function (text, sub, loadingSplash) {
  149. if (text == undefined) var text = "";
  150. if (sub == undefined) var sub = "";
  151. if (loadingSplash) {
  152. if (dtps.embedded) {
  153. jQuery("body").append(`<div id="dtpsNativeOverlay" class="ui-widget-overlay" style="width: 100%;height: 100%;z-index: 500;background: rgba(31, 31, 31, 0.89);">&nbsp;<h1 style="position: fixed;font-size: 125px;background: -webkit-linear-gradient(rgb(255, 167, 0), rgb(255, 244, 0));-webkit-background-clip: text;-webkit-text-fill-color: transparent;font-weight: bolder;font-family: Product sans;text-align: center;top: 200px;width: 100%;">Power+</h1><h5 style="font-family: Product sans;font-size: 30px;color: gray;width: 100%;text-align: center;position: fixed;top: 375px;">` + sub + `</h5><div class="spinner" style="margin-top: 500px;"></div>
  154. <style>@font-face{font-family: 'Product sans'; font-display: auto; font-style: normal; font-weight: 400; src: url(https://fluid.js.org/product-sans.ttf) format('truetype');}.spinner { width: 40px; height: 40px; margin: 100px auto; background-color: gray; border-radius: 100%; -webkit-animation: sk-scaleout 1.0s infinite ease-in-out; animation: sk-scaleout 1.0s infinite ease-in-out; } @-webkit-keyframes sk-scaleout { 0% { -webkit-transform: scale(0) } 100% { -webkit-transform: scale(1.0); opacity: 0; } } @keyframes sk-scaleout { 0% { -webkit-transform: scale(0); transform: scale(0); } 100% { -webkit-transform: scale(1.0); transform: scale(1.0); opacity: 0; } }</style></div>`)
  155. }
  156. } else {
  157. jQuery("body").append(`<div id="dtpsNativeAlert" class="ui-dialog ui-widget ui-widget-content ui-corner-all ui-dialog-buttons" tabindex="-1" aria-hidden="false" style="outline: 0px; z-index: 5000; height: auto; width: 500px; margin-top: 100px; top: 0; margin-left: calc(50% - 250px); display: block;">
  158. <div class="ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix"><span id="ui-id-1" class="ui-dialog-title" role="heading">Power+</span></div>
  159. <form id="new_course_form" class="bootstrap-form form-horizontal ui-dialog-content ui-widget-content" data-turn-into-dialog="{&quot;width&quot;:500,&quot;resizable&quot;:false}" style="width: auto; min-height: 0px; height: auto; display: block;" action="/courses" accept-charset="UTF-8" method="post" aria-expanded="true" scrolltop="0" scrollleft="0">
  160. <h4>` + text + `</h4>
  161. <p>` + sub + `</p>
  162. </form></div>
  163. <div id="dtpsNativeOverlay" class="ui-widget-overlay" style="width: 100%; height: 100%; z-index: 500;"></div>`)
  164. }
  165. };
  166.  
  167. //All Canvas & LMS data is sent through dtps.webReq
  168. dtps.requests = {};
  169. dtps.http = {};
  170. dtps.webReq = function (req, url, callback, q) {
  171. if ((dtps.requests[url] == undefined) || url.includes("|")) {
  172. //Use backend request instead of canvas request for standalone Power+
  173. if ((req == "canvas") && !dtps.embedded) req = "backend"
  174. //"Canvas" request type for making a GET request to the Canvas API
  175. if (req == "canvas") {
  176. dtps.log("Making DTPS Canvas web request")
  177. dtps.http[url] = new XMLHttpRequest();
  178. dtps.http[url].onreadystatechange = function () {
  179. if (this.readyState == 4) {
  180. if (this.status == 200) {
  181. if (callback) callback(this.responseText, q);
  182. dtps.requests[url] = this.responseText;
  183. dtps.log("Returning DTPS data")
  184. } else {
  185. if (callback) callback(JSON.stringify({ error: this.status }), q);
  186. dtps.requests[url] = JSON.stringify({ error: this.status });
  187. dtps.log("DTPS webReq error" + this.status)
  188. }
  189. }
  190. };
  191. dtps.http[url].open("GET", url, true);
  192. dtps.http[url].setRequestHeader("Accept", "application/json+canvas-string-ids")
  193. dtps.http[url].send();
  194. }
  195. //"backend" request type for making a GET request to the Canvas API w/ jottocraft.com backend
  196. if (req == "backend") {
  197. dtps.http[url] = new XMLHttpRequest();
  198. dtps.http[url].onreadystatechange = function () {
  199. if (this.readyState == 4) {
  200. if (this.status == 200) {
  201. if (callback) callback(this.responseText, q);
  202. dtps.requests[url] = this.responseText;
  203. dtps.log("Returning DTPS data")
  204. } else {
  205. if (callback) callback(JSON.stringify({ error: this.status }), q);
  206. dtps.requests[url] = JSON.stringify({ error: this.status });
  207. dtps.log("DTPS webReq error" + this.status)
  208. }
  209. }
  210. };
  211. dtps.http[url].open("GET", "https://lms.jottocraft.com:2755" + url, true);
  212. dtps.http[url].setRequestHeader("Accept", "application/json+canvas-string-ids");
  213. dtps.http[url].setRequestHeader("dtps", "WinterCreek/" + dtps.ver);
  214. dtps.http[url].setRequestHeader("Authorization", "Bearer " + dtps.auth.accessToken);
  215. dtps.http[url].send();
  216. }
  217. //"canSUBMIT" request type for submitting assignments using the Canvas API (POST request)
  218. if (req == "canSUBMIT") {
  219. console.log("canSUBMIT")
  220. dtps.http[url] = new XMLHttpRequest();
  221. dtps.http[url].onreadystatechange = function () {
  222. if (this.readyState == 4) {
  223. if (this.status == 200) {
  224. if (callback) callback(this.responseText, q);
  225. dtps.requests[url] = this.responseText;
  226. dtps.log("Returning DTPS data")
  227. } else {
  228. if (callback) callback(JSON.stringify({ error: this.status }), q);
  229. dtps.requests[url] = JSON.stringify({ error: this.status });
  230. dtps.log("DTPS webReq error" + this.status)
  231. }
  232. }
  233. };
  234. dtps.http[url].open("POST", url.split("|")[0], true);
  235. dtps.http[url].setRequestHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3")
  236. dtps.http[url].setRequestHeader("Content-Type", "application/x-www-form-urlencoded")
  237. dtps.http[url].setRequestHeader("Cache-Control", "max-age=0")
  238. dtps.http[url].send(url.split("|")[1]);
  239. }
  240. } else {
  241. if (callback) callback(dtps.requests[url], q);
  242. }
  243. }
  244.  
  245. //Calculates the class grade based on Outcomes results from Canvas
  246. dtps.computeClassGrade = function (num, renderSidebar) {
  247. dtps.webReq("canvas", "/api/v1/courses/" + dtps.classes[num].id + "/outcome_rollups?user_ids[]=" + dtps.user.id + "&include[]=outcomes", function (resp, classNum) {
  248. var data = JSON.parse(resp);
  249. console.log(data);
  250. var rollups = data.rollups[0];
  251.  
  252. //array of outcome averages sorted from highest to lowest
  253. var rollupScores = rollups.scores.map(function (score) { return score.score; }).sort((a, b) => b - a);
  254.  
  255. //the highest value 75% of outcomes are greater than or equal to
  256. //number75thresh is the percentage of assignments >= number75. number75thresh is always >= 0.75 && <= 1
  257. var number75 = null;
  258. var number75thresh = null;
  259.  
  260. //test values
  261. var testValues = [3.5, 3, 2.5]
  262.  
  263. for (var i = 0; i < testValues.length; i++) {
  264. //make sure a higher number for number75 isn't already found
  265. if (number75 == null) {
  266. //array of numbers equal to or greater than the testing value
  267. var equalOrGreater = [];
  268.  
  269. //check every score to see if it is >= the test value
  270. for (var ii = 0; ii < rollupScores.length; ii++) {
  271. if (rollupScores[ii] >= testValues[i]) equalOrGreater.push(rollupScores[ii]);
  272. }
  273.  
  274. //if at least 75% of the outcomes are >= test value, number75 is the test value
  275. if ((equalOrGreater.length / rollupScores.length) >= 0.75) {
  276. number75 = testValues[i];
  277. number75thresh = (equalOrGreater.length / rollupScores.length);
  278. }
  279. }
  280. }
  281.  
  282. //since rollupScores is sorted greatest to least, the last value is the smallest
  283. var lowestValue = rollupScores[rollupScores.length - 1];
  284.  
  285. //get letter grade
  286. var letter = "I";
  287. if (rollupScores.length == 0) letter = "--";
  288. if ((number75 >= 2.5) && (lowestValue >= 2)) letter = "C";
  289. if ((number75 >= 3) && (lowestValue >= 2)) letter = "B-";
  290. if ((number75 >= 3) && (lowestValue >= 2.25)) letter = "B";
  291. if ((number75 >= 3) && (lowestValue >= 2.5)) letter = "B+";
  292. if ((number75 >= 3.5) && (lowestValue >= 2.5)) letter = "A-";
  293. if ((number75 >= 3.5) && (lowestValue >= 3)) letter = "A";
  294.  
  295. if (fluid.get('pref-calcGrades') !== "false") dtps.classes[classNum].letter = letter;
  296.  
  297. //store grade calculation variables to show them in the gradebook
  298. dtps.classes[classNum].gradeCalc = {
  299. lowestValue: lowestValue,
  300. number75: (number75 !== null ? number75 : ""),
  301. number75thresh: number75thresh,
  302. //number75percent: (number75thresh-0.75)/(1-0.75),
  303. //lowestValuePercent: (),
  304. }
  305.  
  306. //if there are no outcomes, remember this so the grades tab can be hidden
  307. if (!data.linked.outcomes.length) dtps.classes[classNum].noOutcomes = true;
  308.  
  309. dtps.computedClassGrades++;
  310. if (renderSidebar) {
  311. if (letter !== "--") {
  312. if (letter.includes("A")) gpa.push(4)
  313. if (letter.includes("B")) gpa.push(3)
  314. if (letter.includes("C")) gpa.push(2)
  315. if (letter.includes("I")) gpa.push(0)
  316. if (letter == "I") score = 0;
  317. if (letter == "C") score = 75;
  318. if (letter == "B-") score = 80;
  319. if (letter == "B") score = 85;
  320. if (letter == "B+") score = 88;
  321. if (letter == "A-") score = 90;
  322. if (letter == "A") score = 95;
  323. dtps.gradeHTML.push(`<div style="cursor: auto; background-color: var(--norm);" class="progressBar big ` + dtps.classes[classNum].col + `">
  324. <div style="color: var(--dark);" class="progressLabel">` + dtps.classes[classNum].subject + `</div>
  325. <div class="progress" style="background-color: var(--light); width: calc(` + score + `% - 300px);"></div></div>`)
  326. }
  327. }
  328. if ((dtps.computedClassGrades == dtps.classes.length) && renderSidebar) {
  329. dtps.showClasses(true);
  330. var total = 0;
  331. for (var i = 0; i < gpa.length; i++) {
  332. total += gpa[i];
  333. }
  334. dtps.gpa = (total / gpa.length).toFixed(2);
  335. }
  336. }, num);
  337. }
  338.  
  339. dtps.explore = function (path) {
  340. dtps.webReq("canvas", path, function (resp) {
  341. var data = JSON.parse(resp);
  342. $("#explorerData").html(JSON.stringify(data, null, "\t"))
  343. });
  344. }
  345.  
  346. //Convert default Canvas colors to optimized Power+ filters based on hex values for default colors
  347. //Not all Canvas colors are listed here. Only existing Power+ colors and colors that look bad have optimized filters.
  348. dtps.filter = function (color) {
  349. var filter = undefined;
  350. if (color == "#009606") filter = "filter_3"
  351. if (color == "#8D9900") filter = "filter_2"
  352. if ((color == "#D97900") || (color == "#FD5D10")) filter = "filter_1"
  353. if (color == "#F06291") filter = "filter_6"
  354. if (color == "#FF2717") filter = "filter_0"
  355. if (color == "#BD3C14") filter = "filter_7"
  356. if (color == "#8F3E97") filter = "filter_5"
  357. return filter;
  358. }
  359.  
  360. //Get all JavaScript libraries
  361. dtps.JS = function (cb) {
  362. if (dtps.embedded) {
  363. jQuery.getScript('https://dtps.js.org/fluid.js');
  364. jQuery.getScript("https://unpkg.com/sweetalert/dist/sweetalert.min.js")
  365. jQuery.getScript("https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.22.2/moment.min.js", function () {
  366. jQuery.getScript("https://cdnjs.cloudflare.com/ajax/libs/fullcalendar/3.9.0/fullcalendar.min.js")
  367. })
  368. jQuery.getScript('https://cdnjs.cloudflare.com/ajax/libs/fuse.js/3.3.0/fuse.min.js');
  369.  
  370. jQuery.getScript("https://www.googletagmanager.com/gtag/js?id=UA-105685403-3");
  371.  
  372. jQuery.getScript("https://cdn.jottocraft.com/tinycolor.js", cb);
  373. } else {
  374. cb();
  375. }
  376. }
  377.  
  378. //Starts Power+, and renders if running a non-embedded client
  379. dtps.init = function () {
  380. dtps.log("Starting DTPS " + dtps.readableVer + "...");
  381.  
  382. //add basic explorer items
  383. dtps.explorer.push({ name: "/users/self", path: "/api/v1/users/self" });
  384. dtps.explorer.push({ name: "/users/self/dashboard_positions", path: "/api/v1/users/self/dashboard_positions" });
  385. dtps.explorer.push({ name: "/users/self/colors", path: "/api/v1/users/self/colors" });
  386. dtps.explorer.push({ name: "/courses", path: "/api/v1/courses?include[]=total_scores&include[]=public_description&include[]=favorites&include[]=total_students&include[]=account&include[]=teachers&include[]=course_image&include[]=syllabus_body&include[]=tabs" });
  387.  
  388. dtps.webReq("canvas", "/api/v1/users/self", function (user) {
  389. fluidThemes = [["rainbow"]];
  390. fluidAutoLoad = false;
  391.  
  392. document.addEventListener("fluidTheme", function (data) {
  393. if (dtps.oldTheme !== data.detail) {
  394. //theme change
  395. console.log("[DTPS] Fluid UI Theme Change")
  396. dtps.oldTheme = data.detail;
  397. var next = window.getComputedStyle(document.getElementsByClassName("background")[0]).getPropertyValue("--grad")
  398. if (dtps.selectedClass !== "dash") next = "linear-gradient(to bottom right, " + window.getComputedStyle(document.getElementsByClassName("background")[0]).getPropertyValue($("body").hasClass("midnight") ? "--dark" : "--light") + ", " + ($("body").hasClass("dark") ? "var(--background, #252525)" : "var(--background, white)") + ")"
  399. if (dtps.selectedClass !== "dash") $('body').removeClass('dashboard');
  400. $(".background").css("background", next)
  401. dtps.chroma();
  402. }
  403. })
  404.  
  405. dtps.user = JSON.parse(user);
  406. sudoers = ["669", "672", "209"]
  407. if (sudoers.includes(dtps.user.id)) { jQuery("body").addClass("sudo"); dtps.log("Sudo mode enabled"); }
  408. marketers = ["669", "672", "209"]
  409. if (marketers.includes(dtps.user.id)) { jQuery("body").addClass("marketer"); dtps.log("Promotional marketing mode enabled"); }
  410. contributors = ["669"]
  411. if (contributors.includes(dtps.user.id)) { jQuery("body").addClass("contributor"); }
  412. if (dtps.user.id == "669") { jQuery("body").addClass("dev"); dtps.log("Dev mode enabled"); }
  413. if ((dtps.trackSuffix !== "") && (dtps.trackSuffix !== "GM")) jQuery("body").addClass("prerelease");
  414. if (sudoers.includes(dtps.user.id)) jQuery("body").addClass("prerelease");
  415. $ = jQuery;
  416. var min = new Date().getMinutes();
  417. if (String(min).length < 2) min = Number("0" + String(min))
  418. var time = Number(String(new Date().getHours()) + String(min))
  419. dtps.period = null;
  420. if ((new Date().getDay() > 0) && (new Date().getDay() < 6)) {
  421. //Weekday
  422. if (new Date().getDay() == 3) {
  423. //Wednesday
  424. if ((time > 0844) && (time < 0911)) dtps.period = 7;
  425. if ((time > 0912) && (time < 1003)) dtps.period = 1;
  426. if ((time > 1004) && (time < 1056)) dtps.period = 2;
  427. if ((time > 1057) && (time < 1149)) dtps.period = 3;
  428. if ((time > 1220) && (time < 1312)) dtps.period = 4;
  429. if ((time > 1313) && (time < 1405)) dtps.period = 5;
  430. if ((time > 1406) && (time < 1458)) dtps.period = 6;
  431. } else {
  432. if (new Date().getDay() !== 4) {
  433. //M, TU, F
  434. if ((time > 0844) && (time < 0916)) dtps.period = 7;
  435. if ((time > 0917) && (time < 1013)) dtps.period = 1;
  436. if ((time > 1022) && (time < 1118)) dtps.period = 2;
  437. if ((time > 1119) && (time < 1215)) dtps.period = 3;
  438. if ((time > 1246) && (time < 1342)) dtps.period = 4;
  439. if ((time > 1343) && (time < 1439)) dtps.period = 5;
  440. if ((time > 1440) && (time < 1536)) dtps.period = 6;
  441. }
  442. }
  443. }
  444. if (dtps.period && (String(localStorage.dtpsSchedule).startsWith("{"))) {
  445. dtps.currentClass = JSON.parse(localStorage.dtpsSchedule)[dtps.period];
  446. if (dtps.currentClass) {
  447. $("#headText").html(("Period " + dtps.period).replace("Period 7", "@d.tech"));
  448. }
  449. }
  450. if (!dtps.currentClass) {
  451. dtps.selectedClass = "dash";
  452. dtps.selectedContent = "stream";
  453. }
  454. dtps.masterContent = "assignments";
  455. if (!dtps.embedded) { dtps.renderLite(); dtps.showClasses(); }
  456.  
  457. dtps.shouldRender = true;
  458. dtps.showChangelog = false;
  459. dtps.first = false;
  460. if (window.localStorage.dtpsInstalled !== "true") {
  461. if (!dtps.embedded) dtps.shouldRender = false;
  462. dtps.first = true;
  463. }
  464. if (dtps.first && !dtps.embedded) dtps.firstrun();
  465. if (Number(window.localStorage.dtps) < dtps.ver) {
  466. dtps.log("New release")
  467. dtps.showChangelog = true;
  468. if (dtps.shouldRender) dtps.nativeAlert("Loading...", "Updating to Power+ " + dtps.readableVer, true);
  469. }
  470. if (dtps.shouldRender && !dtps.showChangelog) {
  471. dtps.nativeAlert("Loading...", undefined, true);
  472. }
  473. dtps.JS(function () {
  474.  
  475. window.dataLayer = window.dataLayer || [];
  476. function gtag() { dataLayer.push(arguments); }
  477. var configTmp = {
  478. 'page_title': 'portal',
  479. 'page_path': '/portal',
  480. 'anonymize_ip': true
  481. }
  482. if (dtps.trackSuffix !== "") {
  483. configTmp = {
  484. //'page_title' : 'prerelease',
  485. //'page_path': '/prerelease',
  486. 'page_title': 'canvasdp',
  487. 'page_path': '/canvasdp',
  488. 'anonymize_ip': true
  489. }
  490. }
  491. gtag('config', 'UA-105685403-3', configTmp);
  492.  
  493. dtps.webReq("canvas", "/api/v1/users/self/colors", function (colorsResp) {
  494. dtps.webReq("canvas", "/api/v1/users/self/dashboard_positions", function (dashboardResp) {
  495. dtps.webReq("canvas", "/api/v1/courses?include[]=total_scores&include[]=public_description&include[]=favorites&include[]=total_students&include[]=account&include[]=teachers&include[]=course_image&include[]=syllabus_body&include[]=tabs", function (resp) {
  496. dtps.classesReady = 0;
  497. dtps.colorCSS = [];
  498. dtps.gpa = null;
  499. gpa = [];
  500. var colors = JSON.parse(colorsResp);
  501. var dashboard = JSON.parse(dashboardResp);
  502. var data = JSON.parse(resp);
  503. dtps.gradeHTML = [];
  504. data.sort(function (a, b) {
  505. var keyA = dashboard.dashboard_positions["course_" + a.id],
  506. keyB = dashboard.dashboard_positions["course_" + b.id];
  507. if (keyA < keyB) return -1;
  508. if (keyA > keyB) return 1;
  509. return 0;
  510. });
  511. for (var i = 0; i < data.length; i++) {
  512. var name = data[i].name;
  513. var subject = null;
  514. var icon = null;
  515. if (name.includes("Physics")) { var subject = "Physics"; var icon = "experiment"; }; if (name.includes("English")) { var subject = "English"; var icon = "library_books" }; if (name.includes("Physical Education")) { var subject = "PE"; var icon = "directions_run"; };
  516. if (name.includes("Prototyping")) { var subject = "Prototyping"; var icon = "drive_file_rename_outline"; }; if (name.includes("Algebra")) { var subject = "Algebra"; }; if (name.includes("Algebra 2")) { var subject = "Algebra 2"; };
  517. if (name.includes("Spanish")) { var subject = "Spanish" }; if (name.includes("@") || name.includes("dtech")) { var subject = "@d.tech" }; if (name.includes("Environmental")) { var subject = "Environmental Science" };
  518. if (name.includes("Robotics")) { var subject = "Robotics" }; if (name.includes("Chemistry")) { var subject = "Chemistry" }; if (name.includes("Biology")) { var subject = "Biology" }; if (name.includes("Engineering")) { var subject = "Engineering" }; if (name.includes("Geometry")) { var subject = "Geometry" };
  519. if (name.includes("Photography")) { var subject = "Photography" }; if (name.includes("World History")) { var subject = "World History" }; if (name.includes("U.S. History")) { var subject = "US History" };
  520. if (name.includes("Calculus")) { var subject = "Calculus" }; if (name.toUpperCase().includes("CALCULUS") && name.toUpperCase().includes("PRE")) { var subject = "Precalculus" }; if (name.includes("Statistics")) { var subject = "Advanced Statistics" };
  521. if (name.includes("Model United Nations")) { var subject = "Model UN" }; if (name.includes("Government")) { var subject = "Government" }; if (name.includes("Economics")) { var subject = "Economics" };
  522. if (data[i].name !== data[i].course_code) {
  523. //Canvas class manually renamed
  524. subject = null;
  525. }
  526. if (subject == null) var subject = name;
  527. var filter = "filter_" + colors.custom_colors["course_" + data[i].id].toLowerCase().replace("#", "");
  528. //Suport Power+ v1.x.x Colors by detecting if the user selects a native canvas color
  529. if (dtps.filter(colors.custom_colors["course_" + data[i].id])) filter = dtps.filter(colors.custom_colors["course_" + data[i].id]);
  530. pagesTab = false;
  531. for (var ii = 0; ii < data[i].tabs.length; ii++) {
  532. if (data[i].tabs[ii].id == "pages") pagesTab = true;
  533. }
  534. dtps.classes.push({
  535. name: name,
  536. subject: subject,
  537. description: data[i].public_description,
  538. totalStudents: data[i].total_students,
  539. favorite: data[i].is_favorite,
  540. defaultView: data[i].default_view,
  541. icon: icon,
  542. col: filter,
  543. pagesTab: pagesTab,
  544. syllabus: data[i].syllabus_body,
  545. norm: colors.custom_colors["course_" + data[i].id],
  546. light: tinycolor(colors.custom_colors["course_" + data[i].id]).brighten(20).toHexString(),
  547. dark: tinycolor(colors.custom_colors["course_" + data[i].id]).darken(20).toHexString(),
  548. isBright: (dtps.filter(colors.custom_colors["course_" + data[i].id]) ? false : !tinycolor(colors.custom_colors["course_" + data[i].id]).isDark()),
  549. id: data[i].id,
  550. //collection of data used to calculate a letter grade
  551. grades: {},
  552. grade: (data[i].enrollments[0].computed_current_score ? data[i].enrollments[0].computed_current_score : "--"),
  553. letter: (data[i].enrollments[0].computed_current_grade ? data[i].enrollments[0].computed_current_grade : (fluid.get("pref-calcGrades") !== "false" ? false : "--")),
  554. num: i,
  555. tmp: {},
  556. image: data[i].image_download_url,
  557. teacher: {
  558. name: data[i].teachers[0].display_name,
  559. prof: data[i].teachers[0].avatar_image_url
  560. }
  561. })
  562. if (!dtps.filter(colors.custom_colors["course_" + data[i].id])) {
  563. dtps.colorCSS.push(`\n.` + dtps.classes[i].col + ` {
  564. --light: ` + dtps.classes[i].light + `;
  565. --norm: ` + dtps.classes[i].norm + `;
  566. --dark: ` + dtps.classes[i].dark + `;
  567. --filterText: ` + (dtps.classes[i].isBright ? dtps.classes[i].dark : "white") + `;
  568. --grad: linear-gradient(to bottom right, ` + dtps.classes[i].light + `, ` + dtps.classes[i].dark + `);;
  569. }`);
  570. }
  571. dtps.computedClassGrades = 0;
  572. if (fluid.get("pref-calcGrades") !== "false") dtps.computeClassGrade(i, true);
  573. dtps.classStream(i, true);
  574. if (dtps.currentClass == data[i].id) {
  575. dtps.selectedClass = i;
  576. dtps.selectedContent = "stream";
  577. dtps.chroma();
  578. dtps.classStream(i);
  579. }
  580. }
  581. dtps.log("Grades loaded: ", dtps.classes);
  582.  
  583. if (dtps.shouldRender) dtps.render();
  584. if (dtps.first && dtps.embedded) dtps.firstrun();
  585. });
  586. });
  587. });
  588. });
  589. });
  590.  
  591. }
  592.  
  593. //Checks if all classes have loaded to determine if the dashboard is still loading
  594. dtps.readyInterval = "n/a";
  595. dtps.checkReady = function (num) {
  596. dtps.log(num + " reporting as READY total of " + dtps.classesReady);
  597. if ((dtps.selectedClass == "dash") && (dtps.classesReady == dtps.classes.length)) {
  598. dtps.log("All classes ready, loading master stream");
  599. dtps.masterStream(true);
  600. } else {
  601. if ((dtps.selectedClass == "dash") && (dtps.classesReady < dtps.classes.length)) {
  602. dtps.masterStream();
  603. }
  604. }
  605. }
  606.  
  607. //Loads the list of pages for a class
  608. dtps.loadPages = function (num) {
  609. if ((dtps.selectedClass == num) && (dtps.selectedContent == "pages")) {
  610. jQuery(".sidebar").html(`
  611. <div class="classDivider"></div>
  612. <div class="spinner"></div>
  613. `);
  614. jQuery(".classContent").html("");
  615. }
  616. dtps.webReq("canvas", "/api/v1/courses/" + dtps.classes[num].id + "/pages", function (resp) {
  617. var data = JSON.parse(resp);
  618. if (data.error) {
  619. jQuery(".sidebar").html(`<div onclick="dtps.selectedContent = 'stream'; dtps.chroma(); dtps.classStream(dtps.selectedClass);" class="class back">
  620. <div class="name">Classes</div>
  621. <div class="grade"><i class="material-icons">keyboard_arrow_left</i></div>
  622. </div>
  623. <div class="classDivider"></div>
  624. <h5 style="text-align: center; font-weight: bold;margin-left: -10px;">No pages found</h5><p style="text-align: center;margin-left: -10px;">This class doesn't have any pages</p>`)
  625. } else {
  626. dtps.classes[num].pages = [];
  627. dtps.classes[num].pagelist = [];
  628. for (var i = 0; i < data.length; i++) {
  629. dtps.classes[num].pages.push({
  630. id: data[i].page_id,
  631. title: data[i].title,
  632. content: "",
  633. num: i
  634. });
  635. dtps.classes[num].pagelist.push(`
  636. <div onclick="dtps.selectedPage = ` + data[i].page_id + `" class="class ` + data[i].page_id + `">
  637. <div class="name">` + data[i].title + `</div>
  638. <div class="grade"><i class="material-icons">notes</i></div>
  639. </div>
  640. `);
  641. }
  642. if ((dtps.selectedClass == num) && (dtps.selectedContent == "pages")) {
  643. jQuery(".sidebar").html(`<div onclick="dtps.selectedContent = 'stream'; dtps.chroma(); dtps.classStream(dtps.selectedClass);" class="class back">
  644. <div class="name">Classes</div>
  645. <div class="grade"><i class="material-icons">keyboard_arrow_left</i></div>
  646. </div>
  647. <div class="classDivider"></div>
  648. ` + dtps.classes[num].pagelist.join(""))
  649. }
  650.  
  651. $(".class:not(.back)").click(function (event) {
  652. $(this).siblings().removeClass("active")
  653. $(this).addClass("active")
  654. dtps.getPage(dtps.classes[dtps.selectedClass].id, dtps.selectedPage);
  655. });
  656. }
  657. });
  658. }
  659.  
  660. //Loads the list of discussion topics for a class
  661. dtps.loadTopics = function (num) {
  662. if ((dtps.selectedClass == num) && (dtps.selectedContent == "discuss")) {
  663. jQuery(".sidebar").html(`
  664. <div class="classDivider"></div>
  665. <div class="spinner"></div>
  666. `);
  667. jQuery(".classContent").html("");
  668. }
  669. dtps.webReq("canvas", "/api/v1/courses/" + dtps.classes[num].id + "/discussion_topics", function (resp) {
  670. var data = JSON.parse(resp);
  671. if (data.error || (data.length == 0)) {
  672. jQuery(".sidebar").html(`<div onclick="dtps.selectedContent = 'stream'; dtps.chroma(); dtps.classStream(dtps.selectedClass);" class="class back">
  673. <div class="name">Classes</div>
  674. <div class="grade"><i class="material-icons">keyboard_arrow_left</i></div>
  675. </div>
  676. <div onclick="window.open('/courses/` + dtps.classes[num].id + `/discussion_topics/new')" class="class back">
  677. <div class="name">New discussion</div>
  678. <div class="grade"><i class="material-icons">add</i></div>
  679. </div>
  680. <div class="classDivider"></div>
  681. <h5 style="text-align: center; font-weight: bold; margin-left: -10px;">No discussions found</h5><p style="text-align: center;margin-left: -10px;">There aren't any discussion topics</p>`)
  682. } else {
  683. dtps.classes[num].topics = [];
  684. dtps.classes[num].topicList = [];
  685. for (var i = 0; i < data.length; i++) {
  686. dtps.classes[num].topics[data[i].id] = {
  687. id: data[i].id,
  688. title: data[i].title,
  689. content: data[i].message,
  690. author: {
  691. name: data[i].author.display_name,
  692. prof: data[i].author.avatar_image_url
  693. },
  694. locked: data[i].locked_for_user,
  695. requirePost: data[i].require_initial_post,
  696. num: i
  697. };
  698. dtps.classes[num].topicList.push(`
  699. <div onclick="dtps.selectedPage = ` + data[i].id + `" class="class ` + data[i].id + `">
  700. <div class="name">` + data[i].title + `</div>
  701. <div class="grade"><i style="font-family: 'Material Icons Extended';" class="material-icons">` + (data[i].locked_for_user ? "lock_outline" : "chat_bubble_outline") + `</i></div>
  702. </div>
  703. `);
  704. }
  705. if ((dtps.selectedClass == num) && (dtps.selectedContent == "discuss")) {
  706. jQuery(".sidebar").html(`<div onclick="dtps.selectedContent = 'stream'; dtps.chroma(); dtps.classStream(dtps.selectedClass);" class="class back">
  707. <div class="name">Classes</div>
  708. <div class="grade"><i class="material-icons">keyboard_arrow_left</i></div>
  709. </div>
  710. <div onclick="window.open('/courses/` + dtps.classes[num].id + `/discussion_topics/new')" class="class back">
  711. <div class="name">New discussion</div>
  712. <div class="grade"><i class="material-icons">add</i></div>
  713. </div>
  714. <div class="classDivider"></div>
  715. ` + dtps.classes[num].topicList.join(""))
  716. }
  717.  
  718. $(".class:not(.back)").click(function (event) {
  719. $(this).siblings().removeClass("active")
  720. $(this).addClass("active")
  721. dtps.getTopic(dtps.selectedClass, dtps.selectedPage);
  722. });
  723. }
  724. });
  725. }
  726.  
  727. //Gets and displays a discussion
  728. dtps.getTopic = function (num, id, fromModuleStream) {
  729. var classID = dtps.classes[num].id
  730. if (id == undefined) var id = dtps.selectedPage;
  731. if ((dtps.classes[dtps.selectedClass].id == classID) && ((dtps.selectedContent == "discuss") || fromModuleStream)) {
  732. jQuery(".classContent").html(`<div class="spinner"></div>`);
  733. }
  734. if (dtps.classes[num].topics[id].locked) {
  735. alert(dtps.classes[num].topics[id].content);
  736. jQuery(".classContent").html("");
  737. } else {
  738. var spinnerTmp = true;
  739. dtps.webReq("canvas", "/api/v1/courses/" + classID + "/discussion_topics/" + id + "/view", function (resp) {
  740. var data = JSON.parse(resp);
  741. if ((dtps.classes[dtps.selectedClass].id == classID) && ((dtps.selectedContent == "discuss") || fromModuleStream)) {
  742. $(".cacaoBar .tab.active span").html(dtps.classes[num].topics[id].title)
  743. var blob = new Blob([`<base target="_blank" /> <link type="text/css" rel="stylesheet" href="https://cdn.jottocraft.com/CanvasCSS.css" media="screen,projection"/>
  744. <style>body {background-color: ` + getComputedStyle($(".card.details")[0]).getPropertyValue("--cards") + `; color: ` + getComputedStyle($(".card.details")[0]).getPropertyValue("--text") + `}</style>` + dtps.classes[num].topics[id].content], { type: 'text/html' });
  745. var newurl = window.URL.createObjectURL(blob);
  746. var people = {};
  747. (data.participants ? data.participants.forEach((person) => people[person.id] = person) : "");
  748. jQuery(".classContent").html((fromModuleStream ? `<div class="acrylicMaterial" onclick="dtps.moduleStream(dtps.selectedClass)" style="line-height: 40px;display: inline-block;border-radius: 20px;margin: 82px 0px 0px 82px; cursor: pointer;">
  749. <div style="font-size: 16px;display: inline-block;vertical-align: middle;margin: 0px 20px;"><i style="vertical-align: middle;" class="material-icons">keyboard_arrow_left</i> Back</div></div>` : "") + `
  750.  
  751. ` + (dtps.classes[num].topics[id].requirePost && !data.view ? `<div class="acrylicMaterial" style="border-radius: 20px;display: inline-block;margin: 10px 82px;margin-top: 25px;height: 40px;line-height: 40px;padding: 0px 20px; margin-right: -70px;">
  752. Replies are only visible to those who have posted at least one reply</div>` : "") + `
  753.  
  754. <div class="acrylicMaterial" style="line-height: 40px;display: inline-block;border-radius: 20px;margin: 82px 0px 0px 82px;">
  755. <div style="font-size: 16px;display: inline-block;vertical-align: middle;margin: 0px 20px;">Discussions (beta)</div></div>
  756.  
  757. <div class="card" style="margin-top: 20px;">
  758. <h4 style="font-weight: bold; margin: 0px; margin-top: 10px;">` + dtps.classes[num].topics[id].title + `</h4>
  759. <img style="width: 25px; vertical-align: middle; border-radius: 50%;" src="` + dtps.classes[num].topics[id].author.prof + `" />
  760. <h5 style="display: inline-block; vertical-align: middle;color: var(--lightText); font-size: 22px;">` + dtps.classes[num].topics[id].author.name + `</h5>
  761.  
  762. <iframe id="classPageIframe" onload="dtps.iframeLoad('classPageIframe')" style="margin: 10px 0px; width: 100%; border: none; outline: none;" src="` + newurl + `" />
  763. </div>
  764.  
  765. ` + (data.view ? data.view.map(function (comment) {
  766. return `<div class="card" style="padding: 20px;">
  767. <img style="width: 25px; vertical-align: middle; border-radius: 50%;" src="` + people[comment.user_id].avatar_image_url + `" />
  768. <h5 style="display: inline-block; vertical-align: middle;">` + people[comment.user_id].display_name + `
  769. ` + (comment.rating_sum ? `<div style="color: var(--secText);margin: 2px 0px;display: inline-block;font-size: 18px;line-height: 18px; position: absolute; top: 30px; right: 20px;"><i class="material-icons" style=" margin-right: 0px; vertical-align: middle; ">thumb_up_alt</i> ` + comment.rating_sum + `</div>` : "") + `</h5>
  770. ` + comment.message + `
  771. ` + (comment.replies ? comment.replies.map(function (reply) {
  772. return `<div style="padding: 10px 20px;background-color: var(--elements);border-radius: 10px; margin: 20px 0px;"><h6>` + people[reply.user_id].display_name + `
  773. ` + (reply.rating_sum ? `<div style="color: var(--secText);margin: 2px 0px;display: inline-block;font-size: 18px;line-height: 18px; float:right;margin-top: -5px;"><i class="material-icons" style=" margin-right: 0px; vertical-align: middle; ">thumb_up_alt</i> ` + reply.rating_sum + `</div>` : "") + `</h6>
  774. <p>` + reply.message + `</p>
  775. ` + (reply.replies ? reply.replies.map(function (replyLayer) {
  776. return `<div style="padding: 10px 20px;background-color: var(--darker);border-radius: 10px; margin: 20px 0px; margin-left: 20px;"><h6>` + people[replyLayer.user_id].display_name + `
  777. ` + (replyLayer.rating_sum ? `<div style="color: var(--secText);margin: 2px 0px;display: inline-block;font-size: 18px;line-height: 18px; float:right;margin-top: -5px;"><i class="material-icons" style=" margin-right: 0px; vertical-align: middle; ">thumb_up_alt</i> ` + replyLayer.rating_sum + `</div>` : "") + `</h6>
  778. <p>` + replyLayer.message + `</p>
  779. ` + (replyLayer.replies ? replyLayer.replies.map(function (replyLayerLayer) {
  780. return `<div style="padding: 10px 20px;background-color: var(--darkest);border-radius: 10px; margin: 20px 0px; margin-left: 20px;"><h6>` + people[replyLayerLayer.user_id].display_name + `
  781. ` + (replyLayerLayer.rating_sum ? `<div style="color: var(--secText);margin: 2px 0px;display: inline-block;font-size: 18px;line-height: 18px; float:right;margin-top: -5px;"><i class="material-icons" style=" margin-right: 0px; vertical-align: middle; ">thumb_up_alt</i> ` + replyLayerLayer.rating_sum + `</div>` : "") + `</h6>
  782. <p>` + replyLayerLayer.message + `</p></div>`
  783. }).join("") : "") + `</div>`
  784. }).join("") : "") + `
  785. </div>`
  786. }).join("") : "") + `
  787. </div>`
  788. }).join("") : "") + `
  789. `);
  790. }
  791. });
  792. }
  793. }
  794.  
  795. //12 hour time formatter from stackoverflow
  796. dtps.ampm = function (date) {
  797. var hours = date.getHours();
  798. var minutes = date.getMinutes();
  799. var ampm = hours >= 12 ? 'pm' : 'am';
  800. hours = hours % 12;
  801. hours = hours ? hours : 12; // the hour '0' should be '12'
  802. minutes = minutes < 10 ? '0' + minutes : minutes;
  803. var strTime = hours + ':' + minutes + ' ' + ampm;
  804. return strTime;
  805. }
  806.  
  807. //Fetches assignment data for a class
  808. dtps.classStream = function (num, renderOv) {
  809. dtps.log("Fetching assignments for class " + num)
  810. if (!renderOv) dtps.showClasses();
  811. if ((dtps.selectedClass == num) && (dtps.selectedContent == "stream")) {
  812. if (!renderOv) {
  813. jQuery(".classContent").html(`
  814. <div class="spinner"></div>
  815. `);
  816. }
  817. }
  818. var allData = [];
  819. var total = null;
  820. dtps.webReq("canvas", "/api/v1/courses/" + dtps.classes[num].id + "/outcome_results?user_ids[]=" + dtps.user.id, function (respp) {
  821. dtps.webReq("canvas", "/api/v1/courses/" + dtps.classes[num].id + "/assignment_groups?include[]=assignments&include[]=submissions&include[]=submission", function (resp) {
  822. var data = JSON.parse(resp);
  823. var outcomes = JSON.parse(respp).outcome_results;
  824. dtps.classes[num].stream = [];
  825. dtps.classes[num].rawStream = data;
  826. dtps.classes[num].streamitems = [];
  827. dtps.classes[num].weights = [];
  828.  
  829. for (var i = 0; i < data.length; i++) {
  830. dtps.classes[num].weights.push({ weight: data[i].name + " (" + ((data.length == 1) && (data[i].group_weight == 0) ? 100 : data[i].group_weight) + "%)", assignments: [], possiblePoints: 0, earnedPoints: 0, icon: `<i class="material-icons">category</i> ` });
  831. if (dtps.classes[num].weights[i].weight.toUpperCase().includes("SUCCESS") || dtps.classes[num].weights[i].weight.includes("SS")) { dtps.classes[num].weights[i].icon = `<i class="material-icons">star_border</i> `; dtps.classes[num].weights[i].weight = "Success Skills (" + dtps.classes[num].weights[i].weight.match(/\(([^)]+)\)/)[1] + ")"; }
  832. if (dtps.classes[num].weights[i].weight.toUpperCase().includes("COMPREHENSION") || dtps.classes[num].weights[i].weight.includes("CC")) { dtps.classes[num].weights[i].icon = `<i class="material-icons">done</i> `; dtps.classes[num].weights[i].weight = "Comprehension Checks (" + dtps.classes[num].weights[i].weight.match(/\(([^)]+)\)/)[1] + ")"; }
  833. if (dtps.classes[num].weights[i].weight.toUpperCase().includes("PERFORMANCE") || dtps.classes[num].weights[i].weight.includes("PT") || dtps.classes[num].weights[i].weight.includes("UE") || dtps.classes[num].weights[i].weight.includes("Exam")) { dtps.classes[num].weights[i].icon = `<i class="material-icons">assessment</i> `; dtps.classes[num].weights[i].weight = "Performance Tasks (" + dtps.classes[num].weights[i].weight.match(/\(([^)]+)\)/)[1] + ")"; }
  834. for (var ii = 0; ii < data[i].assignments.length; ii++) {
  835. dtps.classes[num].stream.push({
  836. id: data[i].assignments[ii].id,
  837. title: data[i].assignments[ii].name,
  838. due: (data[i].assignments[ii].due_at ? new Date(data[i].assignments[ii].due_at).toDateString().slice(0, -5) + ", " + dtps.ampm(new Date(data[i].assignments[ii].due_at)) : ""),
  839. dueDate: data[i].assignments[ii].due_at,
  840. url: data[i].assignments[ii].html_url,
  841. types: data[i].assignments[ii].submission_types,
  842. col: dtps.classes[num].col,
  843. turnedIn: (data[i].assignments[ii].submission !== undefined ? (data[i].assignments[ii].submission.submission_type !== null) : false),
  844. hasSubmissions: data[i].assignments[ii].has_submitted_submissions,
  845. class: num,
  846. subject: dtps.classes[num].subject,
  847. streamItem: dtps.classes[num].stream.length - 1,
  848. weight: dtps.classes[num].weights[i].weight.replace(/ *\([^)]*\) */g, ""),
  849. weightIcon: dtps.classes[num].weights[i].icon,
  850. uniqueWeight: data.length > 1,
  851. published: new Date(data[i].assignments[ii].created_at).toDateString(),
  852. outcomes: (data[i].assignments[ii].rubric ? data[i].assignments[ii].rubric.map(function (key) { return key.description }) : undefined),
  853. outcomeIDs: (data[i].assignments[ii].rubric ? data[i].assignments[ii].rubric.map(function (key) { return key.outcome_id }) : undefined),
  854. locksAt: data[i].assignments[ii].lock_at,
  855. unlocksAt: data[i].assignments[ii].unlock_at,
  856. locked: data[i].assignments[ii].locked_for_user,
  857. lockedReason: data[i].assignments[ii].lock_explanation,
  858. submissions: data[i].assignments[ii].submission.preview_url,
  859. body: data[i].assignments[ii].description,
  860. rubric: data[i].assignments[ii].rubric,
  861. rubricItems: [],
  862. submissionTypes: data[i].assignments[ii].submission_types,
  863. worth: data[i].assignments[ii].points_possible
  864. });
  865. if (data[i].assignments[ii].rubric) {
  866. for (var iii = 0; iii < data[i].assignments[ii].rubric.length; iii++) {
  867. dtps.classes[num].stream[dtps.classes[num].stream.length - 1].rubricItems.push(data[i].assignments[ii].rubric[iii].outcome_id);
  868. if (data[i].assignments[ii].rubric[iii].ratings) {
  869. data[i].assignments[ii].rubric[iii].ratingItems = [];
  870. for (var iiii = 0; iiii < data[i].assignments[ii].rubric[iii].ratings.length; iiii++) {
  871. if (data[i].assignments[ii].rubric[iii].ratings[iiii].description.toUpperCase().includes("EMERGING")) data[i].assignments[ii].rubric[iii].ratings[iiii].name = "Emerging";
  872. if (data[i].assignments[ii].rubric[iii].ratings[iiii].description.toUpperCase().includes("DEVELOPING")) data[i].assignments[ii].rubric[iii].ratings[iiii].name = "Developing";
  873. if (data[i].assignments[ii].rubric[iii].ratings[iiii].description.toUpperCase().includes("PROFICIENT")) data[i].assignments[ii].rubric[iii].ratings[iiii].name = "Proficient";
  874. if (data[i].assignments[ii].rubric[iii].ratings[iiii].description.toUpperCase().includes("PIONEERING")) data[i].assignments[ii].rubric[iii].ratings[iiii].name = "Pioneering";
  875. if (data[i].assignments[ii].rubric[iii].ratings[iiii].points == 1) data[i].assignments[ii].rubric[iii].ratings[iiii].color = "#b90000";
  876. if (data[i].assignments[ii].rubric[iii].ratings[iiii].points == 2) data[i].assignments[ii].rubric[iii].ratings[iiii].color = "#cc8400";
  877. if (data[i].assignments[ii].rubric[iii].ratings[iiii].points == 3) data[i].assignments[ii].rubric[iii].ratings[iiii].color = "#b5b500";
  878. if (data[i].assignments[ii].rubric[iii].ratings[iiii].points == 4) data[i].assignments[ii].rubric[iii].ratings[iiii].color = "#007700";
  879. data[i].assignments[ii].rubric[iii].ratingItems.push(data[i].assignments[ii].rubric[iii].ratings[iiii].points);
  880. if (!data[i].assignments[ii].rubric[iii].ratings[iiii].name) data[i].assignments[ii].rubric[iii].ratings[iiii].name = "";
  881. if (!data[i].assignments[ii].rubric[iii].ratings[iiii].color) data[i].assignments[ii].rubric[iii].ratings[iiii].color = "gray";
  882. }
  883. }
  884. }
  885. }
  886. dtps.classes[num].streamitems.push(String(data[i].assignments[ii].id));
  887. if ((data[i].assignments[ii].submission.score !== null) && (data[i].assignments[ii].submission.score !== undefined)) {
  888. dtps.classes[num].stream[dtps.classes[num].stream.length - 1].grade = data[i].assignments[ii].submission.score + "/" + data[i].assignments[ii].points_possible;
  889. dtps.classes[num].stream[dtps.classes[num].stream.length - 1].status = data[i].assignments[ii].submission.workflow_state;
  890. dtps.classes[num].stream[dtps.classes[num].stream.length - 1].late = data[i].assignments[ii].submission.late;
  891. dtps.classes[num].stream[dtps.classes[num].stream.length - 1].letter = (data[i].assignments[ii].submission.grade.match(/[a-z]/i) ? data[i].assignments[ii].submission.grade : data[i].assignments[ii].submission.score);
  892. //Only treat assignment as graded in the gradebook if the assignment status says the grades are published. Scores are still shown with a pending review icon. This is to match native Canvas behavior.
  893. if (data[i].assignments[ii].submission.workflow_state == "graded") {
  894. dtps.classes[num].weights[i].possiblePoints = dtps.classes[num].weights[i].possiblePoints + data[i].assignments[ii].points_possible;
  895. dtps.classes[num].weights[i].earnedPoints = dtps.classes[num].weights[i].earnedPoints + data[i].assignments[ii].submission.score;
  896. dtps.classes[num].weights[i].assignments.push({ id: data[i].assignments[ii].id, disp: data[i].assignments[ii].name + ": " + data[i].assignments[ii].submission.score + "/" + data[i].assignments[ii].points_possible, percentage: (data[i].assignments[ii].submission.score / data[i].assignments[ii].points_possible).toFixed(2), possible: data[i].assignments[ii].points_possible, earned: data[i].assignments[ii].submission.score });
  897. }
  898. }
  899. if (data[i].assignments[ii].submission !== undefined) {
  900. dtps.classes[num].stream[dtps.classes[num].stream.length - 1].missing = data[i].assignments[ii].submission.missing;
  901. }
  902. }
  903. if (dtps.classes[num].weights[i].possiblePoints !== 0) { dtps.classes[num].weights[i].grade = ((dtps.classes[num].weights[i].earnedPoints / dtps.classes[num].weights[i].possiblePoints) * 100).toFixed(2) + "%" } else { dtps.classes[num].weights[i].grade = "" }
  904. }
  905.  
  906. for (var i = 0; i < outcomes.length; i++) {
  907. var streamItem = dtps.classes[num].streamitems.indexOf(outcomes[i].links.assignment.match(/\d+/)[0]);
  908. console.log("streamItem ", streamItem)
  909. if (streamItem !== -1) {
  910. var rubricItem = dtps.classes[num].stream[streamItem].rubricItems.indexOf(outcomes[i].links.learning_outcome);
  911. console.log("rubricItem ", rubricItem)
  912. if (rubricItem !== -1) {
  913. dtps.classes[num].stream[streamItem].rubric[rubricItem].score = outcomes[i].score
  914. dtps.classes[num].stream[streamItem].rubric[rubricItem].scoreName = outcomes[i].score
  915. if (dtps.classes[num].grades[outcomes[i].links.learning_outcome] == undefined) dtps.classes[num].grades[outcomes[i].links.learning_outcome] = [];
  916. dtps.classes[num].grades[outcomes[i].links.learning_outcome].push({ score: outcomes[i].score, possible: outcomes[i].possible })
  917. }
  918. }
  919. }
  920. if ((dtps.selectedClass == num) && (dtps.selectedContent == "stream")) {
  921. if (!renderOv) {
  922. jQuery(".classContent").html(dtps.renderStream(dtps.classes[num].stream.slice().sort(function (a, b) {
  923. var keyA = new Date(a.dueDate).getTime(),
  924. keyB = new Date(b.dueDate).getTime();
  925. var now = new Date().getTime();
  926. if (a.dueDate == null) { keyA = Infinity; a.old = true; }
  927. if (b.dueDate == null) { keyB = Infinity; b.old = true; }
  928. if (keyA < now) { keyA += 9999999999999; a.old = true; }
  929. if (keyB < now) { keyB += 9999999999999; b.old = true; }
  930. // Compare the 2 dates
  931. if (keyA > keyB) return 1;
  932. if (keyA < keyB) return -1;
  933. return 0;
  934. })));
  935. }
  936. }
  937. dtps.classesReady++;
  938. dtps.checkReady(num);
  939. });
  940. });
  941. }
  942.  
  943. //Fetches module stream for a class
  944. dtps.moduleStream = function (num) {
  945. var moduleRootHTML = `
  946. <div class="acrylicMaterial" style="position: absolute;display: inline-block;border-radius: 20px;margin: 82px;">
  947. <img src="` + dtps.classes[dtps.selectedClass].teacher.prof + `" style="width: 40px; height: 40px; border-radius: 50%;vertical-align: middle;"> <div style="font-size: 16px;display: inline-block;vertical-align: middle;margin: 0px 10px;">` + dtps.classes[dtps.selectedClass].teacher.name + `</div></div>
  948. <div style="text-align: right;">
  949. <br />
  950. <div class="btns row small acrylicMaterial assignmentPicker" style="margin: 63px 80px 20px 0px !important;">
  951. <button class="btn" onclick="dtps.classStream(dtps.selectedClass);"><i class="material-icons">assignment</i>Assignments</button>
  952. <button class="btn active" onclick="dtps.moduleStream(dtps.selectedClass);"><i class="material-icons">view_module</i>Modules</button>
  953. </div><script>fluid.init();</script>
  954. </div>
  955. </div>`
  956. jQuery(".classContent").html(moduleRootHTML + `<div class="spinner"></div>`);
  957. streamData = [];
  958. dtps.webReq("canvas", "/api/v1/courses/" + dtps.classes[num].id + "/modules?include[]=items&include[]=content_details", function (resp) {
  959. var data = JSON.parse(resp);
  960. for (var i = 0; i < data.length; i++) {
  961. var subsetData = [];
  962. for (var ii = 0; ii < data[i].items.length; ii++) {
  963. var icon = "star_border";
  964. if (data[i].items[ii].type == "ExternalTool") icon = "insert_link";
  965. if (data[i].items[ii].type == "ExternalUrl") icon = "open_in_new";
  966. if (data[i].items[ii].type == "Assignment") icon = "assignment";
  967. if (data[i].items[ii].type == "Page") icon = "insert_drive_file";
  968. if (data[i].items[ii].type == "Discussion") icon = "forum";
  969. if (data[i].items[ii].type == "Quiz") icon = "assessment";
  970. if (data[i].items[ii].type == "SubHeader") icon = "format_size";
  971. var open = `window.open('` + data[i].items[ii].html_url + `')`;
  972. if (data[i].items[ii].type == "ExternalTool") open = `$('#moduleIFrame').attr('src', ''); fluid.cards('.card.moduleURL'); $.getJSON('` + data[i].items[ii].url + `', function (data) { $('#moduleIFrame').attr('src', data.url); });`
  973. if (data[i].items[ii].type == "Assignment") open = `dtps.assignment(` + data[i].items[ii].content_id + `, dtps.selectedClass);`
  974. if (data[i].items[ii].type == "Page") open = `dtps.getPage(dtps.classes[dtps.selectedClass].id, '` + data[i].items[ii].page_url + `', true)`
  975. subsetData.push(`<div onclick="` + open + `" style="background-color:var(--elements);padding:20px;font-size:17px;border-radius:15px;margin:15px 0; cursor: pointer;">
  976. <i class="material-icons" style="vertical-align: middle; margin-right: 10px;">` + icon + `</i>` + data[i].items[ii].title + `</div>`);
  977. }
  978. streamData.push(`<div class="card">
  979. <h4 style="margin-top: 5px;">` + data[i].name + `</h4>
  980. ` + subsetData.join("") + `
  981. </div>`)
  982. }
  983. jQuery(".classContent").html(moduleRootHTML + streamData.join(""));
  984.  
  985. });
  986. }
  987.  
  988. //Asks the user when they have each class to load the class automatically
  989. dtps.schedule = function () {
  990. var schedule = {}
  991. if (confirm("Type in which period you have each class as a number (1-6, type 7 for @d.tech). If the class is from a different semester or you don't have that class for a class period, leave the box blank")) {
  992. for (var i = 0; i < dtps.classes.length; i++) {
  993. var num = prompt("Which class period do you have '" + dtps.classes[i].name + "'? (Number 1-6, type 7 for @d.tech, or leave blank)");
  994. if ((Number(num) > 0) && (Number(num) < 8)) schedule[num] = dtps.classes[i].id;
  995. }
  996. localStorage.setItem("dtpsSchedule", JSON.stringify(schedule));
  997. alert("Your schedule has been saved. When loading Power+, Power+ will try to load the class that you are in instead of the dashboard, if you are using Power+ during school hours.")
  998. }
  999. }
  1000.  
  1001. //Displays class info & syllabus
  1002. dtps.classInfo = function (num) {
  1003. if ((dtps.classes[num].syllabus !== "") && dtps.classes[num].syllabus !== null) {
  1004. var blob = new Blob([`<base target="_blank" /> <link type="text/css" rel="stylesheet" href="https://cdn.jottocraft.com/CanvasCSS.css" media="screen,projection"/>
  1005. <style>body {background-color: ` + getComputedStyle($(".card.details")[0]).getPropertyValue("--cards") + `; color: ` + getComputedStyle($(".card.details")[0]).getPropertyValue("--text") + `}</style>` + dtps.classes[num].syllabus], { type: 'text/html' });
  1006. var newurl = window.URL.createObjectURL(blob);
  1007. }
  1008. $(".card.classInfoCard").html(`<i onclick="fluid.cards.close('.card.classInfoCard')" class="material-icons close">close</i>
  1009. <h4 style="font-weight: bold;">` + dtps.classes[num].name + `</h4>
  1010. <p style="color: var(--secText)">` + (dtps.classes[num].description ? dtps.classes[num].description : "") + `</p>
  1011. <div class="assignmentChip"><i class="material-icons">group</i>` + dtps.classes[num].totalStudents + ` students</div>
  1012. ` + (dtps.classes[num].favorite ? `<div title="Favorited class" class="assignmentChip" style="background-color: #daa520"><i style="color:white;font-family: 'Material Icons Outline'" class="material-icons">star_border</i></div>` : "") + `
  1013. <br />
  1014. <div style="margin-top: 20px;" class="syllabusBody">` + ((dtps.classes[num].syllabus !== "") && dtps.classes[num].syllabus !== null ? `<iframe id="syllabusIframe" onload="dtps.iframeLoad('syllabusIframe')" style="margin: 10px 0px; width: 100%; border: none; outline: none;" src="` + newurl + `" />` : "") + `</div>
  1015. `)
  1016. fluid.modal(".card.classInfoCard")
  1017. }
  1018.  
  1019. //Displays class homepage
  1020. dtps.classHome = function (num) {
  1021. $(".card.classInfoCard").html(`<i onclick="fluid.cards.close('.card.classInfoCard')" class="material-icons close">close</i>
  1022. <h4 style="font-weight: bold;">` + dtps.classes[num].subject + ` Homepage</h4>
  1023. <br />
  1024. <p>Loading...</p>`)
  1025. dtps.webReq("canvas", "/api/v1/courses/" + dtps.classes[num].id + "/front_page", function (resp) {
  1026. var data = JSON.parse(resp);
  1027. if (!data.message) {
  1028. var blob = new Blob([`<base target="_blank" /> <link type="text/css" rel="stylesheet" href="https://cdn.jottocraft.com/CanvasCSS.css" media="screen,projection"/>
  1029. <style>body {background-color: ` + getComputedStyle($(".card.details")[0]).getPropertyValue("--cards") + `; color: ` + getComputedStyle($(".card.details")[0]).getPropertyValue("--text") + `}</style>` + data.body], { type: 'text/html' });
  1030. var newurl = window.URL.createObjectURL(blob);
  1031. $(".card.classInfoCard").html(`<i onclick="fluid.cards.close('.card.classInfoCard')" class="material-icons close">close</i>
  1032. <h4 style="font-weight: bold;">` + data.title + `</h4>
  1033. <br />
  1034. <div style="margin-top: 20px;" class="homepageBody"><iframe id="homepageIframe" onload="dtps.iframeLoad('homepageIframe')" style="margin: 10px 0px; width: 100%; border: none; outline: none;" src="` + newurl + `" /></div>
  1035. `)
  1036. fluid.modal(".card.classInfoCard")
  1037. } else {
  1038. alert("Error: No homepage found for this class")
  1039. fluid.cards.close('.card.classInfoCard')
  1040. }
  1041. })
  1042. }
  1043.  
  1044. //Converts a Power+ stream array into HTML for displaying the assignment list
  1045. dtps.renderStream = function (stream, searchRes) {
  1046. var streamlist = [];
  1047. var oldDiv = false;
  1048. for (var i = 0; i < stream.length; i++) {
  1049. var outcomeDom = [];
  1050. if (stream[i].rubric) {
  1051. for (var ii = 0; ii < stream[i].rubric.length; ii++) {
  1052. if (stream[i].rubric[ii].score) {
  1053. outcomeDom.push(`<div class="outcome score` + stream[i].rubric[ii].score + `"></div>`)
  1054. }
  1055. }
  1056. }
  1057. streamlist.push((stream[i].old && !oldDiv ? `<h5 style="margin: 75px 75px 10px 75px;` + (dtps.selectedClass == "dash" ? `text-align: center;margin: 75px 25px 10px 75px;` : ``) + ` font-weight: bold;">Old/Undated Assignments</h5>` : "") + `
  1058. <div onclick="` + (stream[i].google ? `window.open('` + stream[i].url + `')` : `dtps.assignment('` + stream[i].id + `', ` + stream[i].class + `)`) + `" class="card graded assignment ` + stream[i].col + `">
  1059. ` + (stream[i].turnedIn && (stream[i].status !== "unsubmitted") ? `<i title="Assignment submitted" class="material-icons floatingIcon" style="color: #0bb75b;">assignment_turned_in</i>` : ``) + `
  1060. ` + (stream[i].status == "unsubmitted" ? `<i title="Assignment unsubmitted" class="material-icons floatingIcon" style="color: #b3b70b;">warning</i>` : ``) + `
  1061. ` + (stream[i].missing ? `<i title="Assignment is missing" class="material-icons floatingIcon" style="color: #c44848;">remove_circle_outline</i>` : ``) + `
  1062. ` + (stream[i].late ? `<i title="Assignment is late" class="material-icons floatingIcon" style="color: #c44848;">assignment_late</i>` : ``) + `
  1063. ` + (stream[i].locked ? `<i title="Assignment submissions are locked" class="material-icons floatingIcon" style="font-family: 'Material Icons Extended'; color: var(--secText, gray);">lock_outline</i>` : ``) + `
  1064. ` + (stream[i].status == "pending_review" ? `<i title="Grade is pending review" class="material-icons floatingIcon" style="color: #b3b70b;">rate_review</i>` : ``) + `
  1065. <div class="points">
  1066. ` + (outcomeDom.length ? `<div class="earned outcomes">` + outcomeDom.join("") + `</div>` : `<div class="earned numbers">` + (stream[i].letter ? stream[i].grade.split("/")[0] : "") + `</div>`) + `
  1067. ` + (stream[i].grade ? (stream[i].grade.split("/")[1] !== undefined ? `<div class="total possible">/` + stream[i].grade.split("/")[1] + `</div>` : "") : "") + `
  1068. </div>
  1069. <h4>` + stream[i].title + `</h4>
  1070. <h5>
  1071. ` + (stream[i].due ? `<div class="infoChip"><i style="margin-top: -4px;" class="material-icons">alarm</i> Due ` + stream[i].due + `</div>` : "") + `
  1072. ` + ((stream[i].weight !== undefined) && stream[i].uniqueWeight ? `<div class="infoChip weighted">` + stream[i].weightIcon + stream[i].weight.replace("Comprehension Checks", "CC").replace("Success Skills", "SS").replace("Performance Tasks", "PT") + `</div>` : "") + `
  1073. ` + (stream[i].outcomes !== undefined ? `<div class="infoChip weighted"><i class="material-icons">adjust</i>` + stream[i].outcomes.length + `</div>` : "") + `
  1074. </h5>
  1075. </div>
  1076. `);
  1077. if (stream[i].old) oldDiv = true;
  1078. }
  1079. if (typeof Fuse !== "undefined") {
  1080. if (searchRes == undefined) {
  1081. dtps.latestStream = stream;
  1082. dtps.fuse = new Fuse(stream, {
  1083. shouldSort: true,
  1084. threshold: 0.6,
  1085. keys: ["title", "id", "due", "subject"]
  1086. });
  1087. searchRes = "";
  1088. }
  1089. }
  1090. return ((streamlist.length == 0) && (dtps.selectedClass !== "dash")) ?
  1091. (searchRes !== "" ? `<div style="text-align: right;"><i class="inputIcon material-icons">search</i><input value="` + searchRes + `" onchange="dtps.search()" class="search inputIcon shadow" placeholder="Search assignments" type="search" /></div>` : "") + `<div style="cursor: auto;" class="card assignment"><h4>No ` + (searchRes == "" ? "assignments" : "results found") + `</h4><p>` + (searchRes == "" ? "There aren't any assignments in this class yet" : "There aren't any search results") + `</p></div>`
  1092. : ((typeof Fuse !== "undefined" ? `
  1093.  
  1094. ` + ((dtps.selectedClass !== "dash") && (searchRes == "") ? `
  1095.  
  1096. <div style="position: absolute;display: inline-block;margin: 82px;">
  1097. <div class="acrylicMaterial" style="border-radius: 20px; display: inline-block; margin-right: 5px;">
  1098. <img src="` + dtps.classes[dtps.selectedClass].teacher.prof + `" style="width: 40px; height: 40px; border-radius: 50%;vertical-align: middle;"> <div style="font-size: 16px;display: inline-block;vertical-align: middle;margin: 0px 10px;">` + dtps.classes[dtps.selectedClass].teacher.name + `</div></div>
  1099.  
  1100. <div onclick="dtps.classInfo(` + dtps.selectedClass + `)" class="acrylicMaterial" style="border-radius: 50%; height: 40px; width: 40px; text-align: center; display: inline-block; vertical-align: middle; cursor: pointer; margin-right: 3px;">
  1101. <i style="line-height: 40px;" class="material-icons">info</i>
  1102. </div>
  1103.  
  1104. ` + (dtps.classes[dtps.selectedClass].defaultView == "wiki" ? `<div onclick="dtps.classHome(` + dtps.selectedClass + `)" class="acrylicMaterial" style="border-radius: 50%; height: 40px; width: 40px; text-align: center; display: inline-block; vertical-align: middle; cursor: pointer;">
  1105. <i style="line-height: 40px;" class="material-icons">home</i>
  1106. </div>` : "") + `
  1107.  
  1108. </div>
  1109. ` : "") + `
  1110.  
  1111. <div style="text-align: right;"><i class="inputIcon material-icons">search</i><input value="` + searchRes + `" onchange="dtps.search()" class="search inputIcon shadow" placeholder="Search assignments" type="search" />
  1112. ` + ((dtps.selectedClass !== "dash") && (searchRes == "") ? `<br />
  1113. <div class="btns row small acrylicMaterial assignmentPicker" style="margin: 20px 80px 20px 0px !important;">
  1114. <button class="btn active" onclick="dtps.classStream(dtps.selectedClass);"><i class="material-icons">assignment</i>Assignments</button>
  1115. <button class="btn" onclick="dtps.moduleStream(dtps.selectedClass);"><i class="material-icons">view_module</i>Modules</button>
  1116. </div><script>fluid.init();</script>` : "") + `
  1117. </div>` : "") + streamlist.join(""));
  1118. //return streamlist.join("");
  1119. }
  1120.  
  1121. //Searches the assignment stream for a keyword using Fuse.js
  1122. dtps.search = function () {
  1123. if (dtps.selectedClass == "dash") {
  1124. if ($("input.search").val() == "") {
  1125. jQuery(".classContent .stream").html(dtps.renderStream(dtps.latestStream, ""))
  1126. } else {
  1127. jQuery(".classContent .stream").html(dtps.renderStream(dtps.fuse.search($("input.search").val()), $("input.search").val()))
  1128. }
  1129. $(".card.assignment").addClass("color");
  1130. } else {
  1131. if ($("input.search").val() == "") {
  1132. jQuery(".classContent").html(dtps.renderStream(dtps.latestStream, ""))
  1133. } else {
  1134. jQuery(".classContent").html(dtps.renderStream(dtps.fuse.search($("input.search").val()), $("input.search").val()))
  1135. }
  1136. }
  1137. }
  1138.  
  1139. //Renders the Power+ master stream / dashboard showing an overview of all classes
  1140. dtps.masterStream = function (doneLoading, omitOldAssignments) {
  1141. dtps.log("RENDERING DASHBOARD")
  1142. dtps.showClasses();
  1143. if ((dtps.selectedClass == "dash") && (dtps.masterContent == "assignments")) {
  1144. jQuery(".classContent").html(`
  1145. <div class="spinner"></div>
  1146. `);
  1147. }
  1148. var buffer = [];
  1149. if (dtps.classes) {
  1150. for (var i = 0; i < dtps.classes.length; i++) {
  1151. if (dtps.classes[i].stream) {
  1152. buffer = buffer.concat(dtps.classes[i].stream)
  1153. }
  1154. }
  1155. }
  1156. var loadingDom = "";
  1157. if (!doneLoading) {
  1158. loadingDom = `<div class="spinner"></div>`;
  1159. } else {
  1160. dtps.logGrades();
  1161. }
  1162. if ((dtps.selectedClass == "dash") && (dtps.masterContent == "assignments")) {
  1163. jQuery(".classContent").html(`
  1164. <div class="dash cal" style="width: 40%;display: inline-block; vertical-align: top;">
  1165. ` + ($.fullCalendar !== undefined ? `<div id="calendar" class="card" style="padding: 20px;"></div>` : ``) + `
  1166. <div class="announcements"></div>
  1167. </div>
  1168. <div style="width: 59%; display: inline-block;" class="dash stream">
  1169. ` + loadingDom + `
  1170. <div class="assignmentStream"></div>
  1171. </div>
  1172. `)
  1173. }
  1174.  
  1175. dtps.announcements();
  1176. jQuery(".classContent .dash .assignmentStream").html(dtps.renderStream(buffer.sort(function (a, b) {
  1177. var keyA = new Date(a.dueDate).getTime(),
  1178. keyB = new Date(b.dueDate).getTime();
  1179. var now = new Date().getTime();
  1180. if (a.dueDate == null) { keyA = Infinity; a.old = true; }
  1181. if (b.dueDate == null) { keyB = Infinity; b.old = true; }
  1182. if (keyA < now) { keyA += 9999999999999; a.old = true; }
  1183. if (keyB < now) { keyB += 9999999999999; b.old = true; }
  1184. // Compare the 2 dates
  1185. if (keyA > keyB) return 1;
  1186. if (keyA < keyB) return -1;
  1187. return 0;
  1188. })));
  1189. $(".card.assignment").addClass("color");
  1190. dtps.calendar(doneLoading);
  1191. }
  1192.  
  1193. //Loads Google APIs for Classroom integration
  1194. dtps.gapis = function () {
  1195. //these 2 lines are temporary. remove them when uncommenting the rest of dtps.gapis
  1196. jQuery(".googleClassroom").hide();
  1197. jQuery(".googleSetup").show();
  1198. /*jQuery.getScript("https://apis.google.com/js/api.js", function() {
  1199. gapi.load('client:auth2', function() {
  1200. gapi.client.init({
  1201. apiKey: 'AIzaSyB3l_RWC3UMgNDAjZ4wD_HD2NyrneL9H9g',
  1202. clientId: '117676227556-lrt444o80hgrli1nlcl4ij6cm2dbop8v.apps.googleusercontent.com',
  1203. discoveryDocs: ["https://www.googleapis.com/discovery/v1/apis/classroom/v1/rest"],
  1204. scope: "https://www.googleapis.com/auth/classroom.courses.readonly https://www.googleapis.com/auth/classroom.coursework.me.readonly"
  1205. }).then(function () {
  1206. gapi.auth2.getAuthInstance().isSignedIn.listen(updateSigninStatus);
  1207. updateSigninStatus(gapi.auth2.getAuthInstance().isSignedIn.get());
  1208. }, function(error) {
  1209. dtps.log(JSON.stringify(error));
  1210. console.error(error);
  1211. });
  1212. function updateSigninStatus(isSignedIn) {
  1213. if (isSignedIn) {
  1214. jQuery(".googleClassroom").show();
  1215. jQuery(".googleSetup").hide();
  1216. if (dtps.googleSetup !== undefined) {
  1217. window.alert("Google account linked. You have to sign in to Canvas again to finish setup.")
  1218. window.location.reload();
  1219. } else {
  1220. dtps.googleAuth();
  1221. }
  1222. } else {
  1223. jQuery(".googleClassroom").hide();
  1224. jQuery(".googleSetup").show();
  1225. }
  1226. }
  1227. });
  1228. });*/
  1229. }
  1230.  
  1231. //Loads a page
  1232. dtps.getPage = function (classID, id, fromModuleStream) {
  1233. if (id == undefined) var id = dtps.selectedPage;
  1234. if ((dtps.classes[dtps.selectedClass].id == classID) && ((dtps.selectedContent == "pages") || fromModuleStream)) {
  1235. jQuery(".classContent").html(`<div class="spinner"></div>`);
  1236. }
  1237. var spinnerTmp = true;
  1238. dtps.webReq("canvas", "/api/v1/courses/" + classID + "/pages/" + id, function (resp) {
  1239. var data = JSON.parse(resp);
  1240. if ((dtps.classes[dtps.selectedClass].id == classID) && ((dtps.selectedContent == "pages") || fromModuleStream)) {
  1241. $(".cacaoBar .tab.active span").html(data.title)
  1242. var blob = new Blob([`<base target="_blank" /> <link type="text/css" rel="stylesheet" href="https://cdn.jottocraft.com/CanvasCSS.css" media="screen,projection"/>
  1243. <style>body {background-color: ` + getComputedStyle($(".card.details")[0]).getPropertyValue("--cards") + `; color: ` + getComputedStyle($(".card.details")[0]).getPropertyValue("--text") + `}</style>` + data.body], { type: 'text/html' });
  1244. var newurl = window.URL.createObjectURL(blob);
  1245. jQuery(".classContent").html((fromModuleStream ? `<div class="acrylicMaterial" onclick="dtps.moduleStream(dtps.selectedClass)" style="line-height: 40px;display: inline-block;border-radius: 20px;margin: 82px 0px 0px 82px; cursor: pointer;">
  1246. <div style="font-size: 16px;display: inline-block;vertical-align: middle;margin: 0px 20px;"><i style="vertical-align: middle;" class="material-icons">keyboard_arrow_left</i> Back</div></div>` : "") + `
  1247. <div class="card">
  1248. <h4 style="font-weight: bold;">` + data.title + `</h4>
  1249. <br />
  1250. <iframe id="classPageIframe" onload="dtps.iframeLoad('classPageIframe')" style="margin: 10px 0px; width: 100%; border: none; outline: none;" src="` + newurl + `" />
  1251. </div>
  1252. `);
  1253. }
  1254. });
  1255. }
  1256.  
  1257. dtps.outcome = function (num, id) {
  1258. dtps.webReq("canvas", "/api/v1/outcomes/" + id, function (resp) {
  1259. var data = JSON.parse(resp);
  1260.  
  1261. if (data.description) {
  1262. var blob = new Blob([`<base target="_blank" /> <link type="text/css" rel="stylesheet" href="https://cdn.jottocraft.com/CanvasCSS.css" media="screen,projection"/>
  1263. <style>body {background-color: ` + getComputedStyle($(".card.details")[0]).getPropertyValue("--cards") + `; color: ` + getComputedStyle($(".card.details")[0]).getPropertyValue("--text") + `}</style>` + data.description], { type: 'text/html' });
  1264. var newurl = window.URL.createObjectURL(blob);
  1265. }
  1266.  
  1267. $(".card.outcomeCard").html(`<i onclick="fluid.cards.close('.card.outcomeCard')" class="material-icons close">close</i>
  1268. <h4 style="font-weight: bold;">` + data.title + `</h4>
  1269. ` + data.ratings.map(function (rating) {
  1270. return `<h5>` + rating.points + ": " + rating.description + `</h5>`
  1271. }).join("") + `
  1272. <div style="margin-top: 20px;" class="syllabusBody">` + (data.description ? `<iframe id="syllabusIframe" onload="dtps.iframeLoad('syllabusIframe')" style="margin: 10px 0px; width: 100%; border: none; outline: none;" src="` + newurl + `" />` : "") + `</div>
  1273. `)
  1274. fluid.modal(".card.outcomeCard")
  1275. })
  1276. }
  1277.  
  1278. //Loads the gradebook for a class. The type paramater specifies if it should load the mastery gradebook or not
  1279. dtps.gradebook = function (num) {
  1280. dtps.showClasses();
  1281. if (dtps.classes[num].noOutcomes) {
  1282. $(".btns .btn.grades").hide();
  1283. $(".btns .btn").removeClass("active");
  1284. $(".btns .btn.stream").addClass("active");
  1285. dtps.selectedContent = "stream";
  1286. dtps.classStream(num);
  1287. } else {
  1288. headsUp = `<div class="acrylicMaterial" style="line-height: 40px;display: inline-block;border-radius: 20px;margin: 82px 0px 0px 82px;">
  1289. <div style="font-size: 16px;display: inline-block;vertical-align: middle;margin: 0px 20px;">Gradebook (beta)</div></div>`
  1290. $(".classContent").html(headsUp + `<div class="spinner"></div>`)
  1291.  
  1292. dtps.webReq("canvas", "/api/v1/courses/" + dtps.classes[num].id + "/outcome_alignments?student_id=" + dtps.user.id, function (resp) {
  1293. dtps.webReq("canvas", "/api/v1/courses/" + dtps.classes[num].id + "/outcome_rollups?user_ids[]=" + dtps.user.id + "&include[]=outcomes", function (respp) {
  1294. dtps.webReq("canvas", "/api/v1/courses/" + dtps.classes[num].id + "/outcome_results?user_ids[]=" + dtps.user.id, function (resppp) {
  1295. var alignmentData = JSON.parse(resp);
  1296. var rollupData = JSON.parse(respp);
  1297. var resultsData = JSON.parse(resppp).outcome_results;
  1298. dtps.classes[num].outcomes = {};
  1299.  
  1300. console.log(resultsData)
  1301.  
  1302. for (var i = 0; i < rollupData.linked.outcomes.length; i++) {
  1303. dtps.classes[num].outcomes[rollupData.linked.outcomes[i].id] = rollupData.linked.outcomes[i]
  1304. dtps.classes[num].outcomes[rollupData.linked.outcomes[i].id].alignments = []
  1305. dtps.classes[num].outcomes[rollupData.linked.outcomes[i].id].gradedAlignments = []
  1306. dtps.classes[num].outcomes[rollupData.linked.outcomes[i].id].gradedAlignmentIDs = []
  1307. }
  1308.  
  1309. for (var i = 0; i < alignmentData.length; i++) {
  1310. if (dtps.classes[num].outcomes[alignmentData[i].learning_outcome_id] !== undefined) {
  1311. dtps.classes[num].outcomes[alignmentData[i].learning_outcome_id].alignments.push(alignmentData[i]);
  1312. for (var ii = 0; ii < resultsData.length; ii++) {
  1313. if (String(resultsData[ii].links.assignment).replace("assignment_", "") == alignmentData[i].assignment_id) {
  1314. if (resultsData[ii].score && !dtps.classes[num].outcomes[alignmentData[i].learning_outcome_id].gradedAlignmentIDs.includes(resultsData[ii].links.assignment)) {
  1315. dtps.classes[num].outcomes[alignmentData[i].learning_outcome_id].gradedAlignments.push(alignmentData[i]);
  1316. dtps.classes[num].outcomes[alignmentData[i].learning_outcome_id].gradedAlignmentIDs.push(resultsData[ii].links.assignment);
  1317. }
  1318. }
  1319. }
  1320. } else {
  1321. dtps.log("WARNING: GRADEBOOK FOUND AN OUTCOME ALIGNMENT THAT DOES NOT MATCH ANY ROLLUP (dtps.gradebook)")
  1322. }
  1323. }
  1324.  
  1325. for (var i = 0; i < rollupData.rollups[0].scores.length; i++) {
  1326. var outcome = dtps.classes[num].outcomes[rollupData.rollups[0].scores[i].links.outcome];
  1327. outcome.score = rollupData.rollups[0].scores[i].score
  1328. for (var ii = 0; ii < dtps.classes[num].stream.length; ii++) {
  1329. if (dtps.classes[num].stream[ii].title == rollupData.rollups[0].scores[i].title) {
  1330. outcome.lastAssignment = {
  1331. id: dtps.classes[num].stream[ii].id,
  1332. name: rollupData.rollups[0].scores[i].title,
  1333. score: dtps.classes[num].stream[ii].rubric[dtps.classes[num].stream[ii].rubricItems.indexOf(rollupData.rollups[0].scores[i].links.outcome)].score
  1334. }
  1335. }
  1336. }
  1337. if (outcome.lastAssignment) {
  1338. //straight outcome average (w/o decaying average thing)
  1339. outcome.straightAverage = ((((outcome.score - (outcome.lastAssignment.score * (outcome.calculation_int / 100))) / (1 - (outcome.calculation_int / 100))) * (outcome.gradedAlignments.length - 1)) + outcome.lastAssignment.score) / outcome.gradedAlignments.length;
  1340. }
  1341. }
  1342.  
  1343. if (dtps.classes[num].computedOutcomeScores !== undefined) {
  1344. Object.keys(dtps.classes[num].computedOutcomeScores).forEach((k) => {
  1345. if (dtps.classes[num].outcomes[k]) {
  1346. dtps.classes[num].outcomes[k].score = dtps.classes[num].computedOutcomeScores[k]
  1347. }
  1348. })
  1349. }
  1350.  
  1351. //temporary variable for if outcome divider is added yet
  1352. var dividerAdded = false;
  1353.  
  1354. $(".classContent").html(headsUp + `
  1355.  
  1356. ` + (dtps.classes[num].letter !== "--" ? `<div class="card">
  1357. <h4 style="margin-bottom: 40px; height: 80px; line-height: 80px; margin-top: 0px; font-weight: bold; -webkit-text-fill-color: transparent; background: -webkit-linear-gradient(var(--light), var(--norm)); -webkit-background-clip: text;">` + dtps.classes[num].name + `
  1358. <div class="classGradeCircle" style="display: inline-block;width: 80px;height: 80px;text-align: center;line-height: 80px;border-radius: 50%;float: right;vertical-align: middle;color: var(--light);">` + dtps.classes[num].letter + `</div></h4>
  1359. <h5 style="height: 60px; line-height: 60px;">75% of outcome scores are ≥
  1360. <div style=" display: inline-block; background-color: var(--elements); width: 60px; height: 60px; text-align: center; line-height: 60px; border-radius: 50%; float: right; vertical-align: middle; font-size: 22px;">` + (dtps.classes[num].gradeCalc.number75 ? dtps.classes[num].gradeCalc.number75 : "--") + `</div></h5>
  1361. <h5 style="height: 60px; line-height: 60px;">No outcome scores are lower than
  1362. <div style=" display: inline-block; background-color: var(--elements); width: 60px; height: 60px; text-align: center; line-height: 60px; border-radius: 50%; float: right; vertical-align: middle; font-size: 22px;">` + dtps.classes[num].gradeCalc.lowestValue + `</div></h5>
  1363. <br />
  1364. <a onclick="$('#classGradeMore').show(); $(this).hide();" style="color: var(--secText, gray); cursor: pointer;">Show More</a>
  1365. <div style="display: none;" id="classGradeMore">
  1366. <table class="u-full-width">
  1367. <thead>
  1368. <tr>
  1369. <th>Final Letter</th>
  1370. <th>75% of outcome scores ≥</th>
  1371. <th>No outcome scores below</th>
  1372. </tr>
  1373. </thead>
  1374. <tbody>
  1375. <tr ` + (dtps.classes[num].letter == "A" ? `style="color: var(--norm);font-size:20px;"` : ``) + `>
  1376. <td>A</td>
  1377. <td>3.5</td>
  1378. <td>3.0</td>
  1379. </tr>
  1380. <tr ` + (dtps.classes[num].letter == "A-" ? `style="color: var(--norm);font-size:20px;"` : ``) + `>
  1381. <td>A-</td>
  1382. <td>3.5</td>
  1383. <td>2.5</td>
  1384. </tr>
  1385. <tr ` + (dtps.classes[num].letter == "B+" ? `style="color: var(--norm);font-size:20px;"` : ``) + `>
  1386. <td>B+</td>
  1387. <td>3</td>
  1388. <td>2.5</td>
  1389. </tr>
  1390. <tr ` + (dtps.classes[num].letter == "B" ? `style="color: var(--norm);font-size:20px;"` : ``) + `>
  1391. <td>B</td>
  1392. <td>3</td>
  1393. <td>2.25</td>
  1394. </tr>
  1395. <tr ` + (dtps.classes[num].letter == "B-" ? `style="color: var(--norm);font-size:20px;"` : ``) + `>
  1396. <td>B-</td>
  1397. <td>3</td>
  1398. <td>2.0</td>
  1399. </tr>
  1400. <tr ` + (dtps.classes[num].letter == "C" ? `style="color: var(--norm);font-size:20px;"` : ``) + `>
  1401. <td>C</td>
  1402. <td>2.5</td>
  1403. <td>2.0</td>
  1404. </tr>
  1405. <tr ` + (dtps.classes[num].letter == "I" ? `style="color: var(--norm);font-size:20px;"` : ``) + `>
  1406. <td>I</td>
  1407. <td>Anything else</td>
  1408. <td>--</td>
  1409. </tr>
  1410. </tbody>
  1411. </table>
  1412. </div>
  1413. </div>` : "") + `
  1414. ` + Object.keys(dtps.classes[num].outcomes).sort(function (a, b) {
  1415. var keyA = dtps.classes[num].outcomes[a].score,
  1416. keyB = dtps.classes[num].outcomes[b].score;
  1417. if (keyA == undefined) { keyA = 999999 - dtps.classes[num].outcomes[a].alignments.length; }
  1418. if (keyB == undefined) { keyB = 999999 - dtps.classes[num].outcomes[b].alignments.length; }
  1419. // Compare the 2 scores
  1420. if (keyA > keyB) return 1;
  1421. if (keyA < keyB) return -1;
  1422. return 0;
  1423. }).map(function (i) {
  1424. var divider = !dividerAdded && !dtps.classes[num].outcomes[i].score;
  1425. if (divider) dividerAdded = true;
  1426. return (divider ? `<h5 style="font-weight: bold;margin: 75px 75px 10px 75px;">Unassesed outcomes</h5>` : "") + `
  1427. <div onclick="dtps.outcome(` + num + `, '` + dtps.classes[num].outcomes[i].id + `')" style="border-radius: 20px;padding: 10px 20px;height: 105px;cursor: pointer;" class="card">
  1428. <h5 style="font-size: 1.5rem; white-space: nowrap;overflow: hidden;text-overflow: ellipsis;">` + dtps.classes[num].outcomes[i].title + `</h5>
  1429. <div title="Number of assignments that assess this outcome" style="color: var(--secText); display: inline-block; margin-right: 5px;"><i class="material-icons" style=" vertical-align: middle; ">assignment</i> ` + dtps.classes[num].outcomes[i].alignments.length + `</div>
  1430. ` + (dtps.classes[num].outcomes[i].calculation_method == "decaying_average" ? `<div title="Calculation ratio (last assignment / everything else)" style="color: var(--secText); display: inline-block; margin: 0px 5px;"><i class="material-icons" style=" vertical-align: middle; ">functions</i> ` + dtps.classes[num].outcomes[i].calculation_int + "/" + (100 - dtps.classes[num].outcomes[i].calculation_int) + `</div>` : "") + `
  1431. ` + (dtps.classes[num].outcomes[i].score ? `<div title="Outcome score" style="color: var(--secText); display: inline-block; margin: 0px 5px;"><i class="material-icons" style=" vertical-align: middle; ">assessment</i> ` + dtps.classes[num].outcomes[i].score + `</div>` : "") + `
  1432. ` + (dtps.classes[num].outcomes[i].score >= dtps.classes[num].outcomes[i].mastery_points ? `<div title="Outcome has been mastered" style="display: inline-block;margin: 0px 5px;color: #5d985d;">MASTERED</div>` : "") + `
  1433. </div>`
  1434. }).join("") + `
  1435. </div>`)
  1436. });
  1437. });
  1438. })
  1439. }
  1440. }
  1441.  
  1442. //grade boost is its own thing now because of CBL
  1443. dtps.gradeBoost = function () {
  1444.  
  1445. }
  1446.  
  1447. //Shows details for an assignment given the assignment ID and class number
  1448. dtps.assignment = function (id, classNum, submissions) {
  1449. var streamNum = dtps.classes[classNum].streamitems.indexOf(String(id));
  1450. var assignment = dtps.classes[classNum].stream[streamNum];
  1451. console.log(classNum, streamNum);
  1452. if (submissions == "handIN") {
  1453. $(".card.details").html(`<i onclick="fluid.cards.close('.card.details')" class="material-icons close">close</i>
  1454. <i style="left: 0; right: auto;" onclick="dtps.assignment(` + id + `, ` + classNum + `)" class="material-icons close">arrow_back</i>
  1455. <br /><h4>Loading...</h4>`);
  1456. $.get("https://canvas.instructure.com/courses/" + dtps.classes[classNum].id + "/assignments/" + id, function (data) {
  1457. dtps.submissionToken = $(data).find("input[name=authenticity_token]").attr("value");
  1458. $(".card.details").html(`<i onclick="fluid.cards.close('.card.details')" class="material-icons close">close</i>
  1459. <i style="left: 0; right: auto;" onclick="dtps.assignment(` + id + `, ` + classNum + `)" class="material-icons close">arrow_back</i>
  1460. <br /><h4>Submit assignment (beta)</h4>
  1461. <p>Submitting assignments in Power+ is a beta feature. Use at your own risk and double check the Canvas submission to ensure it worked properly.
  1462. Power+ currently only supports assignments that use online text entry. Other assignment types will be added in the future.</p>
  1463. <br />
  1464. <textarea class="submissionText" placeholder="Submission"></textarea>
  1465. <br /><br />
  1466. <button class="btn" onclick="dtps.assignment(` + id + `, ` + classNum + `, 'submitText')"><i class="material-icons">public</i>Submit</button>`);
  1467. });
  1468. } else {
  1469. if (String(submissions).startsWith("submit")) {
  1470. if (submissions == "submitText") {
  1471. dtps.webReq('canSUBMIT', "/api/v1/courses/" + dtps.classes[classNum].id + "/assignments/" + id + "/submissions?submission[submission_type]=online_text_entry&submission[body]=" + $('.submissionText').val() + "&authenticity_token=" + dtps.submissionToken, function () {
  1472. window.alert('Assignment submitted')
  1473. })
  1474. }
  1475. } else {
  1476. if (submissions) {
  1477. $(".card.details").css("background-color", "white");
  1478. $(".card.details").css("color", "black")
  1479. $(".card.details").html(`<i style="color: black !important;" onclick="fluid.cards.close('.card.details')" class="material-icons close">close</i>
  1480. <i style="left: 0; right: auto; color: black !important;" onclick="dtps.assignment(` + id + `, ` + classNum + `)" class="material-icons close">arrow_back</i>
  1481. <br /><br />
  1482. <iframe style="width: 100%; height: calc(100vh - 175px); border: none;" src="` + assignment.submissions + `"></iframe>`);
  1483. } else {
  1484. $(".card.details").css("background-color", "")
  1485. $(".card.details").css("color", "")
  1486.  
  1487. if (assignment.body) {
  1488. var blob = new Blob([`<base target="_blank" /> <link type="text/css" rel="stylesheet" href="https://cdn.jottocraft.com/CanvasCSS.css" media="screen,projection"/>
  1489. <style>body {background-color: ` + getComputedStyle($(".card.details")[0]).getPropertyValue("--cards") + `; color: ` + getComputedStyle($(".card.details")[0]).getPropertyValue("--text") + `}</style>` + assignment.body], { type: 'text/html' });
  1490. var newurl = window.URL.createObjectURL(blob);
  1491. }
  1492.  
  1493. $(".card.details").html(`<i onclick="fluid.cards.close('.card.details'); $('.card.details').html('');" class="material-icons close">close</i>
  1494. <h4 style="font-weight: bold;">` + assignment.title + `</h4>
  1495.  
  1496. <div>
  1497. ` + (assignment.due ? `<div class="assignmentChip"><i class="material-icons">alarm</i>Due ` + assignment.due + `</div>` : "") + `
  1498. ` + (assignment.outcomes ? `<div class="assignmentChip"><i class="material-icons">adjust</i> ` + assignment.outcomes.length + `</div>` : "") + `
  1499. ` + (assignment.turnedIn && (assignment.status !== "unsubmitted") ? `<div title="Assignment submitted" class="assignmentChip" style="background-color: #0bb75b"><i style="color:white;" class="material-icons">assignment_turned_in</i></div>` : "") + `
  1500. ` + (assignment.status == "unsubmitted" ? `<div title="Assignment unsubmitted" class="assignmentChip" style="background-color: #b3b70b"><i style="color:white;" class="material-icons">warning</i></div>` : "") + `
  1501. ` + (assignment.missing ? `<div title="Assignment is missing" class="assignmentChip" style="background-color: #c44848"><i style="color:white;" class="material-icons">remove_circle_outline</i></div>` : "") + `
  1502. ` + (assignment.late ? `<div title="Assignment is late" class="assignmentChip" style="background-color: #c44848"><i style="color:white;" class="material-icons">assignment_late</i></div>` : "") + `
  1503. ` + (assignment.locked ? `<div title="Assignment submissions are locked" class="assignmentChip" style="background-color: var(--secText, gray);"><i style="color:white;font-family: 'Material Icons Extended';" class="material-icons">lock_outline</i></div>` : "") + `
  1504. ` + (assignment.status == "pending_review" ? `<div title="Grade is pending review" class="assignmentChip" style="background-color: #b3b70b"><i style="color:white;" class="material-icons">rate_review</i></div>` : "") + `
  1505. </div>
  1506.  
  1507. <div style="margin-top: 20px;" class="assignmentBody">` + (assignment.body ? `<iframe id="assignmentIframe" onload="dtps.iframeLoad('assignmentIframe')" style="margin: 10px 0px; width: 100%; border: none; outline: none;" src="` + newurl + `" />` : "") + `</div>
  1508.  
  1509. <div style="margin: 5px 0px; background-color: var(--secText); height: 1px; width: 100%;" class="divider"></div>
  1510. <div style="width: calc(40% - 2px); margin-top: 20px; display: inline-block; overflow: hidden; vertical-align: middle;">
  1511. <p style="color: var(--secText); margin: 5px 0px;"><i style="vertical-align: middle;" class="material-icons">add_box</i> Posted: ` + assignment.published + `</p>
  1512. ` + (assignment.due ? `<p style="color: var(--secText); margin: 5px 0px;"><i style="vertical-align: middle;" class="material-icons">alarm</i> Due: ` + assignment.due + `</p>` : "") + `
  1513. ` + (assignment.locksAt ? `<p style="color: var(--secText); margin: 5px 0px;"><i style="vertical-align: middle;font-family: 'Material Icons Extended'" class="material-icons">lock_outline</i> Locks: ` + new Date(assignment.locksAt).toDateString().slice(0, -5) + ", " + dtps.ampm(new Date(assignment.locksAt)) + `</p>` : "") + `
  1514. ` + (assignment.unlocksAt ? `<p style="color: var(--secText); margin: 5px 0px;"><i style="vertical-align: middle;" class="material-icons">lock_open</i> Unlocks: ` + new Date(assignment.unlocksAt).toDateString().slice(0, -5) + ", " + dtps.ampm(new Date(assignment.unlocksAt)) + `</p>` : "") + `
  1515. ` + (assignment.status ? `<p style="color: var(--secText); margin: 5px 0px;"><i style="vertical-align: middle;" class="material-icons">assignment_return</i> Status: ` + (assignment.status == "submitted" ? "Submitted" : (assignment.status == "unsubmitted" ? "Unsubmitted" : (assignment.status == "graded" ? "Graded" : (assignment.status == "pending_review" ? "Pending Review" : assignment.status)))) + `</p>` : "") + `
  1516. ` + ((assignment.worth !== undefined) && (assignment.worth !== null) ? `<p style="color: var(--secText); margin: 5px 0px;"><i style="vertical-align: middle;" class="material-icons">bar_chart</i> Total Points: ` + assignment.worth + `</p>` : "") + `
  1517. ` + (assignment.grade ? `<p style="color: var(--secText); margin: 5px 0px;"><i style="vertical-align: middle;" class="material-icons">assessment</i> Points Earned: ` + assignment.grade + ` (` + assignment.letter + `)</p>` : "") + `
  1518. ` + (assignment.submissionTypes ? `<p style="color: var(--secText); margin: 5px 0px;"><i style="vertical-align: middle;" class="material-icons">assignment</i> Submission Types: ` + assignment.submissionTypes.join(", ").replace(/online_/g, '').replace(/_/g, ' ') + `</p>` : "") + `
  1519. <p style="color: var(--secText); margin: 5px 0px;"><i style="vertical-align: middle;" class="material-icons">category</i> Group: ` + assignment.weight + `</p>
  1520. ` + (assignment.outcomes ? assignment.outcomes.map(function (key) { return `<p style="color: var(--secText); margin: 5px 0px;"><i style="vertical-align: middle;" class="material-icons">adjust</i> ` + key + `</p>`; }).join("") : "") + `
  1521. ` + (assignment.locked && assignment.lockedReason ? `<p style="color: var(--secText); margin: 5px 0px;"><i style="vertical-align: middle;font-family: 'Material Icons Extended'" class="material-icons">lock_outline</i> ` + assignment.lockedReason + `</p>` : "") + `
  1522. <p style="color: var(--secText); margin: 5px 0px;"><i style="vertical-align: middle;" class="material-icons">class</i> Class: ` + assignment.subject + `</p>
  1523. <br />
  1524. <div class="btn small outline" onclick="dtps.assignment(` + id + `, ` + classNum + `, true)"><i class="material-icons">assignment</i> Submissions</div>
  1525. <div class="btn small outline" onclick="window.open('` + assignment.url + `')"><i class="material-icons">open_in_new</i> Open in Canvas</div>
  1526. </div>
  1527. <div style="width: calc(60% - 7px); margin-top: 20px; margin-left: 5px; display: inline-block; overflow: hidden; vertical-align: middle;">
  1528. ` + (assignment.rubric ? assignment.rubric.map(function (rubric) {
  1529. if (rubric.ratings) {
  1530. dtps.classes[classNum].tmp[rubric.id] = rubric.long_description
  1531. return `
  1532. <div style="margin: 20px 0px;">
  1533. <h6 style="white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">` + rubric.description + `</h6>
  1534. <div style="display: inline-block; margin-right: 20px; vertical-align: middle;">
  1535. <p style="color: var(--secText);" class="` + rubric.id + `"><a onclick="$('p.` + rubric.id + `').html(dtps.classes[` + classNum + `].tmp['` + rubric.id + `'])" href="#">Rubric details</a></p>
  1536. </div>
  1537.  
  1538. <div style="display: inline-block; border-radius: 10px; overflow: hidden; font-size: 0; vertical-align: middle; background: ` + (rubric.score ? rubric.ratings[rubric.ratingItems.indexOf(rubric.score)].color : `linear-gradient(90deg, ` + rubric.ratings.map(function (rating) { return rating.color + ","; }).join("").slice(0, -1) + `)`) + `;">
  1539. ` + (rubric.score !== undefined ? `
  1540. <div style="padding: 5px 10px; font-size: 18px; background-color: transparent; color: white; font-weight: bold; width: 200px; text-align: center; display: inline-block;">
  1541. ` + rubric.score + "/" + rubric.points + " " + rubric.ratings[rubric.ratingItems.indexOf(rubric.score)].name + `
  1542. </div>` : rubric.ratings.map(function (rating) {
  1543. return `
  1544. <div style="padding: 5px 10px; font-size: 18px; background-color: transparent; color: white; font-weight: bold; width: 120px; text-align: center; display: inline-block;">
  1545. ` + rating.name + `
  1546. </div>`
  1547. }).join("")) + `</div></div>`
  1548. } else { return ""; }
  1549. }).join("") : "") + `
  1550. </div>
  1551. `)
  1552. }
  1553. }
  1554. }
  1555. fluid.cards.close(".card.focus");
  1556. fluid.modal(".card.details");
  1557. }
  1558.  
  1559. dtps.iframeLoad = function (iframe) {
  1560. var iFrameID = document.getElementById(iframe);
  1561. if (iFrameID) {
  1562. iFrameID.height = "";
  1563. iFrameID.height = iFrameID.contentWindow.document.body.scrollHeight + "px";
  1564. }
  1565. }
  1566.  
  1567. //Fetches and displays announcements
  1568. dtps.announcements = function () {
  1569. var context = [];
  1570. for (var i = 0; i < dtps.classes.length; i++) {
  1571. if (i == 0) { context.push("?context_codes[]=course_" + dtps.classes[i].id) } else { context.push("&context_codes[]=course_" + dtps.classes[i].id) }
  1572. }
  1573. dtps.webReq("canvas", "/api/v1/announcements" + context.join(""), function (resp) {
  1574. var ann = JSON.parse(resp);
  1575. var announcements = [];
  1576. for (var i = 0; i < ann.length; i++) {
  1577. var dtpsClass = null;
  1578. for (var ii = 0; ii < dtps.classes.length; ii++) {
  1579. if (dtps.classes[ii].id == ann[i].context_code.split("_")[1]) {
  1580. dtpsClass = ii;
  1581. }
  1582. }
  1583. announcements.push(`<div onclick="$(this).toggleClass('open');" style="cursor: pointer;" class="announcement card color ` + dtps.classes[dtpsClass].col + `">
  1584. <div class="label">` + dtps.classes[dtpsClass].subject + `</div>` + ann[i].message + `
  1585. </div>
  1586. `);
  1587. }
  1588. if ((dtps.selectedClass == "dash") && (dtps.masterContent == "assignments")) {
  1589. jQuery(".dash .announcements").html(announcements.join(""));
  1590. }
  1591. });
  1592. };
  1593.  
  1594. //Compiles and displays assignment due dates in the calendar
  1595. dtps.calendar = function (doneLoading) {
  1596. dtps.log("BUILDING CAL")
  1597. if ((dtps.selectedClass == "dash") && (dtps.masterContent == "assignments")) {
  1598. calEvents = [];
  1599. for (var i = 0; i < dtps.classes.length; i++) {
  1600. if (dtps.classes[i].stream) {
  1601. for (var ii = 0; ii < dtps.classes[i].stream.length; ii++) {
  1602. if (dtps.classes[i].stream[ii].dueDate) {
  1603. var styles = window.getComputedStyle($(".class." + i)[0]);
  1604. calEvents.push({
  1605. title: dtps.classes[i].stream[ii].title,
  1606. start: moment(new Date(dtps.classes[i].stream[ii].dueDate)).toISOString(true),
  1607. allDay: false,
  1608. color: styles.getPropertyValue('--norm'),
  1609. classNum: i,
  1610. assignmentID: dtps.classes[i].stream[ii].id
  1611. })
  1612. }
  1613. }
  1614. }
  1615. }
  1616. if ($.fullCalendar !== undefined) {
  1617. $('#calendar').fullCalendar({
  1618. events: calEvents,
  1619. header: {
  1620. left: 'title',
  1621. right: 'prev,next'
  1622. },
  1623. eventClick: function (calEvent, jsEvent, view) {
  1624. dtps.assignment(calEvent.assignmentID, calEvent.classNum);
  1625. },
  1626. eventAfterAllRender: function () {
  1627. $(".fc-prev-button").html(`<i class="material-icons">keyboard_arrow_left</i>`);
  1628. $(".fc-next-button").html(`<i class="material-icons">keyboard_arrow_right</i>`);
  1629. }
  1630. });
  1631. }
  1632. $(".fc-prev-button").html(`<i class="material-icons">keyboard_arrow_left</i>`);
  1633. $(".fc-next-button").html(`<i class="material-icons">keyboard_arrow_right</i>`);
  1634.  
  1635. }
  1636. }
  1637.  
  1638. //Clears all Power+ data
  1639. dtps.clearData = function () {
  1640. if (window.confirm("Clearing Power+ data will clear all local user data stored by Power+. This should be done if it is a new semester / school year or if you are having issues with Power+. Are you sure you want to clear all your Power+ data?")) {
  1641. window.localStorage.clear()
  1642. window.alert("Power+ data cleared. Reload the page to begin repopulating your userdata.")
  1643. }
  1644. }
  1645.  
  1646. //Renders chroma effect for selected class
  1647. dtps.chroma = function () {
  1648. if (fluid.chroma) {
  1649. if (fluid.chroma.on) {
  1650. var classVar = "--light"
  1651. if ($("body").hasClass("dark")) classVar = "--norm"
  1652. if ($("body").hasClass("midnight")) classVar = "--dark"
  1653. if (dtps.selectedClass !== "dash") {
  1654. var dark = "white";
  1655. var lighting = JSON.parse(`[
  1656. [null,null,null,` + (dtps.selectedContent == "stream" ? '"white","white","white","white"' : "null,null,null,null") + `,` + (dtps.selectedContent == "pages" ? '"white","white","white","white"' : "null,null,null,null") + `,` + (dtps.selectedContent == "grades" ? '"white","white","white","white"' : "null,null,null,null") + `,null,null,null,null,null,null,null],
  1657. [null,null,` + (dtps.classes[dtps.selectedClass].grade >= 10 ? `"` + dark + `"` : 0) + `,` + (dtps.classes[dtps.selectedClass].grade >= 20 ? `"` + dark + `"` : 0) + `,` + (dtps.classes[dtps.selectedClass].grade >= 30 ? `"` + dark + `"` : 0) + `,` + (dtps.classes[dtps.selectedClass].grade >= 40 ? `"` + dark + `"` : 0) + `,` + (dtps.classes[dtps.selectedClass].grade >= 50 ? `"` + dark + `"` : 0) + `,
  1658. ` + (dtps.classes[dtps.selectedClass].grade >= 60 ? `"` + dark + `"` : 0) + `,` + (dtps.classes[dtps.selectedClass].grade >= 70 ? `"` + dark + `"` : 0) + `,` + (dtps.classes[dtps.selectedClass].grade >= 80 ? `"` + dark + `"` : 0) + `,` + (dtps.classes[dtps.selectedClass].grade >= 90 ? `"` + dark + `"` : 0) + `,` + (dtps.classes[dtps.selectedClass].grade >= 100 ? `"` + dark + `"` : 0) + `,null,null,null,null,null,null,null,null,null,null],
  1659. [null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null],
  1660. [null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null],
  1661. [null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null],
  1662. [null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null]]`)
  1663. var ele = $(".background")[0];
  1664. if ($(".class." + dtps.selectedClass)[0] !== undefined) ele = $(".class." + dtps.selectedClass)[0];
  1665. fluid.chroma.effect(tinycolor(getComputedStyle(ele).getPropertyValue(classVar)).saturate(70).toHexString(), lighting);
  1666. } else {
  1667. var ele = $(".background")[0];
  1668. if ($(".class.masterStream")[0] !== undefined) ele = $(".class.masterStream")[0];
  1669. fluid.chroma.static(getComputedStyle(ele).getPropertyValue(classVar));
  1670. }
  1671. }
  1672. }
  1673. }
  1674.  
  1675. //Renders the class list in the sidebar
  1676. dtps.showClasses = function (override) {
  1677. var streamClass = "active"
  1678. if (dtps.selectedClass !== "dash") var streamClass = "";
  1679. dtps.classlist = [];
  1680. for (var i = 0; i < dtps.classes.length; i++) {
  1681. var name = dtps.classes[i].subject
  1682. if (dtps.fullNames) name = dtps.classes[i].name
  1683. dtps.classlist.push(`
  1684. <div onclick="dtps.selectedClass = ` + i + `" class="class ` + i + ` ` + dtps.classes[i].col + `">
  1685. <div class="name">` + name + `</div>
  1686. <div class="grade val">` + (dtps.classes[i].letter !== false ? `<span style="display: block !important;" class="letter">` + dtps.classes[i].letter + `</span><span style="display: none !important;" class="points">` + dtps.classes[i].grade + `%</span>` : `
  1687. <div class="spinner" style=" background-color: var(--norm); margin: 5px; "></div>`) + `</div>
  1688. </div>
  1689. `);
  1690. }
  1691. var googleClassDom = ""
  1692. if (dtps.isolatedGoogleClasses) {
  1693. dtps.classlist.push(`<div class="classDivider"></div>`)
  1694. for (var i = 0; i < dtps.isolatedGoogleClasses.length; i++) {
  1695. dtps.classlist.push(`<div style="display: none;" onclick="$('.sidebar .class').removeClass('active'); $(this).addClass('active'); $('body').addClass('isolatedGoogleClass'); dtps.selectedClass = 'isolatedGoogleClass'; $('.classContent').html(dtps.renderStream(dtps.googleClasses[` + dtps.isolatedGoogleClasses[i] + `].stream)); $('#headText').html('` + dtps.googleClasses[dtps.isolatedGoogleClasses[i]].name + `')" class="class isolated google ` + dtps.googleClasses[dtps.isolatedGoogleClasses[i]].id + `">
  1696. <div style="width: 100%; padding-right: 10px;" class="name">` + dtps.googleClasses[dtps.isolatedGoogleClasses[i]].name + `</div>
  1697. </div>`)
  1698. }
  1699. }
  1700. if ((!Boolean(jQuery(".sidebar .class.masterStream")[0])) || override) {
  1701. jQuery(".sidebar").html(`<h5 style="margin: 10px 0px 25px 0px; font-weight: 600; font-size: 27px; text-align: center;">Power+</h5>
  1702. <div onclick="dtps.selectedClass = 'dash';" class="class masterStream ` + streamClass + `">
  1703. <div class="name">Dashboard</div>
  1704. <div class="grade"><i class="material-icons">dashboard</i></div>
  1705. </div>
  1706. <div class="classDivider"></div>
  1707. ` + dtps.classlist.join(""));
  1708. if (dtps.selectedClass !== "dash") $(".class." + dtps.selectedClass).addClass("active");
  1709. if ($(".btn.pages").hasClass("active")) { $(".btn.pages").removeClass("active"); $(".btn.stream").addClass("active"); dtps.classStream(dtps.selectedClass); dtps.selectedContent = "stream"; }
  1710. if ($(".btn.discuss").hasClass("active")) { $(".btn.discuss").removeClass("active"); $(".btn.stream").addClass("active"); dtps.classStream(dtps.selectedClass); dtps.selectedContent = "stream"; }
  1711. $(".class:not(.google)").click(function (event) {
  1712. if (dtps.selectedClass == "dash") $('body').addClass('dashboard');
  1713. if (dtps.selectedClass !== "dash") $('body').removeClass('dashboard');
  1714. dtps.chroma();
  1715. $('body').removeClass('isolatedGoogleClass');
  1716. $(".btn.google").hide();
  1717. $(".background").addClass("trans");
  1718. if (dtps.classes[dtps.selectedClass]) {
  1719. if (dtps.classes[dtps.selectedClass].image && (fluid.get("pref-classImages") !== "true")) {
  1720. $(".cover.image").css("background-image", 'url("' + dtps.classes[dtps.selectedClass].image + '")');
  1721. $(".background").css("opacity", '0.90');
  1722. $(".background").css("filter", 'none');
  1723. } else {
  1724. $(".cover.image").css("background-image", 'none');
  1725. $(".background").css("opacity", '1');
  1726. $(".background").css("filter", 'blur(10px)');
  1727. }
  1728. } else {
  1729. $(".cover.image").css("background-image", 'none');
  1730. $(".background").css("opacity", '1');
  1731. $(".background").css("filter", 'blur(10px)');
  1732. }
  1733. clearTimeout(dtps.bgTimeout);
  1734. dtps.bgTimeout = setTimeout(function () {
  1735. dtps.oldTheme = "squidward theme park";
  1736. document.dispatchEvent(new CustomEvent('fluidTheme'))
  1737. $(".background").removeClass("trans");
  1738. }, 500);
  1739. $(".background").removeClass(jQuery.grep($(".background").attr("class").split(" "), function (item, index) {
  1740. return item.trim().match(/^filter_/);
  1741. })[0]);
  1742. $(".cacaoBar .tab.active").removeClass(jQuery.grep($(".cacaoBar .tab.active").attr("class").split(" "), function (item, index) {
  1743. return item.trim().match(/^filter_/);
  1744. })[0]);
  1745. $(".header").removeClass(jQuery.grep($(".header").attr("class").split(" "), function (item, index) {
  1746. return item.trim().match(/^filter_/);
  1747. })[0]);
  1748. $(".classContent").removeClass(jQuery.grep($(".classContent").attr("class").split(" "), function (item, index) {
  1749. return item.trim().match(/^filter_/);
  1750. })[0]);
  1751. if (dtps.classes[dtps.selectedClass]) {
  1752. if (dtps.classes[dtps.selectedClass].google) {
  1753. $(".btn.google").show();
  1754. };
  1755. $(".background").addClass(dtps.classes[dtps.selectedClass].col);
  1756. $(".cacaoBar .tab.active").addClass(dtps.classes[dtps.selectedClass].col);
  1757. $(".header").addClass(dtps.classes[dtps.selectedClass].col);
  1758. $(".classContent").addClass(dtps.classes[dtps.selectedClass].col);
  1759. }
  1760. $(this).siblings().removeClass("active")
  1761. $(this).addClass("active")
  1762. $(".header h1").html($(this).children(".name").text())
  1763. $(".cacaoBar .tab.active span").html($(this).children(".name").text())
  1764. if (dtps.selectedClass == "dash") {
  1765. $(".cacaoBar .tab.active i").html("dashboard")
  1766. } else {
  1767. $(".cacaoBar .tab.active i").html(($(".btn." + dtps.selectedContent + " i").html() ? $(".btn." + dtps.selectedContent + " i").html() : "dashboard"))
  1768. }
  1769. if (!dtps.classes[dtps.selectedClass]) {
  1770. $(".header .btns").hide();
  1771. } else {
  1772. $(".header .btns:not(.master)").show();
  1773. }
  1774. if ((dtps.selectedContent == "stream") && (dtps.classes[dtps.selectedClass])) dtps.classStream(dtps.selectedClass)
  1775. if ((dtps.selectedContent == "grades") && (dtps.classes[dtps.selectedClass])) dtps.gradebook(dtps.selectedClass)
  1776. if (dtps.selectedClass == "dash") dtps.masterStream(true);
  1777. if (dtps.selectedClass == "announcements") dtps.announcements();
  1778. if (dtps.classes[dtps.selectedClass]) { if (dtps.classes[dtps.selectedClass].pagesTab) { $(".btns .btn.pages").show(); } else { $(".btns .btn.pages").hide(); } }
  1779. if (dtps.classes[dtps.selectedClass]) { if (dtps.classes[dtps.selectedClass].noOutcomes) { $(".btns .btn.grades").hide(); } else { $(".btns .btn.grades").show(); } }
  1780. });
  1781. }
  1782. if (override == "first") {
  1783. if (dtps.currentClass) {
  1784. $(".class." + dtps.selectedClass).click();
  1785. }
  1786. }
  1787. }
  1788.  
  1789. //Fetches Google Classroom assignment data for all Google Classes
  1790. dtps.googleStream = function () {
  1791. dtps.log("FETCHING GOOGLE ASSIGNMENTS")
  1792. function googleStream(i) {
  1793. if (dtps.googleClasses[i]) {
  1794. gapi.client.classroom.courses.courseWork.list({ courseId: dtps.googleClasses[i].id }).then(function (resp) {
  1795. dtps.googleClasses[i].rawData = resp.result;
  1796. dtps.googleClasses[i].stream = [];
  1797. for (var ii = 0; ii < resp.result.courseWork.length; ii++) {
  1798. if (resp.result.courseWork[ii].dueDate) {
  1799. var due = new Date(resp.result.courseWork[ii].dueDate.year, resp.result.courseWork[ii].dueDate.month - 1, resp.result.courseWork[ii].dueDate.day - 1);
  1800. } else {
  1801. var due = new Date();
  1802. }
  1803. dtps.googleClasses[i].stream.push({
  1804. title: resp.result.courseWork[ii].title,
  1805. due: due.toHumanString(),
  1806. dueDate: due.toISOString(),
  1807. turnedIn: false,
  1808. google: true,
  1809. url: resp.result.courseWork[ii].alternateLink + "?authuser=" + dtps.user.google.getEmail(),
  1810. letter: "--",
  1811. grade: "/" + resp.result.courseWork[ii].maxPoints
  1812. })
  1813. if (dtps.googleClasses[i].psClass !== undefined) {
  1814. dtps.googleClasses[i].stream[ii].class = dtps.googleClasses[i].psClass;
  1815. dtps.googleClasses[i].stream[ii].subject = dtps.classes[dtps.googleClasses[i].psClass].subject;
  1816. }
  1817. }
  1818. if (i < (dtps.googleClasses.length - 1)) googleStream(i + 1);
  1819. });
  1820. } else {
  1821. if (i < (dtps.googleClasses.length - 1)) googleStream(i + 1);
  1822. }
  1823. }
  1824. googleStream(0);
  1825. }
  1826.  
  1827. //Authenticates the user for Google Classroom integration
  1828. dtps.googleAuth = function () {
  1829. dtps.log("GOOGLE AUTH")
  1830. dtps.user.google = gapi.auth2.getAuthInstance().currentUser.get().getBasicProfile();
  1831. $(".items img").attr("src", dtps.user.google.getImageUrl())
  1832. gapi.client.classroom.courses.list({ pageSize: 40 }).then(function (resp) {
  1833. dtps.googleClasses = resp.result.courses;
  1834. if (dtps.googleClasses == undefined) {
  1835. dtps.gapis();
  1836. } else {
  1837. for (var i = 0; i < dtps.googleClasses.length; i++) {
  1838. if (dtps.googleClasses[i].courseState == "ACTIVE") {
  1839. var match = null;
  1840. for (var ii = 0; ii < dtps.classes.length; ii++) {
  1841. if (dtps.googleClasses[i].name.includes(dtps.classes[ii].subject)) match = ii;
  1842. }
  1843. if (match !== null) {
  1844. if (dtps.classes[match].google == undefined) {
  1845. dtps.classes[match].google = dtps.googleClasses[i]
  1846. dtps.googleClasses[i].psClass = match
  1847. }
  1848. }
  1849. }
  1850. }
  1851. dtps.isolatedGoogleClasses = [];
  1852. var isolatedDom = [];
  1853. for (var i = 0; i < dtps.googleClasses.length; i++) {
  1854. if ((dtps.googleClasses[i].psClass == undefined) && (dtps.googleClasses[i].courseState == "ACTIVE")) {
  1855. dtps.isolatedGoogleClasses.push(i)
  1856. isolatedDom.push(`<br /><br />
  1857. <div onclick="jQuery('.google.isolated.class.` + dtps.googleClasses[i].id + `').toggle()" class="switch"><span class="head"></span></div>
  1858. <div class="label">` + dtps.googleClasses[i].name + `</div>`)
  1859. }
  1860. }
  1861. $(".isolatedGClassList").html(isolatedDom.join("").slice(12));
  1862. dtps.showClasses(true);
  1863. dtps.googleStream();
  1864. fluid.init();
  1865. }
  1866. });
  1867. }
  1868.  
  1869. //Saves grade data locally for grade trend
  1870. dtps.logGrades = function () {
  1871. if ((window.localStorage.dtpsGradeTrend !== "false") && (window.localStorage.dtpsGradeTrend !== undefined)) {
  1872. if (window.localStorage.dtpsGradeTrend.startsWith("{")) {
  1873. dtps.log("LOGGING GRADES")
  1874. var now = new Date();
  1875. var start = new Date(now.getFullYear(), 0, 0);
  1876. var diff = (now - start) + ((start.getTimezoneOffset() - now.getTimezoneOffset()) * 60 * 1000);
  1877. var oneDay = 1000 * 60 * 60 * 24;
  1878. var day = Math.floor(diff / oneDay);
  1879. var gradeData = JSON.parse(window.localStorage.dtpsGradeTrend);
  1880. for (var i = 0; i < dtps.classes.length; i++) {
  1881. if (dtps.classes[i].grade !== "--") {
  1882. if (!gradeData[dtps.classes[i].id]) gradeData[dtps.classes[i].id] = { oldGrade: dtps.classes[i].grade, lastUpdated: new Date(), currentGrade: dtps.classes[i].grade }
  1883. if (dtps.classes[i].grade !== gradeData[dtps.classes[i].id].currentGrade) {
  1884. gradeData[dtps.classes[i].id].oldGrade = gradeData[dtps.classes[i].id].currentGrade
  1885. gradeData[dtps.classes[i].id].currentGrade = dtps.classes[i].grade
  1886. gradeData[dtps.classes[i].id].lastUpdated = new Date();
  1887. }
  1888. }
  1889. }
  1890. window.localStorage.setItem("dtpsGradeTrend", JSON.stringify(gradeData));
  1891. }
  1892. }
  1893. }
  1894.  
  1895. //Enables/Disables grade trend
  1896. dtps.gradeTrend = function (ele) {
  1897. var temp = ele;
  1898. if ($(temp).hasClass('head')) { temp = $(ele).parent()[0] };
  1899. if (!$(temp).hasClass('active')) {
  1900. window.localStorage.setItem('dtpsGradeTrend', 'false');
  1901. swal('Grade trend is disabled. All data stored on your computer by grade trend has been deleted.', { icon: 'success', });
  1902. } else {
  1903. swal({ title: 'Enable grade trend', text: 'By enabling grade trend, Power+ will store a copy of your grades locally on your computer every time you use Power+. When a grade for one of your classes changes, Power+ will tell you how much it changed in the grades tab of the class. The grade trend setting applies to all classes.', buttons: true }).then((enable) => {
  1904. if (enable) {
  1905. window.localStorage.setItem('dtpsGradeTrend', '{}'); swal('Grade trend is enabled', { icon: 'success', });
  1906. } else {
  1907. $(temp).removeClass('active')
  1908. }
  1909. });
  1910. }
  1911. }
  1912.  
  1913. //cacao variables
  1914. dtps.states = {};
  1915. letters = 'abcdefghijklmnopqrstuvwxyz'.split('')
  1916. letter = 1;
  1917.  
  1918. //Tab click handler (cacao)
  1919. dtps.cacao = function (state) {
  1920. if (String(fluid.get("pref-cacao")) == "true") {
  1921. if (state == "new") {
  1922. $(".cacaoBar .tab.new").before(`<div onclick="dtps.cacao('` + letters[letter] + `');" state="` + letters[letter] + `" class="tab ` + letters[letter] + `"><i class="material-icons">dashboard</i><span>Dashboard</span></div>`)
  1923. letter++;
  1924. } else {
  1925. dtps.saveState($(".cacaoBar .tab.active").attr("state"));
  1926. $(".cacaoBar .tab." + state).siblings().removeClass("active")
  1927. $(".cacaoBar .tab." + state).addClass("active")
  1928. dtps.loadState(state);
  1929. }
  1930. }
  1931. }
  1932.  
  1933. //Save state (cacao)
  1934. dtps.saveState = function (state) {
  1935. if (String(fluid.get("pref-cacao")) == "true") {
  1936. dtps.states[state] = {
  1937. class: dtps.selectedClass,
  1938. content: dtps.selectedContent,
  1939. page: dtps.selectedPage,
  1940. classContent: $(".classContent").html(),
  1941. scrollTop: document.documentElement.scrollTop,
  1942. scrollLeft: document.documentElement.scrollLeft
  1943. }
  1944. }
  1945. }
  1946.  
  1947. //Load state (cacao)
  1948. dtps.loadState = function (stateKey) {
  1949. if (String(fluid.get("pref-cacao")) == "true") {
  1950. var state = dtps.states[stateKey]
  1951. if (state) {
  1952. dtps.selectedClass = state.class;
  1953. dtps.selectedContent = state.content;
  1954. dtps.selectedPage = state.page;
  1955. dtps.showClasses(true);
  1956. $(".class." + (state.class == "dash" ? "masterStream" : state.class)).click();
  1957. if (state.content == "pages") {
  1958. $(".header .btn.pages").click();
  1959. $(".sidebar .btn." + dtps.selectedPage).click();
  1960. }
  1961. $(".classContent").html(state.classContent);
  1962. document.documentElement.scrollTop = state.scrollTop;
  1963. document.documentElement.scrollLeft = state.scrollLeft;
  1964. } else {
  1965. //no state present, load default dashboard state
  1966. dtps.selectedClass = "dash"
  1967. dtps.selectedContent = "stream"
  1968. document.documentElement.scrollTop = 0;
  1969. document.documentElement.scrollLeft = 0;
  1970. dtps.showClasses(true);
  1971. $(".class.masterStream").click();
  1972. dtps.saveState(stateKey)
  1973. }
  1974. }
  1975. }
  1976.  
  1977. //Renders Power+ and removes all Canvas HTML
  1978. //Seperated into static HTML and javascript-based things
  1979. dtps.render = function () {
  1980. if (dtps.embedded) {
  1981. jQuery("head").html("");
  1982. $("body").addClass("dashboard");
  1983. }
  1984. document.title = "Power+" + dtps.trackSuffix;
  1985.  
  1986. //Cacao pref
  1987. if (fluid.get("pref-cacao") == "true") { $("body").addClass("cacao"); $('.sidebar').addClass("acrylicMaterial"); }
  1988. document.addEventListener("pref-cacao", function (e) {
  1989. if (String(e.detail) == "true") {
  1990. $("body").addClass("cacao");
  1991. $('.sidebar').addClass("acrylicMaterial");
  1992. } else {
  1993. $("body").removeClass("cacao");
  1994. $('.sidebar').removeClass("acrylicMaterial");
  1995. }
  1996. })
  1997.  
  1998. //Full names pref
  1999. if (fluid.get("pref-fullNames") == "true") { dtps.fullNames = true; }
  2000. document.addEventListener("pref-fullNames", function (e) {
  2001. if (String(e.detail) == "true") { dtps.fullNames = true; } else { dtps.fullNames = false; }
  2002. dtps.showClasses(true);
  2003. })
  2004.  
  2005. //Hide grades pref
  2006. if (fluid.get("pref-hideGrades") == "true") { jQuery('body').addClass('hidegrades'); }
  2007. document.addEventListener("pref-hideGrades", function (e) {
  2008. if (String(e.detail) == "true") { jQuery('body').addClass('hidegrades'); } else { jQuery('body').removeClass('hidegrades'); }
  2009. dtps.showClasses(true);
  2010. })
  2011.  
  2012. if (dtps.embedded) {
  2013. jQuery("body").html(`
  2014. <div style="line-height: 0;" class="sidebar">
  2015. </div>
  2016. <div class="cover image"></div>
  2017. <div class="background trans"></div>
  2018. <div class="header">
  2019. <h1 id="headText">Dashboard</h1>
  2020. <div style="display: none;" class="btns row tabs">
  2021. <button onclick="dtps.selectedContent = 'stream'; dtps.chroma(); $('.cacaoBar .tab.active i').html('assignment'); dtps.classStream(dtps.selectedClass);" class="btn active stream">
  2022. <i class="material-icons">library_books</i>
  2023. Coursework
  2024. </button>
  2025. <button onclick="dtps.selectedContent = 'google'; dtps.chroma(); $('.cacaoBar .tab.active i').html('class'); $('.classContent').html(dtps.renderStream(dtps.classes[dtps.selectedClass].google.stream))" class="btn google">
  2026. <i class="material-icons">class</i>
  2027. google_logo Classroom
  2028. </button>
  2029. <button onclick="dtps.selectedContent = 'discuss'; dtps.chroma(); $('.cacaoBar .tab.active i').html('group'); dtps.loadTopics(dtps.selectedClass);" class="btn discuss">
  2030. <i class="material-icons">forum</i>
  2031. Discussions
  2032. </button>
  2033. <button onclick="dtps.selectedContent = 'pages'; dtps.chroma(); $('.cacaoBar .tab.active i').html('insert_drive_file'); dtps.loadPages(dtps.selectedClass);" class="btn pages">
  2034. <i class="material-icons">insert_drive_file</i>
  2035. Pages
  2036. </button>
  2037. <button onclick="dtps.selectedContent = 'grades'; dtps.chroma(); $('.cacaoBar .tab.active i').html('assessment'); dtps.gradebook(dtps.selectedClass);" class="btn grades">
  2038. <i class="material-icons">assessment</i>
  2039. Grades
  2040. </button>
  2041. </div>
  2042. </div>
  2043. <div class="classContent">
  2044. <div class="spinner"></div>
  2045. </div>
  2046. <div style="height: calc(100vh - 50px); overflow: auto !important;" class="card withnav focus close container abt-new"></div>
  2047.  
  2048. <div style="display: none;" class="cacaoBar acrylicMaterial">
  2049. <div onclick="dtps.cacao('a')" state="a" class="tab a active"><i class="material-icons">dashboard</i><span>Dashboard</span></div>
  2050. <div onclick="dtps.cacao('new')" class="tab icon new"><i class="material-icons">add</i></div>
  2051. </div>
  2052. <div class="items">
  2053. </div>
  2054. <div style="border-radius: 30px;" class="card focus changelog close container">
  2055. <i onclick="fluid.cards.close('.card.changelog')" class="material-icons close">close</i>
  2056. <h3>What's new in Power+</h3>
  2057. <h5>There was an error loading the changelog. Try again later.</h5>
  2058. </div>
  2059. <div style="border-radius: 30px;" class="card focus details close container">
  2060. <i onclick="fluid.cards.close('.card.details')" class="material-icons close">close</i>
  2061. <p>An error occured</p>
  2062. </div>
  2063.  
  2064. <div style="border-radius: 30px; top: 50px; background-color: white; color: black;" class="card focus close moduleURL container">
  2065. <i style="color: black !important;" onclick="fluid.cards.close('.card.moduleURL'); $('#moduleIFrame').attr('src', '');" class="material-icons close">close</i>
  2066. <br /><br />
  2067. <iframe style="width: 100%; height: calc(100vh - 175px); border: none;" id="moduleIFrame"></iframe>
  2068. </div>
  2069.  
  2070. <div style="border-radius: 30px; top: 50px;" class="card focus close classInfoCard container">
  2071. <i onclick="fluid.cards.close('.card.classInfoCard')" class="material-icons close">close</i>
  2072. <h4>An error occured</h4>
  2073. </div>
  2074.  
  2075. <div style="border-radius: 30px; top: 50px;" class="card focus close outcomeCard container">
  2076. <i onclick="fluid.cards.close('.card.outcomeCard')" class="material-icons close">close</i>
  2077. <h4>An error occured</h4>
  2078. </div>
  2079.  
  2080. <style id="colorCSS"></style>
  2081. <script>fluid.init();</script>
  2082. `);
  2083. }
  2084.  
  2085.  
  2086. jQuery("#colorCSS").html(dtps.colorCSS ? dtps.colorCSS.join("") : "")
  2087. if (dtps.embedded) dtps.renderLite();
  2088.  
  2089.  
  2090.  
  2091. var idleTime = 0;
  2092. $(document).ready(function () {
  2093. //Increment the idle time counter every minute.
  2094. var idleInterval = setInterval(timerIncrement, 60000); // 1 minute
  2095.  
  2096. //Zero the idle timer on mouse movement.
  2097. $(this).mousemove(function (e) {
  2098. idleTime = 0;
  2099. });
  2100. $(this).keypress(function (e) {
  2101. idleTime = 0;
  2102. });
  2103. });
  2104.  
  2105. function timerIncrement() {
  2106. idleTime = idleTime + 1;
  2107. if (idleTime > 60) { // Clear all saved assignment data to get the latest info
  2108. dtps.requests = {};
  2109. dtps.http = {};
  2110. dtps.classesReady = 0;
  2111. for (var i = 0; i < dtps.classes.length; i++) {
  2112. dtps.classStream(i, true);
  2113. }
  2114. }
  2115. }
  2116.  
  2117. var getURL = "https://api.github.com/repos/jottocraft/dtps/commits?path=init.js";
  2118. //if (dtps.trackSuffix !== "") var getURL = "https://api.github.com/repos/jottocraft/dtps/commits?path=dev.js";
  2119. jQuery.getJSON(getURL, function (data) {
  2120. jQuery(".buildInfo").html("build " + data[0].sha.substring(7, 0));
  2121. jQuery(".buildInfo").click(function () {
  2122. window.open("https://github.com/jottocraft/dtps/commit/" + data[0].sha)
  2123. });
  2124. })
  2125. jQuery.getScript("https://cdnjs.cloudflare.com/ajax/libs/showdown/1.8.6/showdown.min.js", function () {
  2126. markdown = new showdown.Converter();
  2127. jQuery.getJSON("https://api.github.com/repos/jottocraft/dtps/releases", function (data) {
  2128. jQuery(".card.changelog").html(`<i onclick="fluid.cards.close('.card.changelog')" class="material-icons close">close</i>` + markdown.makeHtml(data[0].body));
  2129. if (data[0].tag_name == dtps.readableVer.replace(dtps.trackSuffix, "")) {
  2130. localStorage.setItem('dtps', dtps.ver);
  2131. if (dtps.showChangelog) dtps.changelog();
  2132. }
  2133. $(".btn.changelog").show();
  2134. });
  2135. });
  2136.  
  2137. fluid.theme();
  2138. dtps.showClasses("first");
  2139. //dtps.gapis();
  2140.  
  2141. if (dtps.embedded) {
  2142. $("link").remove();
  2143. jQuery("<link/>", {
  2144. rel: "shortcut icon",
  2145. type: "image/png",
  2146. href: "https://dtps.js.org/favicon.png"
  2147. }).appendTo("head");
  2148. jQuery("<link/>", {
  2149. rel: "stylesheet",
  2150. type: "text/css",
  2151. href: "https://dtps.js.org/fluid.css"
  2152. }).appendTo("head");
  2153. jQuery("<link/>", {
  2154. rel: "stylesheet",
  2155. type: "text/css",
  2156. href: "https://cdnjs.cloudflare.com/ajax/libs/fullcalendar/3.9.0/fullcalendar.min.css"
  2157. }).appendTo("head");
  2158.  
  2159. if (dtps.trackSuffix !== "") {
  2160. jQuery("<link/>", {
  2161. rel: "stylesheet",
  2162. type: "text/css",
  2163. href: "https://dtps.js.org/dev.css"
  2164. }).appendTo("head");
  2165. } else {
  2166. jQuery("<link/>", {
  2167. rel: "stylesheet",
  2168. type: "text/css",
  2169. href: "https://dtps.js.org/dtps.css"
  2170. }).appendTo("head");
  2171. }
  2172.  
  2173. jQuery("<link/>", {
  2174. rel: "stylesheet",
  2175. type: "text/css",
  2176. href: "https://fonts.googleapis.com/icon?family=Material+Icons+Extended"
  2177. }).appendTo("head");
  2178.  
  2179. }
  2180.  
  2181. jQuery('.classContent').bind('heightChange', function () {
  2182. jQuery(".sidebar").css("height", Number(jQuery(".classContent").css("height").slice(0, -2)))
  2183. });
  2184.  
  2185. if (dtps.embedded) fluid.onLoad();
  2186. }
  2187.  
  2188. //Render function that can ran before classes are ready
  2189. dtps.renderLite = function () {
  2190. if (fluid.chroma) {
  2191. fluid.chroma.supported(function (res) {
  2192. if (res) {
  2193. //Razer Synapse installed
  2194. $(".razerChroma").show();
  2195.  
  2196. //Razer Chroma pref
  2197. if (fluid.get("pref-chromaEffects") == "true") { if (!fluid.chroma.on) { fluid.chroma.init(dtps.chromaProfile, () => fluid.chroma.static(getComputedStyle($(".background")[0]).getPropertyValue("--dark"))); } }
  2198. document.addEventListener("pref-chromaEffects", function (e) {
  2199. if (String(e.detail) == "true") { if (!fluid.chroma.on) { fluid.chroma.init(dtps.chromaProfile, () => fluid.chroma.static(getComputedStyle($(".background")[0]).getPropertyValue("--dark"))); } } else { fluid.chroma.disable(); }
  2200. })
  2201. }
  2202. })
  2203. }
  2204. var trackDom = "";
  2205. if (dtps.trackSuffix !== "") {
  2206. trackDom = `<div style="display:inline-block;font-size: 16px; padding: 3px 4px;background-color: ` + dtps.trackColor + `" class="beta badge notice">` + dtps.trackSuffix.replace(" (", "").replace(")", "") + `</div>`
  2207. } else {
  2208. trackDom = ``;
  2209. }
  2210. var verDom = dtps.readableVer.replace(dtps.trackSuffix, "");
  2211. if (dtps.trackSuffix !== "") {
  2212. verDom = `<div class="buildInfo" style="display: inline-block;font-size: 12px;cursor: pointer;"></div>`
  2213. } else {
  2214. verDom = dtps.readableVer.replace(dtps.trackSuffix, "");
  2215. }
  2216. jQuery(".card.abt-new").html(`<i onclick="fluid.cards.close('.card.abt-new')" class="material-icons close">close</i>
  2217. <div class="sidenav" style="position: fixed; height: calc(100% - 50px); border-radius: 20px 0px 0px 20px;">
  2218. <div class="title">
  2219. <img src="https://dtps.js.org/outline.png" style="width: 50px;vertical-align: middle;padding: 7px; padding-top: 14px;" />
  2220. <div style="vertical-align: middle; display: inline-block;">
  2221. <h5 style="font-weight: bold;display: inline-block;vertical-align: middle;">Power+</h5>` + trackDom + `
  2222. <p style="font-weight: bold;">` + verDom + `</p>
  2223. </div>
  2224. </div>
  2225. <div onclick="$('.abtpage').hide();$('.abtpage.settings').show();" class="item active">
  2226. <i class="material-icons">settings</i> Settings
  2227. </div>
  2228. <div onclick="$('.abtpage').hide();$('.abtpage.classes').show();" class="item">
  2229. <i class="material-icons">book</i> Classes
  2230. </div>
  2231. <div onclick="$('.abtpage').hide();$('.abtpage.experiments').show();" style="/*display: none !important;*/" class="item sudo">
  2232. <i class="material-icons" style="font-family: 'Material Icons Extended'">experiment</i> Experiments
  2233. </div>
  2234. <div onclick="$('.abtpage').hide();$('.abtpage.debug').show();" class="item dev">
  2235. <i class="material-icons">bug_report</i> Debugging
  2236. </div>
  2237. <div onclick="$('.abtpage').hide();$('.abtpage.about').show(); if ($('body').hasClass('sudo')) { $('.advancedOptions').show(); $('.advOp').hide(); } else { $('.advancedOptions').hide(); $('.advOp').show(); }" class="item">
  2238. <i class="material-icons">info</i> About
  2239. </div>
  2240. </div>
  2241. <div style="min-height: 100%" class="content">
  2242. <div class="abtpage settings">
  2243. <h5>Settings</h5>
  2244. <br />
  2245. <p>Theme</p>
  2246. <div class="btns row themeSelector"></div>
  2247. <br />
  2248. <p>Grades</p>
  2249. <div onclick="fluid.set('pref-calcGrades')" class="switch pref-calcGrades active"><span class="head"></span></div>
  2250. <div class="label"><i class="material-icons">functions</i> Calculate class grades (beta)</div>
  2251. <br /><br />
  2252. <div onclick="fluid.set('pref-hideGrades')" class="switch pref-hideGrades"><span class="head"></span></div>
  2253. <div class="label"><i class="material-icons">visibility_off</i> Hide class grades</div>
  2254. <!-- <br /><br />
  2255. <div onclick="dtps.gradeTrend(this);" class="switch` + (String(window.localStorage.dtpsGradeTrend).startsWith("{") ? " active" : "") + `"><span class="head"></span></div>
  2256. <div class="label"><i class="material-icons">timeline</i> Show grade trend</div> -->
  2257. <br /><br />
  2258. <p>Classes</p>
  2259. <div onclick="fluid.set('pref-fullNames')" class="switch pref-fullNames"><span class="head"></span></div>
  2260. <div class="label"><i class="material-icons">title</i> Show full class names</div>
  2261. <br /><br />
  2262. <div onclick="fluid.set('pref-classImages')" class="switch pref-classImages"><span class="head"></span></div>
  2263. <div class="label"><i class="material-icons">image</i> Hide class images</div>
  2264. <br style="display: none;" class="razerChroma" /><br style="display: none;" class="razerChroma" />
  2265. <div style="display: none" onclick="fluid.set('pref-chromaEffects')" class="switch pref-chromaEffects razerChroma"><span class="head"></span></div>
  2266. <div class="label razerChroma" style="display: none;"><img style="width: 26px;vertical-align: middle;margin-right: 2px;" src="https://i.imgur.com/FLwviAM.png" class="material-icons" /img> Razer Chroma Effects (beta)</div>
  2267. <div class="embeddedOptions">
  2268. <br /><br />
  2269. <p>Power+</p>
  2270. <div onclick="fluid.set('pref-autoLoad')" class="switch pref-autoLoad"><span class="head"></span></div>
  2271. <div class="label"><i class="material-icons">code</i> Automatically load Power+</div>
  2272. <!-- <br /><br />
  2273. <div onclick="fluid.set('pref-devChannel')" class="switch pref-devChannel"><span class="head"></span></div>
  2274. <div class="label"><i class="material-icons">bug_report</i> Use the unstable (dev) version of Power+</div> -->
  2275. </div>
  2276. </div>
  2277. <div style="display: none;" class="abtpage classes">
  2278. <h5>Classes</h5>
  2279. <button onclick="dtps.schedule()" class="btn"><i class="material-icons">access_time</i>Schedule classes</button>
  2280. <br /><br />
  2281. <div id="classGrades">
  2282. <h5>Grades</h5>
  2283. <div class="gradeDom">
  2284. <p>Loading...</p>
  2285. </div>
  2286. </div>
  2287. <!--
  2288. <br /><br />
  2289. <div class="googleClassroom sudo">
  2290. <h5>google_logo Classes</h5>
  2291. <button class="btn" onclick="window.alert('On the page that opens, select Project DTPS, and click Remove Access.'); window.open('https://myaccount.google.com/permissions?authuser=' + dtps.user.google.getEmail());"><i class="material-icons">link_off</i>Unlink Google Classroom</button>
  2292. <br /><br />
  2293. <p>Classes listed below could not be associated with a Canvas class. You can choose which classes to show in the sidebar.</p>
  2294. <div class="isolatedGClassList"><p>Loading...</p></div>
  2295. </div>
  2296. <div class="googleSetup sudo">
  2297. <h5>google_logo Classroom <div class="badge">beta</div></h5>
  2298. <p>Link google_logo Classroom to see assignments and classes from both Canvas and Google.</p>
  2299. <p>If Power+ thinks one of your Canvas classes also has a Google Classroom, it'll add a Google Classroom tab to that class. You can choose which extra classes to show in the sidebar.</p>
  2300. <button onclick="if (window.confirm('EXPERIMENTAL FEATURE: Google Classroom features are still in development. Continue at your own risk. Please leave feedback by clicking the feedback button at the top right corner of Power+.')) { dtps.googleSetup = true; dtps.webReq('psGET', 'https://dtechhs.learning.powerschool.com/do/account/logout', function() { gapi.auth2.getAuthInstance().signIn().catch(function(err) { /*window.location.reload()*/ console.warn(err); }); })}" class="btn sudo"><i class="material-icons">link</i>Link Google Classroom</button>
  2301. </div>
  2302. -->
  2303. </div>
  2304. <div style="display: none;" class="abtpage extension">
  2305. <h5>Extension</h5>
  2306. <div class="extensionDom" ></div>
  2307. </div>
  2308. <div style="display: none;" class="abtpage apiExplorer">
  2309. <h5>API Explorer</h5>
  2310. <ul>` + dtps.explorer.map(function (item) {
  2311. return `<li style="cursor: pointer;" onclick="dtps.explore('` + item.path + `')">` + item.name + `</li>`
  2312. }).join("") + `</ul>
  2313. <br />
  2314. <pre><code id="explorerData">Select an item
  2315. </code></pre>
  2316. </div>
  2317. <div style="display: none;" class="abtpage experiments">
  2318. <div class="sudo">
  2319. <h5>Experiments</h5>
  2320. <p>WARNING: Features listed below are not officially supported and can break Power+. Use at your own risk.</p>
  2321. <p>Want to test out new features as they are developed? <a href="https://dtps.js.org/devbookmark.txt">Try the dev version of Power+</a>.</p>
  2322. <br />
  2323. <div onclick="fluid.set('pref-cacao')" class="switch pref-cacao"><span class="head"></span></div>
  2324. <div class="label"><i class="material-icons">view_carousel</i> Project Cacao</div>
  2325. <br /><br />
  2326. </div>
  2327. </div>
  2328. <div style="display: none;" class="abtpage debug">
  2329. <div class="dev">
  2330. <h5>Debugging</h5>
  2331. <br>
  2332. <div id="dtpsLocal" onclick="fluid.set('pref-localDtps')" class="switch pref-localDtps"><span class="head"></span></div>
  2333. <div class="label"><i class="material-icons">public</i> Use local copy of Project DTPS</div>
  2334. <br /><br>
  2335. <button onclick="$('body').removeClass('sudo');$('body').removeClass('contributor');$('body').removeClass('dev');">Remove badges</button>
  2336. <br /><br>
  2337. <span class="log">
  2338. </span>
  2339. </div>
  2340. </div>
  2341. <div style="display: none;" class="abtpage about">
  2342. <h5>About</h5>
  2343. <div class="card" style="padding: 10px 20px; box-shadow: none !important; border: 2px solid var(--elements); margin-top: 20px;">
  2344. <img src="https://dtps.js.org/outline.png" style="height: 50px; margin-right: 10px; vertical-align: middle; margin-top: 20px;" />
  2345. <div style="display: inline-block; vertical-align: middle;">
  2346. <h4 style="font-weight: bold; font-size: 32px; margin-bottom: 0px;">Power+</h4>
  2347. <div style="font-size: 16px; margin-top: 5px;">` + dtps.readableVer + ` <div class="buildInfo" style="display: inline-block;margin: 0px 5px;font-size: 12px;cursor: pointer;"></div></div>
  2348. </div>
  2349. <div style="margin-top: 15px; margin-bottom: 7px;"><a onclick="dtps.changelog();" style="color: var(--lightText); margin: 0px 5px;" href="#"><i class="material-icons" style="vertical-align: middle">update</i> Changelog</a>
  2350. <a onclick="if (window.confirm('Are you sure you want to uninstall Power+? The extension will be removed and all of your Power+ data will be erased.')) { document.dispatchEvent(new CustomEvent('extensionData', { detail: 'extensionUninstall' })); window.localStorage.clear(); window.alert('Power+ has been uninstalled. Reload the page to go back to Canvas.') }" style="color: var(--lightText); margin: 0px 5px;" href="#"><i class="material-icons" style="vertical-align: middle">delete_outline</i> Uninstall</a>
  2351. <a style="color: var(--lightText); margin: 0px 5px;" href="https://github.com/jottocraft/dtps"><i class="material-icons" style="vertical-align: middle">code</i> GitHub</a></div>
  2352. </div>
  2353. <div class="card" style="padding: 10px 20px; box-shadow: none !important; border: 2px solid var(--elements); margin-top: 20px;">
  2354. <img src="` + dtps.user.avatar_url + `" style="height: 50px; margin-right: 10px; vertical-align: middle; margin-top: 20px; border-radius: 50%;" />
  2355. <div style="display: inline-block; vertical-align: middle;">
  2356. <h4 style="font-weight: bold; font-size: 32px; margin-bottom: 0px;">` + dtps.user.name + ` <span style="font-size: 12px;">` + dtps.user.id + `</span></h4>
  2357.  
  2358.  
  2359. <div style="display:inline-block;" class="badge marketer">marketer<i style="vertical-align: middle;" class="material-icons marketer">work_outline</i></div>
  2360. <div style="display:inline-block;" class="badge sudo">tester<i style="vertical-align: middle;" class="material-icons sudo">bug_report</i></div>
  2361. <div style="display:inline-block;" class="badge contributor">contributor<i style="vertical-align: middle;" class="material-icons contributor">group</i></div>
  2362. <div style="display:inline-block;" class="badge dev">developer<i style="vertical-align: middle;" class="material-icons dev">code</i></div>
  2363. </div>
  2364. <div style="margin-top: 15px; margin-bottom: 7px;"><a style="color: var(--lightText); margin: 0px 5px;" href="/logout"><i class="material-icons" style="vertical-align: middle">exit_to_app</i> Logout</a></div>
  2365. </div>
  2366. <div class="card advancedOptions" style="padding: 8px 16px; box-shadow: none !important; border: 2px solid var(--elements); margin-top: 20px; display: none;">
  2367. <div style="display: inline-block; vertical-align: middle;">
  2368. <h4 style="font-weight: bold; font-size: 28px; margin-bottom: 0px;">Advanced Options</h4>
  2369. </div>
  2370. <div style="margin-top: 15px; margin-bottom: 7px;">
  2371. <a style="color: var(--lightText); margin: 0px 5px;" onclick="dtps.clearData();" href="#"><i class="material-icons" style="vertical-align: middle">refresh</i> Reset Power+</a>
  2372. <a style="color: var(--lightText); margin: 0px 5px;" onclick="$('.abtpage').hide();$('.abtpage.apiExplorer').show();" href="#"><i class="material-icons" style="vertical-align: middle">folder_shared</i> API Explorer</a>
  2373. <a style="color: var(--lightText); margin: 0px 5px;" href="https://github.com/jottocraft/dtps/issues/new/choose"><i class="material-icons" style="vertical-align: middle">feedback</i> Send feedback</a></div>
  2374. </div>
  2375. <br />
  2376. <p style="cursor: pointer; color: var(--secText, gray)" onclick="$('.advancedOptions').toggle(); $(this).hide();" class="advOp">Show advanced options</p>
  2377. <p>(c) 2018-2019 jottocraft (<a href="https://github.com/jottocraft/dtps/blob/master/LICENSE">license</a>)</p>
  2378. </div>
  2379. </div>`)
  2380. jQuery(".items").html(`<h4>` + dtps.user.name + `</h4>
  2381. <img src="` + dtps.user.avatar_url + `" style="width: 50px; height: 50px; margin: 0px 5px; border-radius: 50%; vertical-align: middle;box-shadow: 0 5px 5px rgba(0, 0, 0, 0.17);" />
  2382. <i onclick="window.open('https://github.com/jottocraft/dtps/issues/new/choose')" class="material-icons prerelease">feedback</i>
  2383. <i onclick="if (dtps.gradeHTML) { $('.gradeDom').html((dtps.gpa ? '<p>Estimated GPA (beta): ' + dtps.gpa + '</p>' : '') + dtps.gradeHTML.join('')); if (dtps.gradeHTML.length == 0) { $('#classGrades').hide(); } else { $('#classGrades').show(); }; } else {$('#classGrades').hide();}; fluid.modal('.abt-new')" class="material-icons">settings</i>`);
  2384. if (!dtps.embedded) $(".embeddedOptions").hide();
  2385. if (!dtps.embedded) fluid.onLoad();
  2386. }
  2387.  
  2388. dtps.init();
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement