Last active
July 17, 2020 18:17
-
-
Save kubarium/9a8243fd4fba8ca8a915b7be0e4084bb to your computer and use it in GitHub Desktop.
A Better Safari Books Online Experience : http://denizkumsal.com/programming/a-better-safari-books-online-experience/
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var course_id = RegExp(/\d+/g).exec(location.href)[0]; | |
var styles = { | |
pagewrapid: { | |
width: "1270px", | |
margin: "0 auto" | |
}, | |
p: { | |
"font-size": "16px !important", | |
"line-height": "1.3rem !important" | |
} | |
}; | |
function rearrange(){ | |
return new Promise((resolve, reject) => { | |
pagewrapid = document.querySelector("#pagewrapid"); | |
pagewrapid.setAttribute("style", stylize("pagewrapid")); | |
p = document.querySelectorAll(".htmlcontent p"); | |
p.forEach(p=>p.setAttribute("style", stylize("p"))); | |
document.querySelector("#readerleft").style.height = document.querySelector("#readerright").style.height; | |
resolve(); | |
}); | |
} | |
function saveBookmark() { | |
new Promise((resolve, reject) => { | |
localStorage.setItem( | |
course_id, | |
document.querySelector(".current").dataset.xmlid | |
); | |
resolve(); | |
}); | |
} | |
function loadBookmark() { | |
var video_id = localStorage.getItem(course_id); | |
if (video_id) { | |
Array.from(document.querySelectorAll("#lefttoc a")) | |
.filter(a => a.dataset.xmlid === video_id)[0] | |
.click(); | |
} | |
} | |
function stylize(id) { | |
return Object.keys(styles[id]) | |
.reduce((cssText, key) => cssText.concat(`${key}:${styles[id][key]};`), []) | |
.join(""); | |
} | |
$(document).ajaxSuccess((event, xhr, settings) => { | |
if (/_ajax_htmlview/.test(settings.url)) { | |
rearrange() | |
.then(saveBookmark); | |
} | |
}); | |
rearrange().then(loadBookmark); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var kdp; | |
var scrubInSeconds = 10; | |
var altModifier = 2; | |
var shiftModifier = 3; | |
var paused = false; | |
var tocWrapperScrollTop = 0; | |
var tocWrapperScrollEventAttached = false; | |
var course_id = RegExp(/\d+/g).exec(location.href)[0]; | |
var styles = { | |
percentage: { | |
position: "absolute", | |
right: "25px", | |
top: "25px", | |
"font-size": "larger", | |
"font-weight": "bold" | |
}, | |
tocWrapper: { | |
overflow: "auto", | |
height: "550px" | |
}, | |
pagewrapid: { | |
width: "1570px", | |
display: "flex", | |
justifyContent: "space-around", | |
border: "1px solid #7990A2", | |
"border-radius": "5px" | |
}, | |
metadata_flashactive: { | |
alignSelf: "flex-start" | |
}, | |
shadowBox1: { | |
"border-radius": 0, | |
padding: 0, | |
margin: 0, | |
border: 0 | |
}, | |
catalog_container: { | |
height: "606px", | |
margin: 0, | |
padding: "25px", | |
border: 0, | |
"border-radius": 0, | |
position: "relative" | |
}, | |
bcv_next: { | |
top: "-12%" | |
}, | |
bcv_previous: { | |
top: "-12%", | |
right: "20px", | |
left: "inherit" | |
} | |
}; | |
var percentage = document.createElement("div"); | |
percentage.setAttribute("style", stylize("percentage")); | |
document.querySelector(".catalog_container").appendChild(percentage); | |
function rearrange() { | |
return new Promise((resolve, reject) => { | |
Array.from(document.querySelector("#pagewrapid").childNodes) | |
.filter( | |
child => | |
child.nodeName !== "INPUT" && | |
child.id !== "metadata_flashactive" && | |
child.className !== "catalog_container" | |
) | |
.forEach(selector => selector.remove()); | |
pagewrapid = document.querySelector("#pagewrapid"); | |
toc = document.querySelector(".videotoc"); | |
tocWrapper = document.createElement("div"); | |
tocWrapper.id = "tocWrapper"; | |
toc.parentNode.appendChild(tocWrapper); | |
if (tocWrapperScrollEventAttached === false) attachTocWrapperScrollEvent(); | |
tocWrapper.appendChild(toc); | |
tocWrapper.setAttribute("style", stylize("tocWrapper")); | |
tocWrapper.scrollTop = tocWrapperScrollTop; | |
pagewrapid.setAttribute("style", stylize("pagewrapid")); | |
document | |
.querySelector("#metadata_flashactive") | |
.setAttribute("style", stylize("metadata_flashactive")); | |
document | |
.querySelector(".shadowBox1") | |
.setAttribute("style", stylize("shadowBox1")); | |
document | |
.querySelector(".catalog_container") | |
.setAttribute("style", stylize("catalog_container")); | |
document | |
.getElementById("bcv_next") | |
.setAttribute("style", stylize("bcv_next")); | |
document | |
.getElementById("bcv_previous") | |
.setAttribute("style", stylize("bcv_previous")); | |
resolve(); | |
}); | |
} | |
function attachTocWrapperScrollEvent() { | |
tocWrapperScrollEventAttached = true; | |
tocWrapper.onscroll = event => (tocWrapperScrollTop = event.target.scrollTop); | |
} | |
function keyEvents(event) { | |
switch (event.key) { | |
case ".": | |
document.getElementById("bcv_next").click(); | |
break; | |
case ",": | |
document.getElementById("bcv_previous").click(); | |
break; | |
case "ArrowUp": | |
event.altKey | |
? sr | |
.increment() | |
.then(speed => | |
kdp.sendNotification("playbackRateChangeSpeed", speed) | |
) | |
: vr | |
.increment() | |
.then(volume => kdp.sendNotification("changeVolume", volume)); | |
break; | |
case "ArrowDown": | |
event.altKey | |
? sr | |
.decrement() | |
.then(speed => | |
kdp.sendNotification("playbackRateChangeSpeed", speed) | |
) | |
: vr | |
.decrement() | |
.then(volume => kdp.sendNotification("changeVolume", volume)); | |
break; | |
case "ArrowRight": | |
adjustTime("forward", event); | |
break; | |
case "ArrowLeft": | |
adjustTime("backward", event); | |
break; | |
case " ": | |
kdp.sendNotification(paused ? "doPlay" : "doPause"); | |
paused = !paused; | |
break; | |
case "0": | |
case "1": | |
case "2": | |
case "3": | |
case "4": | |
case "5": | |
case "6": | |
case "7": | |
case "8": | |
case "9": | |
adjustTime("headshot", event); | |
break; | |
default: | |
break; | |
} | |
} | |
class Range { | |
constructor(value, minimum, maximum, interval) { | |
this.value = value; | |
this.minimum = minimum; | |
this.maximum = maximum; | |
this.interval = interval; | |
} | |
increment() { | |
return new Promise((resolve, reject) => { | |
var newValue = this.value + this.interval; | |
this.value = | |
newValue >= this.maximum ? this.maximum : parseFloat(newValue); | |
resolve(this.value); | |
}); | |
} | |
decrement() { | |
return new Promise((resolve, reject) => { | |
var newValue = this.value - this.interval; | |
this.value = | |
newValue <= this.minimum ? this.minimum : parseFloat(newValue); | |
resolve(this.value); | |
}); | |
} | |
} | |
class VolumeRange extends Range { | |
constructor() { | |
super(1, 0.1, 1, 0.1); | |
} | |
} | |
class SpeedRange extends Range { | |
constructor() { | |
super(1.25, 0.25, 2, 0.25); | |
} | |
} | |
function stylize(id) { | |
return Object.keys(styles[id]) | |
.reduce((cssText, key) => cssText.concat(`${key}:${styles[id][key]};`), []) | |
.join(""); | |
} | |
function adjustTime(transition, event) { | |
var currentTime = kdp.evaluate("{video.player.currentTime}"); | |
var timeAdjustment = | |
scrubInSeconds * | |
(event.altKey ? altModifier : 1) * | |
(event.shiftKey ? shiftModifier : 1); | |
if ( | |
transition === "forward" && | |
currentTime + timeAdjustment < kdp.evaluate("{duration}") | |
) { | |
kdp.sendNotification("doSeek", currentTime + timeAdjustment); | |
} | |
if (transition === "backward" && currentTime - timeAdjustment > 0) { | |
kdp.sendNotification("doSeek", currentTime - timeAdjustment); | |
} | |
if (transition === "headshot") { | |
kdp.sendNotification( | |
"doSeek", | |
parseInt(event.key) / 10 * kdp.evaluate("{duration}") | |
); | |
} | |
} | |
function saveBookmark() { | |
new Promise((resolve, reject) => { | |
localStorage.setItem( | |
course_id, | |
document.querySelector(".clipselected").dataset.clipRef | |
); | |
resolve(); | |
}); | |
} | |
function loadBookmark() { | |
var video_id = localStorage.getItem(course_id); | |
if (video_id) { | |
Array.from(document.querySelectorAll(".videotoc a")) | |
.filter(a => a.dataset.clipRef === video_id)[0] | |
.click(); | |
document.getElementById("tocWrapper").scrollTop = document.querySelector( | |
".clipselected" | |
).offsetParent.offsetTop; | |
} | |
} | |
function calculateWatchedTime() { | |
var totalTime = 0, | |
watchedTime = 0; | |
var selectedClipIndex = Array.from(document.querySelectorAll(".videotoc a")) | |
.filter(a => a.dataset.clipRef) | |
.map(a => a.className) | |
.indexOf("clipselected"); | |
var spans = Array.from(document.querySelectorAll(".videotoc p span")).filter( | |
span => { | |
return span.parentElement.classList[1] === "time" && span.innerText; | |
} | |
); | |
totalTime = collectTime(spans); | |
spans.splice(selectedClipIndex); | |
watchedTime = collectTime(spans); | |
return `${Math.round(watchedTime / totalTime * 100)}%`; | |
} | |
function collectTime(spans) { | |
var hour = 0, | |
min = 0, | |
second = 0; | |
spans.forEach(span => { | |
var chunks = span.innerText.split(":"); | |
hour += parseInt(chunks[0]); | |
min += parseInt(chunks[1]); | |
second += parseInt(chunks[2]); | |
}); | |
return hour * 60 * 60 + min * 60 + second; | |
} | |
function setPercentage() { | |
percentage.innerText = calculateWatchedTime(); | |
} | |
kWidget.addReadyCallback(function(playerId) { | |
kdp = document.getElementById(playerId); | |
kdp.kBind("playerPlayEnd", function(data, id) { | |
document.getElementById("bcv_next").click(); | |
}); | |
}); | |
let vr = new VolumeRange(); | |
let sr = new SpeedRange(); | |
document.addEventListener("keydown", keyEvents); | |
$(document).ajaxSuccess((event, xhr, settings) => { | |
if (/ajaxtoc/.test(settings.url)) { | |
rearrange() | |
.then(saveBookmark) | |
.then(setPercentage); | |
} | |
}); | |
$(document).ajaxSend( | |
() => (tocWrapperScrollTop = document.getElementById("tocWrapper").scrollTop) | |
); | |
rearrange().then(loadBookmark); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
The current version allows you to: