') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })(); Add support for callback graph improvements and timing by rpkyle · Pull Request #224 · plotly/dashR · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line numberDiff line numberDiff line change
Expand Up@@ -39,7 +39,7 @@ License: MIT + file LICENSE
Encoding: UTF-8
LazyData: true
KeepSource: true
RoxygenNote: 7.1.0
RoxygenNote: 7.1.1
Roxygen: list(markdown = TRUE)
URL: https://github.com/plotly/dashR
BugReports: https://github.com/plotly/dashR/issues
99 changes: 91 additions & 8 deletions R/dash.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -386,14 +386,15 @@ Dash <- R6::R6Class(

if (!private$debug && has_fingerprint) {
response$status <- 200L
response$set_header('Cache-Control',
sprintf('public, max-age=%s',
31536000) # 1 year
response$append_header('Cache-Control',
sprintf('public, max-age=%s',
'31536000') # 1 year
)
} else if (!private$debug && !has_fingerprint) {
modified <- as.character(as.integer(file.mtime(dep_path)))

response$set_header('ETag', modified)
response$append_header('ETag',
Comment thread
rpkyle marked this conversation as resolved.
modified)

request_etag <- request$get_header('If-None-Match')

Expand DownExpand Up@@ -480,9 +481,9 @@ Dash <- R6::R6Class(
file.size(asset_path))
close(file_handle)

response$set_header('Cache-Control',
sprintf('public, max-age=%s',
'31536000')
response$append_header('Cache-Control',
sprintf('public, max-age=%s',
'31536000')
Comment thread
alexcjohnson marked this conversation as resolved.
)
response$type <- 'image/x-icon'
response$status <- 200L
Expand DownExpand Up@@ -831,9 +832,46 @@ Dash <- R6::R6Class(
if (is.null(private$callback_context_)) {
warning("callback_context is undefined; callback_context may only be accessed within a callback.")
}

private$callback_context_
},

# ------------------------------------------------------------------------
# request and return callback timing data
# ------------------------------------------------------------------------
#' @description
#' Records timing information for a server resource.
#' @details
#' The `callback_context.record_timing` method permits retrieving the
#' duration required to execute a given callback. It may only be called
#' from within a callback; a warning will be thrown and the method will
#' otherwise return `NULL` if invoked outside of a callback.
#'
#' @param name Character. The name of the resource.
#' @param duration Numeric. The time in seconds to report. Internally, this is
#' rounded to the nearest millisecond.
#' @param description Character. A description of the resource.
#'
callback_context.record_timing = function(name,
duration=NULL,
description=NULL) {
if (is.null(private$callback_context_)) {
warning("callback_context is undefined; callback_context.record_timing may only be accessed within a callback.")
return(NULL)
}

timing_information <- self$server$get_data("timing-information")

if (name %in% timing_information) {
stop(paste0("Duplicate resource name ", name, " found."), call.=FALSE)
}

timing_information[[name]] <- list("dur" = round(duration * 1000),
"desc" = description)

self$server$set_data("timing-information", timing_information)
Comment thread
alexcjohnson marked this conversation as resolved.
},

# ------------------------------------------------------------------------
# return asset URLs
# ------------------------------------------------------------------------
Expand DownExpand Up@@ -1221,6 +1259,42 @@ Dash <- R6::R6Class(
self$config$silence_routes_logging <- dev_tools_silence_routes_logging
self$config$props_check <- dev_tools_props_check

if (private$debug && self$config$ui) {
self$server$on('before-request', function(server, ...) {
self$server$set_data("timing-information", list(
"__dash_server" = list(
"dur" = as.numeric(Sys.time()),
"desc" = NULL
)
))
})

self$server$on('request', function(server, request, ...) {
Comment thread
rpkyle marked this conversation as resolved.
timing_information <- self$server$get_data('timing-information')
dash_total <- timing_information[['__dash_server']]
timing_information[['__dash_server']][['dur']] <- round((as.numeric(Sys.time()) - dash_total[['dur']]) * 1000)

header_as_string <- list()

for (item in seq_along(timing_information)) {
header_content <- names(timing_information[item])

if (!is.null(timing_information[[item]]$desc)) {
header_content <- paste0(header_content, ';desc="', timing_information[[item]]$desc, '"')
}

if (!is.null(timing_information[[item]]$dur)) {
header_content <- paste0(header_content, ';dur=', timing_information[[item]]$dur)
}

header_as_string[[item]] <- header_content
}

request$response$append_header('Server-Timing',
paste0(unlist(header_as_string), collapse=", "))
})
}

if (hot_reload == TRUE & !(is.null(source_dir))) {
self$server$on('cycle-end', function(server, ...) {
# handle case where assets are not present, since we can still hot reload the app itself
Expand DownExpand Up@@ -1327,10 +1401,19 @@ Dash <- R6::R6Class(

# reset the timestamp so we're able to determine when the last cycle end occurred
private$last_cycle <- as.integer(Sys.time())

# flush the context to prepare for the next request cycle
self$server$set_data("timing-information", list())
})
} else if (hot_reload == TRUE & is.null(source_dir)) {
message("\U{26A0} No source directory information available; hot reloading has been disabled.\nPlease ensure that you are loading your Dash for R application using source().\n")
}
} else if (hot_reload == FALSE && private$debug && self$config$ui) {
self$server$on("cycle-end", function(server, ...) {
# flush the context to prepare for the next request cycle
self$server$set_data("timing-information", list())
})
}

self$server$ignite(block = block, showcase = showcase, ...)
}
),
Expand Down
Loading