// ==UserScript==
// @name ASU Canvas/PlayPosit QOL Mod
// @namespace http://tampermonkey.net/
// @version v0.2
// @description Quality of life improvements for ASU online course videos.
// @author Jacob Biddle
// @match https://canvas.asu.edu/courses/*
// @match https://api.playposit.com/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=asu.edu
// @grant none
// @run-at document-start
// @license MIT
// @downloadURL https://update.greasyfork.cloud/scripts/530140/ASU%20CanvasPlayPosit%20QOL%20Mod.user.js
// @updateURL https://update.greasyfork.cloud/scripts/530140/ASU%20CanvasPlayPosit%20QOL%20Mod.meta.js
// ==/UserScript==
(function() {
'use strict';
var wrapper, lti_embed, playerIframe, transcript, transcript_wrapper, vt, alt_transcript;
// All updates run through a DOM mutation observer for simplicity.
var observer = new MutationObserver(onMutated);
observer.observe(document, {childList: true, subtree: true});
function onMutated(changes, observer) {
// Canvas video wrapper. Default limits to 800x600. Just overwride css to 100% width to match page layout.
// Also have to update the content wrapper to remove max-width limits
if (!lti_embed) {
lti_embed = document.getElementsByClassName("lti-embed")[0]
if (lti_embed) {
wrapper = document.getElementById("wrapper")
}
} else {
lti_embed.style.width = "100%";
lti_embed.style.height = "auto";
lti_embed.style['min-height'] = "600px";
lti_embed.style['aspect-ratio'] = "1.667";
wrapper.style['max-width'] = "100%";
}
// This is an intermediate iframe
//if (!playerIframe)
// playerIframe = document.getElementById('playerIframe')
// PlayPosit video player speed option hook - inserts some additional speeds after the vue component renders
let speed_settings = document.getElementById("playback-speed-select-all")
if (speed_settings && speed_settings.innerHTML.indexOf("playback-four") === -1) {
speed_settings.innerHTML += ''
speed_settings.innerHTML += ''
}
// PlayPosit sidebar updates for the transcript tab
if (!transcript || !transcript_wrapper) {
transcript = document.getElementById("transcript")
transcript_wrapper = document.getElementsByClassName("transcript-wrapper")[0]
// Initial set-up once the vue component is rendered
if (transcript && transcript_wrapper) {
vt = transcript.__vue__
vt.hasLoadedAltTranscript = false
vt.loadingAltTranscript = false
// Hook into the transcript loading function to update between videos
vt.__loadTranscriptFile = vt.loadTranscriptFile
vt.loadTranscriptFile = () => {
if (vt.hasLoadedAltTranscript) {
if (alt_transcript) {
alt_transcript.parentNode.removeChild(alt_transcript);
}
}
vt.hasLoadedAltTranscript = false
vt.__loadTranscriptFile()
}
// Add watch to vue transcript's activeIndex to highlight active and viewed text
vt.$watch('activeIndex', (newIdx, oldIdx) => {
if (alt_transcript && alt_transcript.children.length > Math.max(oldIdx, newIdx)) {
if (oldIdx > -1) {
alt_transcript.children[oldIdx].style.color = "gray";
alt_transcript.children[oldIdx].style["font-weight"] = "";
}
if (newIdx > -1) {
alt_transcript.children[newIdx].style.color = "blue";
alt_transcript.children[newIdx].style["font-weight"] = "700";
}
}
})
// Add scrolling to the transcript-wrapper
transcript_wrapper.setAttribute("style", "overflow-y:scroll")
}
} else {
// Populate a readable transcript if a new transcript is loaded
if (!vt.hasLoadedAltTranscript && !vt.loadingTranscript) {
vt.hasLoadedAltTranscript = true
alt_transcript = document.createElement('div')
alt_transcript.setAttribute("style", "margin: 0 2.5%; padding: 0 10px;");
transcript_wrapper.prepend(alt_transcript);
let captions = Object.values(vt.parsedTranscripts)[0]
for (let c in captions) {
let span = document.createElement('span')
span.innerHTML = captions[c].text + ' '
span.style.cursor = "pointer"
span.addEventListener("click", () => { vt.jump(captions[c]) });
span.onmouseover = () => {span.style["text-decoration-line"] = "underline"};
span.onmouseout = () => {span.style["text-decoration-line"] = ""};
alt_transcript.append(span)
}
}
}
}
})();