NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript==
// @name Smartsheet Dashboard Scroller
// @description Create a Kiosk or information radiator out of Smartsheet Dashboards. This script adds a new toolbar button to Smartsheet Dashboards to add an auto-scrolling feature that scrolls to each Title widget and pauses for one minute.
// @icon https://github.com/activescott/browser-grease/raw/master/smartsheet-dashboard-scroller/icon.png
// @homepageURL https://github.com/activescott/browser-grease
// @supportURL https://github.com/activescott/browser-grease/issues
// @license Apache-2.0
// @namespace activescott
// @version 1.2
// @grant none
// @run-at document-idle
// @match https://app.smartsheet.com/*
// ==/UserScript==
Promise.delay = n => new Promise(resolve => setTimeout(resolve, n))
function getDashboardTitleWidgets() {
return Array.from(
document.querySelectorAll(".clsDashboardTitleWidget")
).filter(t => t.innerText)
}
function uniqBy(array, mapper) {
let dict = {}
array.forEach(item => (dict[mapper(item)] = item))
return Object.values(dict)
}
function scrollerThunk() {
let titles = getDashboardTitleWidgets()
console.log(`found ${titles.length} titles`, titles)
titles = uniqBy(titles, title => title.getBoundingClientRect().top)
titles.sort(
(a, b) => a.getBoundingClientRect().top - b.getBoundingClientRect().top
)
console.log(`found ${titles.length} unique titles:`, titles)
let seconds = 60
let result = window.prompt(
"How many seconds should it pause on each title widget?",
seconds.toString()
)
seconds = Number.isNaN(Number.parseFloat(result))
? seconds
: Number.parseFloat(result)
console.log("result seconds:", seconds)
let scroller = async () => {
while (true) {
for (let t of titles) {
if (!window.activeScroller) {
console.log("aborting scroller; No activeScroller.")
return
}
if (getDashboardTitleWidgets().length == 0) {
console.log("aborting scroller; No active title widgets.")
return
}
console.log(
"scrolling to title:",
t.innerText,
t.getBoundingClientRect().top
)
let scrollOptions = {
behavior: "smooth",
block: "start",
inline: "nearest"
}
t.scrollIntoView(scrollOptions)
await Promise.delay(seconds * 1000)
}
}
}
return scroller
}
function toggleScroller() {
const button = document.querySelector("#dashboardScrollerButton")
if (window.activeScroller) {
window.activeScroller = null
button.style.setProperty("border-style", "")
button.style.setProperty("border-width", "1px")
} else {
window.activeScroller = scrollerThunk()
window.activeScroller()
button.style.setProperty("border-style", "inset")
button.style.setProperty("border-width", "3px")
}
}
async function addScrollButton() {
if (document.querySelector("#dashboardScrollerButton")) {
return
}
console.log("addScrollButton waiting for toolbar to init...")
await Promise.delay(2000)
let button = document.createElement("div")
button.setAttribute("id", "dashboardScrollerButton")
button.className =
"clsBorderBox clsButton clsTertiaryLightActionButton clsDashboardToolbarButton"
button.style.cssText =
"position: relative; width: 39px; height: 30px; display: block;"
const buttonContent = document.createElement("div")
buttonContent.className = "clsButtonContent"
buttonContent.style.cssText =
"position: relative; width: 100%; height: 100%; flex-direction: row-reverse;"
const span = document.createElement("span")
span.className = "clsImageRenderingOptimization"
span.style.cssText = "overflow: hidden; font-size: x-large;"
span.innerText = "🎬"
buttonContent.appendChild(span)
button.appendChild(buttonContent)
const toolbar = document.querySelector(
"div.clsDashboardEditorToolbar > div.clsFlexContainer"
)
if (toolbar) {
console.log("Dashboard scroller toolbar exists!")
toolbar.appendChild(button)
button.addEventListener("click", toggleScroller)
} else {
console.log("Dashboard scroller toolbar not found!")
}
}
if (document.readyState === "loading") {
// Loading hasn't finished yet
console.log("Dashboard scroller listening...")
document.addEventListener("DOMContentLoaded", addScrollButton)
} else {
// `DOMContentLoaded` has already fired
addScrollButton()
}