diff --git a/NAMESPACE b/NAMESPACE index 4ed32af5..405057be 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -72,7 +72,9 @@ S3method(input_ids,hidden_field) S3method(input_ids,list_field) S3method(is_initialized,block) S3method(is_initialized,field) +S3method(is_valid,block) S3method(is_valid,field) +S3method(is_valid,stack) S3method(layout,block) S3method(remove_button,block) S3method(remove_button,stack) diff --git a/NEWS.md b/NEWS.md index f6f2e77c..fe6483f6 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,6 +1,7 @@ # blockr 0.0.2.9031 ## Feature +- New validate stack function. - Improved `submit` feature for blocks. Now submit isn't added as a class but as a special block attribute. When you design a block, you can pass the `submit` parameter like so: ```r diff --git a/R/server.R b/R/server.R index 70653199..98414eb5 100644 --- a/R/server.R +++ b/R/server.R @@ -14,13 +14,17 @@ generate_server <- function(x, ...) { #' @rdname generate_server #' @export generate_server.result_field <- function(x, ...) { - function(id, init = NULL, data = NULL) { + function(id, init = NULL, data = NULL, is_link_valid = NULL) { moduleServer(id, function(input, output, session) { - get_result <- function(inp) { - req(inp) - res <- get_stack_result( - get_workspace_stack(inp) - ) + get_result <- function(inp = NULL) { + if (is.null(inp)) { + res <- data.frame() + # Needed by ui_update + attr(res, "result_field_stack_name") <- "" + return(res) + } + stack <- workspace_stacks()[[inp]]$stack + res <- get_stack_result(stack) attr(res, "result_field_stack_name") <- inp @@ -44,13 +48,17 @@ generate_server.result_field <- function(x, ...) { updateSelectInput( session, "select-stack", - choices = result_field_stack_opts(session$ns, workspace_stacks()), + choices = result_field_stack_opts(session$ns, names(workspace_stacks())), selected = input[["select-stack"]] ) ) reactive({ - get_result(input[["select-stack"]]) + if (is_link_valid()) { + get_result(input[["select-stack"]]) + } else { + get_result() + } }) }) } @@ -87,7 +95,7 @@ generate_server_block <- function( in_dat = NULL, id, display = c("table", "plot"), - is_prev_valid) { + is_prev_valid, linked_stack = NULL) { display <- match.arg(display) # if in_dat is NULL (data block), turn it into a reactive expression that @@ -95,6 +103,8 @@ generate_server_block <- function( if (is.null(in_dat)) { in_dat <- reactive(NULL) is_prev_valid <- reactive(NULL) + } else { + linked_stack <- reactive(NULL) } obs_expr <- function(x) { @@ -136,12 +146,16 @@ generate_server_block <- function( l_values_module <- list() # a list with reactive values (module server output) for (name in names(x_srv)) { l_values_module[[name]] <- - generate_server(x_srv[[name]])(name, init = l_init[[name]], data = in_dat) + generate_server(x_srv[[name]])( + name, + init = l_init[[name]], + data = in_dat, + is_link_valid = reactive(linked_stack()$is_valid) + ) } # proceed in standard fashion (if fields have no generate_server) r_values_default <- reactive({ - # if (!is.null(is_prev_valid)) req(is_prev_valid) blk_no_srv <- blk() blk_no_srv[is_srv] <- NULL # to keep class etc eval(obs_expr(blk_no_srv)) @@ -156,7 +170,7 @@ generate_server_block <- function( # This will also trigger when the previous block # valid status changes. - obs$update_blk <- observeEvent(c(r_values(), in_dat(), is_prev_valid()), + obs$update_blk <- observeEvent(c(r_values(), in_dat(), linked_stack()$is_valid), { # 1. upd blk, b <- update_blk( @@ -174,7 +188,7 @@ generate_server_block <- function( log_debug("Updating UI of block ", class(x)[[1]]) # Validating - is_valid$block <- validate_block(blk()) + is_valid$block <- is_valid(blk()) is_valid$message <- attr(is_valid$block, "msg") is_valid$fields <- attr(is_valid$block, "fields") log_debug("Validating block ", class(x)[[1]]) @@ -210,7 +224,7 @@ generate_server_block <- function( out_dat <- if (attr(x, "submit") > -1) { eventReactive(input$submit, { - req(is_valid$block) + if (!is_valid$block) return(data.frame()) if (is.null(in_dat())) { evaluate_block(blk()) } else { @@ -223,7 +237,7 @@ generate_server_block <- function( ) } else { reactive({ - req(is_valid$block) + if (!is_valid$block) return(data.frame()) if (is.null(in_dat()) && !inherits(x, "transform_block")) { evaluate_block(blk()) } else { @@ -291,8 +305,8 @@ generate_server_block <- function( #' @rdname generate_server #' @export -generate_server.data_block <- function(x, id, ...) { - generate_server_block(x = x, in_dat = NULL, id = id, is_prev_valid = NULL) +generate_server.data_block <- function(x, id, linked_stack, ...) { + generate_server_block(x = x, in_dat = NULL, id = id, is_prev_valid = NULL, linked_stack = linked_stack) } #' @param in_dat Reactive input data @@ -322,7 +336,7 @@ generate_server.plot_block <- function(x, in_dat, id, is_prev_valid, ...) { #' @rdname generate_server #' @export generate_server.stack <- function(x, id = NULL, new_block = NULL, - workspace = get_workspace(), ...) { + workspace = get_workspace(), prev_stack, ...) { stopifnot(...length() == 0L) id <- coal(id, get_stack_name(x)) @@ -339,10 +353,14 @@ generate_server.stack <- function(x, id = NULL, new_block = NULL, moduleServer( id = id, function(input, output, session) { + ns <- session$ns + vals <- reactiveValues( stack = x, blocks = vector("list", length(x)), - removed = FALSE + removed = FALSE, + is_valid = NULL, + prev_stack = NULL ) # Don't remove: needed by shinytest2 exportTestValues( @@ -396,19 +414,70 @@ generate_server.stack <- function(x, id = NULL, new_block = NULL, # Any block change: data or input should be sent # up to the stack so we can properly serialise. observeEvent( - c( - get_block_vals(vals$blocks), - get_last_block_data(vals$blocks)() - ), { - vals$stack <- set_stack_blocks( - vals$stack, - get_block_vals(vals$blocks), - get_last_block_data(vals$blocks) + req(length(vals$blocks) > 0) + c( + lapply(vals$blocks, \(block) { + block$is_valid() + }), + get_block_vals(vals$blocks) ) - } + }, + { + # get_last_block_data(vals$blocks)() errors + # if any block is invalid + tryCatch( + { + vals$stack <- set_stack_blocks( + vals$stack, + get_block_vals(vals$blocks), + get_last_block_data(vals$blocks)() + ) + }, + error = function(e) { + vals$stack <- set_stack_blocks( + vals$stack, + get_block_vals(vals$blocks), + list() + ) + } + ) + vals$is_valid <- is_valid(vals$stack) + }, priority = 1000 ) + # stack UI validation message + # We only display which block is invalid + observeEvent(c(vals$is_valid, prev_stack()$is_valid), { + vals$prev_stack <- prev_stack() + removeUI(sprintf("#%s .stack-validation-message", ns(NULL))) + msg <- if (is.null(prev_stack()$is_valid)) { + HTML(paste( + lapply(attr(vals$is_valid, "msgs"), \(msg) { + sprintf("Block %s is invalid", msg) + }), + collapse = ",
" + )) + } else { + if (!prev_stack()$is_valid) { + "Linked stack isn't valid. Please fix upstream errors." + } + } + insertUI( + sprintf("#%s .stack-validation", ns(NULL)), + ui = div( + class = "text-danger text-center stack-validation-message", + msg + ) + ) + + # Disable copy code button + session$sendCustomMessage( + "toggle-copy-code", + list(state = vals$is_valid, id = ns("copy")) + ) + }) + observeEvent(vals$stack, { log_debug("UPDADING WORKSPACE with stack ", id) add_workspace_stack(id, vals$stack, @@ -424,7 +493,7 @@ generate_server.stack <- function(x, id = NULL, new_block = NULL, session$sendCustomMessage( "blockr-render-stack", list( - stack = session$ns(NULL), + stack = ns(NULL), locked = is_locked(session) ) ) @@ -687,7 +756,12 @@ generate_server.workspace <- function(x, id, ...) { vals$stacks[[stack_id]] <- generate_server( el, id = stack_id, - new_block = reactive(vals$new_block[[stack_id]]) + new_block = reactive(vals$new_block[[stack_id]]), + prev_stack = if (length(vals$stacks) == 1) { + reactive(NULL) + } else { + reactive(vals$stacks[[length(vals$stacks) - 1]]) + } ) # Handle new block injection @@ -702,9 +776,16 @@ generate_server.workspace <- function(x, id, ...) { }) attr(x, "reactive_stack_directory") <- reactive({ - names(vals$stacks) + vals$stacks }) |> bindEvent( - chr_ply(lapply(vals$stacks, `[[`, "stack"), attr, "title") + c( + chr_ply(lapply(vals$stacks, `[[`, "stack"), attr, "title"), + lapply(vals$stacks, \(stack) { + lgl_ply(stack$blocks, \(block) { + block$is_valid() + }) + }) + ) ) # Serialize @@ -741,10 +822,16 @@ init.workspace <- function(x, vals, session, ...) { observeEvent(TRUE, { lapply(names(stacks), \(nme) { + idx <- which(nme == names(stacks)) vals$stacks[[nme]] <- generate_server( stacks[[nme]], id = nme, - new_block = reactive(vals$new_block[[nme]]) + new_block = reactive(vals$new_block[[nme]]), + prev_stack = if (idx == 1) { + reactive(NULL) + } else { + reactive(vals$stacks[[nme]]) + } ) }) }) @@ -813,6 +900,11 @@ init_block <- function(i, vals) { NULL } else { vals$blocks[[i - 1]]$is_valid + }, + linked_stack = if (i == 1) { + reactive(vals$prev_stack) + } else { + NULL } ) } diff --git a/R/ui.R b/R/ui.R index 1f29a74f..e6d00419 100644 --- a/R/ui.R +++ b/R/ui.R @@ -387,6 +387,7 @@ generate_ui.stack <- function(x, id = NULL, ...) { class = "card stack border", id = id, stack_header(x, id, ns), + div(class = "stack-validation"), div( class = "card-body p-1", id = sprintf("%s-body", id), diff --git a/R/utils.R b/R/utils.R index 6fb7e01a..68a0887d 100644 --- a/R/utils.R +++ b/R/utils.R @@ -259,7 +259,7 @@ send_error_to_ui <- function(blk, is_valid, session) { ) # Toggle submit field - if (!is.null(attr(blk, "submit"))) { + if (attr(blk, "submit") > -1) { session$sendCustomMessage( "toggle-submit", list(state = is_valid$block, id = ns("submit")) diff --git a/R/validation.R b/R/validation.R index 3b125cc9..1ca3e88c 100644 --- a/R/validation.R +++ b/R/validation.R @@ -43,9 +43,11 @@ is_valid.field <- function(x) { ) } -validate_block <- function(x) { - tmp <- lapply(names(x), \(name) { - is_valid(x[[name]]) +#' @rdname validate_field +#' @export +is_valid.block <- function(x) { + tmp <- lapply(x, \(field) { + is_valid(field) }) structure( all(unlist(tmp) == TRUE), @@ -54,6 +56,18 @@ validate_block <- function(x) { ) } +#' @rdname validate_field +#' @export +is_valid.stack <- function(x) { + tmp <- lapply(x, \(block) { + is_valid(block) + }) + structure( + all(unlist(tmp) == TRUE), + msgs = which(tmp == FALSE) # captures invalid blocks + ) +} + #' @param ... Message components (forwarded to [paste0()]) #' @param class Condition class (will be a subclass of `validation_failure`) #' @rdname validate_field diff --git a/inst/assets/index.js b/inst/assets/index.js index e18aee9e..8d0c0574 100644 --- a/inst/assets/index.js +++ b/inst/assets/index.js @@ -1 +1 @@ -!function(e,n){"object"==typeof exports&&"object"==typeof module?module.exports=n(require("Shiny")):"function"==typeof define&&define.amd?define(["Shiny"],n):"object"==typeof exports?exports.blockr=n(require("Shiny")):e.blockr=n(e.Shiny)}(self,(e=>(()=>{var n={826:()=>{window.Shiny.addCustomMessageHandler("blockr-copy-code",(e=>{var n;e.code?(n=e.code.map((e=>e.trim())).join("\n\t"),navigator.clipboard.writeText(n),window.Shiny.notifications.show({html:"Code copied to clipboard",type:"message"})):window.Shiny.notifications.show({html:"Failed to copy code to clipboard",type:"error"})}))},603:()=>{$((()=>{$("body").on("click",".stack-title-display",(e=>{const n=$(e.target).closest(".stack-title");n.find(".stack-title-display").addClass("d-none"),n.find(".stack-title-input").removeClass("d-none"),n.find(".stack-title-input").find("input").focus()})),$("body").on("click",".stack-title-save",(e=>{const n=$(e.target).closest(".input-group").closest(".stack-title"),t=n.find(".stack-title-input").find("input").val();""!==t?(n.find(".stack-title-display").text(t),n.find(".stack-title-input").addClass("d-none"),n.find(".stack-title-display").removeClass("d-none")):window.Shiny.notifications.show({html:"Must set a title",type:"error"})})),$("body").on("keydown",".stack-title-input",(e=>{if("Enter"!==e.key)return;const n=$(e.target).closest(".stack-title"),t=$(e.target).val();""!==t?(n.find(".stack-title-display").text(t),n.find(".stack-title-display").removeClass("d-none"),n.find(".stack-title-input").addClass("d-none")):window.Shiny.notifications.show({html:"Must set a title",type:"error"})}))}))},294:()=>{$((()=>{e()}));const e=()=>{[...document.querySelectorAll('[data-bs-toggle="tooltip"]')].map((e=>new window.bootstrap.Tooltip(e)))}},230:n=>{"use strict";n.exports=e},390:e=>{function n(e){return e instanceof Map?e.clear=e.delete=e.set=function(){throw new Error("map is read-only")}:e instanceof Set&&(e.add=e.clear=e.delete=function(){throw new Error("set is read-only")}),Object.freeze(e),Object.getOwnPropertyNames(e).forEach((t=>{const o=e[t],i=typeof o;"object"!==i&&"function"!==i||Object.isFrozen(o)||n(o)})),e}class t{constructor(e){void 0===e.data&&(e.data={}),this.data=e.data,this.isMatchIgnored=!1}ignoreMatch(){this.isMatchIgnored=!0}}function o(e){return e.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'")}function i(e,...n){const t=Object.create(null);for(const n in e)t[n]=e[n];return n.forEach((function(e){for(const n in e)t[n]=e[n]})),t}const s=e=>!!e.scope;class a{constructor(e,n){this.buffer="",this.classPrefix=n.classPrefix,e.walk(this)}addText(e){this.buffer+=o(e)}openNode(e){if(!s(e))return;const n=((e,{prefix:n})=>{if(e.startsWith("language:"))return e.replace("language:","language-");if(e.includes(".")){const t=e.split(".");return[`${n}${t.shift()}`,...t.map(((e,n)=>`${e}${"_".repeat(n+1)}`))].join(" ")}return`${n}${e}`})(e.scope,{prefix:this.classPrefix});this.span(n)}closeNode(e){s(e)&&(this.buffer+="")}value(){return this.buffer}span(e){this.buffer+=``}}const r=(e={})=>{const n={children:[]};return Object.assign(n,e),n};class c{constructor(){this.rootNode=r(),this.stack=[this.rootNode]}get top(){return this.stack[this.stack.length-1]}get root(){return this.rootNode}add(e){this.top.children.push(e)}openNode(e){const n=r({scope:e});this.add(n),this.stack.push(n)}closeNode(){if(this.stack.length>1)return this.stack.pop()}closeAllNodes(){for(;this.closeNode(););}toJSON(){return JSON.stringify(this.rootNode,null,4)}walk(e){return this.constructor._walk(e,this.rootNode)}static _walk(e,n){return"string"==typeof n?e.addText(n):n.children&&(e.openNode(n),n.children.forEach((n=>this._walk(e,n))),e.closeNode(n)),e}static _collapse(e){"string"!=typeof e&&e.children&&(e.children.every((e=>"string"==typeof e))?e.children=[e.children.join("")]:e.children.forEach((e=>{c._collapse(e)})))}}class l extends c{constructor(e){super(),this.options=e}addText(e){""!==e&&this.add(e)}startScope(e){this.openNode(e)}endScope(){this.closeNode()}__addSublanguage(e,n){const t=e.root;n&&(t.scope=`language:${n}`),this.add(t)}toHTML(){return new a(this,this.options).value()}finalize(){return this.closeAllNodes(),!0}}function d(e){return e?"string"==typeof e?e:e.source:null}function u(e){return f("(?=",e,")")}function g(e){return f("(?:",e,")*")}function h(e){return f("(?:",e,")?")}function f(...e){return e.map((e=>d(e))).join("")}function p(...e){const n=function(e){const n=e[e.length-1];return"object"==typeof n&&n.constructor===Object?(e.splice(e.length-1,1),n):{}}(e);return"("+(n.capture?"":"?:")+e.map((e=>d(e))).join("|")+")"}function b(e){return new RegExp(e.toString()+"|").exec("").length-1}const m=/\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./;function k(e,{joinWith:n}){let t=0;return e.map((e=>{t+=1;const n=t;let o=d(e),i="";for(;o.length>0;){const e=m.exec(o);if(!e){i+=o;break}i+=o.substring(0,e.index),o=o.substring(e.index+e[0].length),"\\"===e[0][0]&&e[1]?i+="\\"+String(Number(e[1])+n):(i+=e[0],"("===e[0]&&t++)}return i})).map((e=>`(${e})`)).join(n)}const w="[a-zA-Z]\\w*",y="[a-zA-Z_]\\w*",E="\\b\\d+(\\.\\d+)?",v="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",x="\\b(0b[01]+)",_={begin:"\\\\[\\s\\S]",relevance:0},S={scope:"string",begin:"'",end:"'",illegal:"\\n",contains:[_]},C={scope:"string",begin:'"',end:'"',illegal:"\\n",contains:[_]},$=function(e,n,t={}){const o=i({scope:"comment",begin:e,end:n,contains:[]},t);o.contains.push({scope:"doctag",begin:"[ ]*(?=(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):)",end:/(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):/,excludeBegin:!0,relevance:0});const s=p("I","a","is","so","us","to","at","if","in","it","on",/[A-Za-z]+['](d|ve|re|ll|t|s|n)/,/[A-Za-z]+[-][a-z]+/,/[A-Za-z][a-z]{2,}/);return o.contains.push({begin:f(/[ ]+/,"(",s,/[.]?[:]?([.][ ]|[ ])/,"){3}")}),o},A=$("//","$"),N=$("/\\*","\\*/"),M=$("#","$"),O={scope:"number",begin:E,relevance:0},R={scope:"number",begin:v,relevance:0},T={scope:"number",begin:x,relevance:0},j={scope:"regexp",begin:/\/(?=[^/\n]*\/)/,end:/\/[gimuy]*/,contains:[_,{begin:/\[/,end:/\]/,relevance:0,contains:[_]}]},I={scope:"title",begin:w,relevance:0},L={scope:"title",begin:y,relevance:0},B={begin:"\\.\\s*"+y,relevance:0};var H=Object.freeze({__proto__:null,APOS_STRING_MODE:S,BACKSLASH_ESCAPE:_,BINARY_NUMBER_MODE:T,BINARY_NUMBER_RE:x,COMMENT:$,C_BLOCK_COMMENT_MODE:N,C_LINE_COMMENT_MODE:A,C_NUMBER_MODE:R,C_NUMBER_RE:v,END_SAME_AS_BEGIN:function(e){return Object.assign(e,{"on:begin":(e,n)=>{n.data._beginMatch=e[1]},"on:end":(e,n)=>{n.data._beginMatch!==e[1]&&n.ignoreMatch()}})},HASH_COMMENT_MODE:M,IDENT_RE:w,MATCH_NOTHING_RE:/\b\B/,METHOD_GUARD:B,NUMBER_MODE:O,NUMBER_RE:E,PHRASAL_WORDS_MODE:{begin:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/},QUOTE_STRING_MODE:C,REGEXP_MODE:j,RE_STARTERS_RE:"!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",SHEBANG:(e={})=>{const n=/^#![ ]*\//;return e.binary&&(e.begin=f(n,/.*\b/,e.binary,/\b.*/)),i({scope:"meta",begin:n,end:/$/,relevance:0,"on:begin":(e,n)=>{0!==e.index&&n.ignoreMatch()}},e)},TITLE_MODE:I,UNDERSCORE_IDENT_RE:y,UNDERSCORE_TITLE_MODE:L});function P(e,n){"."===e.input[e.index-1]&&n.ignoreMatch()}function D(e,n){void 0!==e.className&&(e.scope=e.className,delete e.className)}function z(e,n){n&&e.beginKeywords&&(e.begin="\\b("+e.beginKeywords.split(" ").join("|")+")(?!\\.)(?=\\b|\\s)",e.__beforeBegin=P,e.keywords=e.keywords||e.beginKeywords,delete e.beginKeywords,void 0===e.relevance&&(e.relevance=0))}function U(e,n){Array.isArray(e.illegal)&&(e.illegal=p(...e.illegal))}function G(e,n){if(e.match){if(e.begin||e.end)throw new Error("begin & end are not supported with match");e.begin=e.match,delete e.match}}function Z(e,n){void 0===e.relevance&&(e.relevance=1)}const F=(e,n)=>{if(!e.beforeMatch)return;if(e.starts)throw new Error("beforeMatch cannot be used with starts");const t=Object.assign({},e);Object.keys(e).forEach((n=>{delete e[n]})),e.keywords=t.keywords,e.begin=f(t.beforeMatch,u(t.begin)),e.starts={relevance:0,contains:[Object.assign(t,{endsParent:!0})]},e.relevance=0,delete t.beforeMatch},W=["of","and","for","in","not","or","if","then","parent","list","value"],X="keyword";function q(e,n,t=X){const o=Object.create(null);return"string"==typeof e?i(t,e.split(" ")):Array.isArray(e)?i(t,e):Object.keys(e).forEach((function(t){Object.assign(o,q(e[t],n,t))})),o;function i(e,t){n&&(t=t.map((e=>e.toLowerCase()))),t.forEach((function(n){const t=n.split("|");o[t[0]]=[e,K(t[0],t[1])]}))}}function K(e,n){return n?Number(n):function(e){return W.includes(e.toLowerCase())}(e)?0:1}const V={},J=e=>{console.error(e)},Y=(e,...n)=>{console.log(`WARN: ${e}`,...n)},Q=(e,n)=>{V[`${e}/${n}`]||(console.log(`Deprecated as of ${e}. ${n}`),V[`${e}/${n}`]=!0)},ee=new Error;function ne(e,n,{key:t}){let o=0;const i=e[t],s={},a={};for(let e=1;e<=n.length;e++)a[e+o]=i[e],s[e+o]=!0,o+=b(n[e-1]);e[t]=a,e[t]._emit=s,e[t]._multi=!0}function te(e){!function(e){e.scope&&"object"==typeof e.scope&&null!==e.scope&&(e.beginScope=e.scope,delete e.scope)}(e),"string"==typeof e.beginScope&&(e.beginScope={_wrap:e.beginScope}),"string"==typeof e.endScope&&(e.endScope={_wrap:e.endScope}),function(e){if(Array.isArray(e.begin)){if(e.skip||e.excludeBegin||e.returnBegin)throw J("skip, excludeBegin, returnBegin not compatible with beginScope: {}"),ee;if("object"!=typeof e.beginScope||null===e.beginScope)throw J("beginScope must be object"),ee;ne(e,e.begin,{key:"beginScope"}),e.begin=k(e.begin,{joinWith:""})}}(e),function(e){if(Array.isArray(e.end)){if(e.skip||e.excludeEnd||e.returnEnd)throw J("skip, excludeEnd, returnEnd not compatible with endScope: {}"),ee;if("object"!=typeof e.endScope||null===e.endScope)throw J("endScope must be object"),ee;ne(e,e.end,{key:"endScope"}),e.end=k(e.end,{joinWith:""})}}(e)}function oe(e){function n(n,t){return new RegExp(d(n),"m"+(e.case_insensitive?"i":"")+(e.unicodeRegex?"u":"")+(t?"g":""))}class t{constructor(){this.matchIndexes={},this.regexes=[],this.matchAt=1,this.position=0}addRule(e,n){n.position=this.position++,this.matchIndexes[this.matchAt]=n,this.regexes.push([n,e]),this.matchAt+=b(e)+1}compile(){0===this.regexes.length&&(this.exec=()=>null);const e=this.regexes.map((e=>e[1]));this.matcherRe=n(k(e,{joinWith:"|"}),!0),this.lastIndex=0}exec(e){this.matcherRe.lastIndex=this.lastIndex;const n=this.matcherRe.exec(e);if(!n)return null;const t=n.findIndex(((e,n)=>n>0&&void 0!==e)),o=this.matchIndexes[t];return n.splice(0,t),Object.assign(n,o)}}class o{constructor(){this.rules=[],this.multiRegexes=[],this.count=0,this.lastIndex=0,this.regexIndex=0}getMatcher(e){if(this.multiRegexes[e])return this.multiRegexes[e];const n=new t;return this.rules.slice(e).forEach((([e,t])=>n.addRule(e,t))),n.compile(),this.multiRegexes[e]=n,n}resumingScanAtSamePosition(){return 0!==this.regexIndex}considerAll(){this.regexIndex=0}addRule(e,n){this.rules.push([e,n]),"begin"===n.type&&this.count++}exec(e){const n=this.getMatcher(this.regexIndex);n.lastIndex=this.lastIndex;let t=n.exec(e);if(this.resumingScanAtSamePosition())if(t&&t.index===this.lastIndex);else{const n=this.getMatcher(0);n.lastIndex=this.lastIndex+1,t=n.exec(e)}return t&&(this.regexIndex+=t.position+1,this.regexIndex===this.count&&this.considerAll()),t}}if(e.compilerExtensions||(e.compilerExtensions=[]),e.contains&&e.contains.includes("self"))throw new Error("ERR: contains `self` is not supported at the top-level of a language. See documentation.");return e.classNameAliases=i(e.classNameAliases||{}),function t(s,a){const r=s;if(s.isCompiled)return r;[D,G,te,F].forEach((e=>e(s,a))),e.compilerExtensions.forEach((e=>e(s,a))),s.__beforeBegin=null,[z,U,Z].forEach((e=>e(s,a))),s.isCompiled=!0;let c=null;return"object"==typeof s.keywords&&s.keywords.$pattern&&(s.keywords=Object.assign({},s.keywords),c=s.keywords.$pattern,delete s.keywords.$pattern),c=c||/\w+/,s.keywords&&(s.keywords=q(s.keywords,e.case_insensitive)),r.keywordPatternRe=n(c,!0),a&&(s.begin||(s.begin=/\B|\b/),r.beginRe=n(r.begin),s.end||s.endsWithParent||(s.end=/\B|\b/),s.end&&(r.endRe=n(r.end)),r.terminatorEnd=d(r.end)||"",s.endsWithParent&&a.terminatorEnd&&(r.terminatorEnd+=(s.end?"|":"")+a.terminatorEnd)),s.illegal&&(r.illegalRe=n(s.illegal)),s.contains||(s.contains=[]),s.contains=[].concat(...s.contains.map((function(e){return function(e){return e.variants&&!e.cachedVariants&&(e.cachedVariants=e.variants.map((function(n){return i(e,{variants:null},n)}))),e.cachedVariants?e.cachedVariants:ie(e)?i(e,{starts:e.starts?i(e.starts):null}):Object.isFrozen(e)?i(e):e}("self"===e?s:e)}))),s.contains.forEach((function(e){t(e,r)})),s.starts&&t(s.starts,a),r.matcher=function(e){const n=new o;return e.contains.forEach((e=>n.addRule(e.begin,{rule:e,type:"begin"}))),e.terminatorEnd&&n.addRule(e.terminatorEnd,{type:"end"}),e.illegal&&n.addRule(e.illegal,{type:"illegal"}),n}(r),r}(e)}function ie(e){return!!e&&(e.endsWithParent||ie(e.starts))}class se extends Error{constructor(e,n){super(e),this.name="HTMLInjectionError",this.html=n}}const ae=o,re=i,ce=Symbol("nomatch"),le=function(e){const o=Object.create(null),i=Object.create(null),s=[];let a=!0;const r="Could not find the language '{}', did you forget to load/include a language module?",c={disableAutodetect:!0,name:"Plain text",contains:[]};let d={ignoreUnescapedHTML:!1,throwUnescapedHTML:!1,noHighlightRe:/^(no-?highlight)$/i,languageDetectRe:/\blang(?:uage)?-([\w-]+)\b/i,classPrefix:"hljs-",cssSelector:"pre code",languages:null,__emitter:l};function b(e){return d.noHighlightRe.test(e)}function m(e,n,t){let o="",i="";"object"==typeof n?(o=e,t=n.ignoreIllegals,i=n.language):(Q("10.7.0","highlight(lang, code, ...args) has been deprecated."),Q("10.7.0","Please use highlight(code, options) instead.\nhttps://github.com/highlightjs/highlight.js/issues/2277"),i=e,o=n),void 0===t&&(t=!0);const s={code:o,language:i};C("before:highlight",s);const a=s.result?s.result:k(s.language,s.code,t);return a.code=s.code,C("after:highlight",a),a}function k(e,n,i,s){const c=Object.create(null);function l(){if(!C.keywords)return void A.addText(N);let e=0;C.keywordPatternRe.lastIndex=0;let n=C.keywordPatternRe.exec(N),t="";for(;n;){t+=N.substring(e,n.index);const i=v.case_insensitive?n[0].toLowerCase():n[0],s=(o=i,C.keywords[o]);if(s){const[e,o]=s;if(A.addText(t),t="",c[i]=(c[i]||0)+1,c[i]<=7&&(M+=o),e.startsWith("_"))t+=n[0];else{const t=v.classNameAliases[e]||e;g(n[0],t)}}else t+=n[0];e=C.keywordPatternRe.lastIndex,n=C.keywordPatternRe.exec(N)}var o;t+=N.substring(e),A.addText(t)}function u(){null!=C.subLanguage?function(){if(""===N)return;let e=null;if("string"==typeof C.subLanguage){if(!o[C.subLanguage])return void A.addText(N);e=k(C.subLanguage,N,!0,$[C.subLanguage]),$[C.subLanguage]=e._top}else e=w(N,C.subLanguage.length?C.subLanguage:null);C.relevance>0&&(M+=e.relevance),A.__addSublanguage(e._emitter,e.language)}():l(),N=""}function g(e,n){""!==e&&(A.startScope(n),A.addText(e),A.endScope())}function h(e,n){let t=1;const o=n.length-1;for(;t<=o;){if(!e._emit[t]){t++;continue}const o=v.classNameAliases[e[t]]||e[t],i=n[t];o?g(i,o):(N=i,l(),N=""),t++}}function f(e,n){return e.scope&&"string"==typeof e.scope&&A.openNode(v.classNameAliases[e.scope]||e.scope),e.beginScope&&(e.beginScope._wrap?(g(N,v.classNameAliases[e.beginScope._wrap]||e.beginScope._wrap),N=""):e.beginScope._multi&&(h(e.beginScope,n),N="")),C=Object.create(e,{parent:{value:C}}),C}function p(e,n,o){let i=function(e,n){const t=e&&e.exec(n);return t&&0===t.index}(e.endRe,o);if(i){if(e["on:end"]){const o=new t(e);e["on:end"](n,o),o.isMatchIgnored&&(i=!1)}if(i){for(;e.endsParent&&e.parent;)e=e.parent;return e}}if(e.endsWithParent)return p(e.parent,n,o)}function b(e){return 0===C.matcher.regexIndex?(N+=e[0],1):(T=!0,0)}function m(e){const t=e[0],o=n.substring(e.index),i=p(C,e,o);if(!i)return ce;const s=C;C.endScope&&C.endScope._wrap?(u(),g(t,C.endScope._wrap)):C.endScope&&C.endScope._multi?(u(),h(C.endScope,e)):s.skip?N+=t:(s.returnEnd||s.excludeEnd||(N+=t),u(),s.excludeEnd&&(N=t));do{C.scope&&A.closeNode(),C.skip||C.subLanguage||(M+=C.relevance),C=C.parent}while(C!==i.parent);return i.starts&&f(i.starts,e),s.returnEnd?0:t.length}let y={};function E(o,s){const r=s&&s[0];if(N+=o,null==r)return u(),0;if("begin"===y.type&&"end"===s.type&&y.index===s.index&&""===r){if(N+=n.slice(s.index,s.index+1),!a){const n=new Error(`0 width match regex (${e})`);throw n.languageName=e,n.badRule=y.rule,n}return 1}if(y=s,"begin"===s.type)return function(e){const n=e[0],o=e.rule,i=new t(o),s=[o.__beforeBegin,o["on:begin"]];for(const t of s)if(t&&(t(e,i),i.isMatchIgnored))return b(n);return o.skip?N+=n:(o.excludeBegin&&(N+=n),u(),o.returnBegin||o.excludeBegin||(N=n)),f(o,e),o.returnBegin?0:n.length}(s);if("illegal"===s.type&&!i){const e=new Error('Illegal lexeme "'+r+'" for mode "'+(C.scope||"")+'"');throw e.mode=C,e}if("end"===s.type){const e=m(s);if(e!==ce)return e}if("illegal"===s.type&&""===r)return 1;if(R>1e5&&R>3*s.index)throw new Error("potential infinite loop, way more iterations than matches");return N+=r,r.length}const v=x(e);if(!v)throw J(r.replace("{}",e)),new Error('Unknown language: "'+e+'"');const _=oe(v);let S="",C=s||_;const $={},A=new d.__emitter(d);!function(){const e=[];for(let n=C;n!==v;n=n.parent)n.scope&&e.unshift(n.scope);e.forEach((e=>A.openNode(e)))}();let N="",M=0,O=0,R=0,T=!1;try{if(v.__emitTokens)v.__emitTokens(n,A);else{for(C.matcher.considerAll();;){R++,T?T=!1:C.matcher.considerAll(),C.matcher.lastIndex=O;const e=C.matcher.exec(n);if(!e)break;const t=E(n.substring(O,e.index),e);O=e.index+t}E(n.substring(O))}return A.finalize(),S=A.toHTML(),{language:e,value:S,relevance:M,illegal:!1,_emitter:A,_top:C}}catch(t){if(t.message&&t.message.includes("Illegal"))return{language:e,value:ae(n),illegal:!0,relevance:0,_illegalBy:{message:t.message,index:O,context:n.slice(O-100,O+100),mode:t.mode,resultSoFar:S},_emitter:A};if(a)return{language:e,value:ae(n),illegal:!1,relevance:0,errorRaised:t,_emitter:A,_top:C};throw t}}function w(e,n){n=n||d.languages||Object.keys(o);const t=function(e){const n={value:ae(e),illegal:!1,relevance:0,_top:c,_emitter:new d.__emitter(d)};return n._emitter.addText(e),n}(e),i=n.filter(x).filter(S).map((n=>k(n,e,!1)));i.unshift(t);const s=i.sort(((e,n)=>{if(e.relevance!==n.relevance)return n.relevance-e.relevance;if(e.language&&n.language){if(x(e.language).supersetOf===n.language)return 1;if(x(n.language).supersetOf===e.language)return-1}return 0})),[a,r]=s,l=a;return l.secondBest=r,l}function y(e){let n=null;const t=function(e){let n=e.className+" ";n+=e.parentNode?e.parentNode.className:"";const t=d.languageDetectRe.exec(n);if(t){const n=x(t[1]);return n||(Y(r.replace("{}",t[1])),Y("Falling back to no-highlight mode for this block.",e)),n?t[1]:"no-highlight"}return n.split(/\s+/).find((e=>b(e)||x(e)))}(e);if(b(t))return;if(C("before:highlightElement",{el:e,language:t}),e.dataset.highlighted)return void console.log("Element previously highlighted. To highlight again, first unset `dataset.highlighted`.",e);if(e.children.length>0&&(d.ignoreUnescapedHTML||(console.warn("One of your code blocks includes unescaped HTML. This is a potentially serious security risk."),console.warn("https://github.com/highlightjs/highlight.js/wiki/security"),console.warn("The element with unescaped HTML:"),console.warn(e)),d.throwUnescapedHTML))throw new se("One of your code blocks includes unescaped HTML.",e.innerHTML);n=e;const o=n.textContent,s=t?m(o,{language:t,ignoreIllegals:!0}):w(o);e.innerHTML=s.value,e.dataset.highlighted="yes",function(e,n,t){const o=n&&i[n]||t;e.classList.add("hljs"),e.classList.add(`language-${o}`)}(e,t,s.language),e.result={language:s.language,re:s.relevance,relevance:s.relevance},s.secondBest&&(e.secondBest={language:s.secondBest.language,relevance:s.secondBest.relevance}),C("after:highlightElement",{el:e,result:s,text:o})}let E=!1;function v(){"loading"!==document.readyState?document.querySelectorAll(d.cssSelector).forEach(y):E=!0}function x(e){return e=(e||"").toLowerCase(),o[e]||o[i[e]]}function _(e,{languageName:n}){"string"==typeof e&&(e=[e]),e.forEach((e=>{i[e.toLowerCase()]=n}))}function S(e){const n=x(e);return n&&!n.disableAutodetect}function C(e,n){const t=e;s.forEach((function(e){e[t]&&e[t](n)}))}"undefined"!=typeof window&&window.addEventListener&&window.addEventListener("DOMContentLoaded",(function(){E&&v()}),!1),Object.assign(e,{highlight:m,highlightAuto:w,highlightAll:v,highlightElement:y,highlightBlock:function(e){return Q("10.7.0","highlightBlock will be removed entirely in v12.0"),Q("10.7.0","Please use highlightElement now."),y(e)},configure:function(e){d=re(d,e)},initHighlighting:()=>{v(),Q("10.6.0","initHighlighting() deprecated. Use highlightAll() now.")},initHighlightingOnLoad:function(){v(),Q("10.6.0","initHighlightingOnLoad() deprecated. Use highlightAll() now.")},registerLanguage:function(n,t){let i=null;try{i=t(e)}catch(e){if(J("Language definition for '{}' could not be registered.".replace("{}",n)),!a)throw e;J(e),i=c}i.name||(i.name=n),o[n]=i,i.rawDefinition=t.bind(null,e),i.aliases&&_(i.aliases,{languageName:n})},unregisterLanguage:function(e){delete o[e];for(const n of Object.keys(i))i[n]===e&&delete i[n]},listLanguages:function(){return Object.keys(o)},getLanguage:x,registerAliases:_,autoDetection:S,inherit:re,addPlugin:function(e){!function(e){e["before:highlightBlock"]&&!e["before:highlightElement"]&&(e["before:highlightElement"]=n=>{e["before:highlightBlock"](Object.assign({block:n.el},n))}),e["after:highlightBlock"]&&!e["after:highlightElement"]&&(e["after:highlightElement"]=n=>{e["after:highlightBlock"](Object.assign({block:n.el},n))})}(e),s.push(e)},removePlugin:function(e){const n=s.indexOf(e);-1!==n&&s.splice(n,1)}}),e.debugMode=function(){a=!1},e.safeMode=function(){a=!0},e.versionString="11.9.0",e.regex={concat:f,lookahead:u,either:p,optional:h,anyNumberOfTimes:g};for(const e in H)"object"==typeof H[e]&&n(H[e]);return Object.assign(e,H),e},de=le({});de.newInstance=()=>le({}),e.exports=de,de.HighlightJS=de,de.default=de}},t={};function o(e){var i=t[e];if(void 0!==i)return i.exports;var s=t[e]={exports:{}};return n[e](s,s.exports,o),s.exports}o.d=(e,n)=>{for(var t in n)o.o(n,t)&&!o.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:n[t]})},o.o=(e,n)=>Object.prototype.hasOwnProperty.call(e,n),o.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})};var i={};return(()=>{"use strict";o.r(i),o.d(i,{isLocked:()=>r}),o(230),o(826);const e=o(390);e.registerLanguage("r",(function(e){const n=e.regex,t=/(?:(?:[a-zA-Z]|\.[._a-zA-Z])[._a-zA-Z0-9]*)|\.(?!\d)/,o=n.either(/0[xX][0-9a-fA-F]+\.[0-9a-fA-F]*[pP][+-]?\d+i?/,/0[xX][0-9a-fA-F]+(?:[pP][+-]?\d+)?[Li]?/,/(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?[Li]?/),i=/[=!<>:]=|\|\||&&|:::?|<-|<<-|->>|->|\|>|[-+*\/?!$&|:<=>@^~]|\*\*/,s=n.either(/[()]/,/[{}]/,/\[\[/,/[[\]]/,/\\/,/,/);return{name:"R",keywords:{$pattern:t,keyword:"function if in break next repeat else for while",literal:"NULL NA TRUE FALSE Inf NaN NA_integer_|10 NA_real_|10 NA_character_|10 NA_complex_|10",built_in:"LETTERS letters month.abb month.name pi T F abs acos acosh all any anyNA Arg as.call as.character as.complex as.double as.environment as.integer as.logical as.null.default as.numeric as.raw asin asinh atan atanh attr attributes baseenv browser c call ceiling class Conj cos cosh cospi cummax cummin cumprod cumsum digamma dim dimnames emptyenv exp expression floor forceAndCall gamma gc.time globalenv Im interactive invisible is.array is.atomic is.call is.character is.complex is.double is.environment is.expression is.finite is.function is.infinite is.integer is.language is.list is.logical is.matrix is.na is.name is.nan is.null is.numeric is.object is.pairlist is.raw is.recursive is.single is.symbol lazyLoadDBfetch length lgamma list log max min missing Mod names nargs nzchar oldClass on.exit pos.to.env proc.time prod quote range Re rep retracemem return round seq_along seq_len seq.int sign signif sin sinh sinpi sqrt standardGeneric substitute sum switch tan tanh tanpi tracemem trigamma trunc unclass untracemem UseMethod xtfrm"},contains:[e.COMMENT(/#'/,/$/,{contains:[{scope:"doctag",match:/@examples/,starts:{end:n.lookahead(n.either(/\n^#'\s*(?=@[a-zA-Z]+)/,/\n^(?!#')/)),endsParent:!0}},{scope:"doctag",begin:"@param",end:/$/,contains:[{scope:"variable",variants:[{match:t},{match:/`(?:\\.|[^`\\])+`/}],endsParent:!0}]},{scope:"doctag",match:/@[a-zA-Z]+/},{scope:"keyword",match:/\\[a-zA-Z]+/}]}),e.HASH_COMMENT_MODE,{scope:"string",contains:[e.BACKSLASH_ESCAPE],variants:[e.END_SAME_AS_BEGIN({begin:/[rR]"(-*)\(/,end:/\)(-*)"/}),e.END_SAME_AS_BEGIN({begin:/[rR]"(-*)\{/,end:/\}(-*)"/}),e.END_SAME_AS_BEGIN({begin:/[rR]"(-*)\[/,end:/\](-*)"/}),e.END_SAME_AS_BEGIN({begin:/[rR]'(-*)\(/,end:/\)(-*)'/}),e.END_SAME_AS_BEGIN({begin:/[rR]'(-*)\{/,end:/\}(-*)'/}),e.END_SAME_AS_BEGIN({begin:/[rR]'(-*)\[/,end:/\](-*)'/}),{begin:'"',end:'"',relevance:0},{begin:"'",end:"'",relevance:0}]},{relevance:0,variants:[{scope:{1:"operator",2:"number"},match:[i,o]},{scope:{1:"operator",2:"number"},match:[/%[^%]*%/,o]},{scope:{1:"punctuation",2:"number"},match:[s,o]},{scope:{2:"number"},match:[/[^a-zA-Z0-9._]|^/,o]}]},{scope:{3:"operator"},match:[t,/\s+/,/<-/,/\s+/]},{scope:"operator",relevance:0,variants:[{match:i},{match:/%[^%]*%/}]},{scope:"punctuation",relevance:0,match:s},{begin:"`",end:"`",contains:[{begin:/\\./}]}]}})),$((()=>{$(document).on("shiny:value",(n=>{n.name.match(/-code$/)&&($(`#${n.name}`).addClass("language-r"),setTimeout((()=>{delete document.getElementById(n.name).dataset.highlighted,e.highlightElement(document.getElementById(n.name))}),250))}))})),$((()=>{s(),t(),n()}));const n=()=>{$("body").on("click",".stack-edit-toggle",(e=>{$(e.currentTarget).find("i").toggleClass("fa-chevron-up fa-chevron-down")})),$("body").on("click",".block-output-toggle",(e=>{$(e.currentTarget).find("i").toggleClass("fa-chevron-up fa-chevron-down")}))},t=()=>{$("body").on("click",".block-output-toggle",(e=>{const n=$(e.target).closest(".block");n.find(".block-inputs").is(":visible")?(n.find(".block-inputs").addClass("d-none"),n.find(".block-output").addClass("d-none")):(n.find(".block-inputs").removeClass("d-none"),n.find(".block-output").removeClass("d-none"));let t="shown";n.find(".block-output").hasClass("d-none")&&(t="hidden"),n.find(".block-inputs").trigger(t),n.find(".block-output").trigger(t)}))},s=()=>{$("body").on("click",".stack-edit-toggle",(e=>{const n=$(e.target).closest(".stack").find(".block");$(e.currentTarget).toggleClass("editable");const t=$(e.currentTarget).hasClass("editable");n.each(((e,o)=>{const i=$(o);if(t){if(i.removeClass("d-none"),i.find(".block-title").removeClass("d-none"),i.find(".block-download").removeClass("d-none"),i.find(".block-code-toggle").removeClass("d-none"),i.find(".block-output-toggle").removeClass("d-none"),e==n.length-1)return i.find(".block-output").addClass("show"),i.find(".block-output").removeClass("d-none"),i.find(".block-output").trigger("shown"),window.bootstrap.Collapse.getOrCreateInstance(i.find(".block-code")[0],{toggle:!1}).hide(),i.find(".block-inputs").removeClass("d-none"),void i.find(".block-inputs").trigger("shown");i.find(".block-loading").addClass("d-none")}else{if(i.find(".block-download").addClass("d-none"),i.find(".block-code-toggle").addClass("d-none"),i.find(".block-output-toggle").addClass("d-none"),i.find(".block-output-toggle").find("i").addClass("fa-chevron-up"),i.find(".block-output-toggle").find("i").removeClass("fa-chevron-down"),i.find(".block-title").addClass("d-none"),e==n.length-1)return i.removeClass("d-none"),i.find(".block-output").addClass("show"),i.find(".block-output").removeClass("d-none"),i.find(".block-output").trigger("shown"),window.bootstrap.Collapse.getOrCreateInstance(i.find(".block-code")[0],{toggle:!1}).hide(),i.find(".block-inputs").addClass("d-none"),void i.find(".block-inputs").trigger("hidden");i.addClass("d-none")}}))}))};o(603);let a=!1;window.Shiny.addCustomMessageHandler("lock",(e=>{a=e.locked,l(),c(e.locked)}));const r=()=>a,c=e=>{const n=new CustomEvent("blockr:lock",{detail:{locked:e}});document.dispatchEvent(n)},l=()=>{a&&($(".block-code-toggle").hide(),$(".block-output-toggle").hide(),$(".stack-remove").hide(),$(".stack-add-block").hide(),$(".stack-edit-toggle").hide(),$(".block-remove").hide(),$(".stack-title").off(),$(".stack").each(((e,n)=>{const t=$(n).find(".stack-edit-toggle");t.find("i").hasClass("fa-chevron-up")||t.trigger("click")})))};window.Shiny.addCustomMessageHandler("blockr-render-stack",(e=>{const n=`#${e.stack}`;(e=>{const n=$(e).find(".block").last(),t=n.find(".block-output"),o=n.find(".block-title"),i=n.find(".block-inputs");o.addClass("d-none"),i.addClass("d-none"),n.find(".block-download").addClass("d-none"),n.find(".block-code-toggle").addClass("d-none"),n.find(".block-output-toggle").addClass("d-none");const s=t.find(".shiny-bound-output").first().attr("id");$(document).on("shiny:value",(e=>{e.name===s&&t.find(".block-loading").addClass("d-none")}))})(n),((e,n)=>{a=n,a&&(e=>{if(!a)return;let n=$(e);n.find(".block-code-toggle").hide(),n.find(".block-output-toggle").hide(),n.find(".stack-remove").hide(),n.find(".stack-add-block").hide(),n.find(".stack-edit-toggle").hide(),n.find(".block-remove").hide(),n.find(".stack-title").off();const t=n.find(".stack-edit-toggle");t.find("i").hasClass("fa-chevron-up")||t.trigger("click")})(e)})(n,e.locked),(e=>{$(document).on("shiny:outputinvalidated",(n=>{n.name.match(`^${e}`)&&n.name.match("res$|plot$")&&($(`#${n.name}`).addClass("d-none"),$(`#${n.name}`).closest(".block").find(".block-loading").removeClass("d-none"))})),$(document).on("shiny:value shiny:error",(n=>{n.name.match(`^${e}`)&&n.name.match("res$|plot$")&&($(`#${n.name}`).removeClass("d-none"),$(`#${n.name}`).closest(".block").find(".block-loading").addClass("d-none"))}))})(e.stack);const t=new CustomEvent("blockr:stack-render",{detail:e});document.dispatchEvent(t)})),window.Shiny.addCustomMessageHandler("blockr-add-block",(e=>{const n=`#${e.stack}`;$(n).removeClass("d-none"),setTimeout((()=>{((e,n)=>{const t=$(e).find(".block-output-toggle");$(t).each(((e,t)=>{$(t).closest(".block").data("value")!=`${n}-block`&&($(t).find("i").hasClass("fa-chevron-down")||$(t).trigger("click"))}))})(n,e.block)}),350)})),window.Shiny.addCustomMessageHandler("validate-block",(e=>{})),window.Shiny.addCustomMessageHandler("validate-input",(e=>{})),window.Shiny.addCustomMessageHandler("toggle-submit",(e=>{$(`#${e.id}`).toggleClass("disabled",!e.state).attr("aria-disabled",!e.state)})),o(294)})(),i})())); \ No newline at end of file +!function(e,n){"object"==typeof exports&&"object"==typeof module?module.exports=n(require("Shiny")):"function"==typeof define&&define.amd?define(["Shiny"],n):"object"==typeof exports?exports.blockr=n(require("Shiny")):e.blockr=n(e.Shiny)}(self,(e=>(()=>{var n={826:()=>{window.Shiny.addCustomMessageHandler("blockr-copy-code",(e=>{var n;e.code?(n=e.code.map((e=>e.trim())).join("\n\t"),navigator.clipboard.writeText(n),window.Shiny.notifications.show({html:"Code copied to clipboard",type:"message"})):window.Shiny.notifications.show({html:"Failed to copy code to clipboard",type:"error"})})),window.Shiny.addCustomMessageHandler("toggle-copy-code",(e=>{e.state?$(`#${e.id}`).show():$(`#${e.id}`).hide()}))},603:()=>{$((()=>{$("body").on("click",".stack-title-display",(e=>{const n=$(e.target).closest(".stack-title");n.find(".stack-title-display").addClass("d-none"),n.find(".stack-title-input").removeClass("d-none"),n.find(".stack-title-input").find("input").focus()})),$("body").on("click",".stack-title-save",(e=>{const n=$(e.target).closest(".input-group").closest(".stack-title"),t=n.find(".stack-title-input").find("input").val();""!==t?(n.find(".stack-title-display").text(t),n.find(".stack-title-input").addClass("d-none"),n.find(".stack-title-display").removeClass("d-none")):window.Shiny.notifications.show({html:"Must set a title",type:"error"})})),$("body").on("keydown",".stack-title-input",(e=>{if("Enter"!==e.key)return;const n=$(e.target).closest(".stack-title"),t=$(e.target).val();""!==t?(n.find(".stack-title-display").text(t),n.find(".stack-title-display").removeClass("d-none"),n.find(".stack-title-input").addClass("d-none")):window.Shiny.notifications.show({html:"Must set a title",type:"error"})}))}))},294:()=>{$((()=>{e()}));const e=()=>{[...document.querySelectorAll('[data-bs-toggle="tooltip"]')].map((e=>new window.bootstrap.Tooltip(e)))}},230:n=>{"use strict";n.exports=e},390:e=>{function n(e){return e instanceof Map?e.clear=e.delete=e.set=function(){throw new Error("map is read-only")}:e instanceof Set&&(e.add=e.clear=e.delete=function(){throw new Error("set is read-only")}),Object.freeze(e),Object.getOwnPropertyNames(e).forEach((t=>{const o=e[t],i=typeof o;"object"!==i&&"function"!==i||Object.isFrozen(o)||n(o)})),e}class t{constructor(e){void 0===e.data&&(e.data={}),this.data=e.data,this.isMatchIgnored=!1}ignoreMatch(){this.isMatchIgnored=!0}}function o(e){return e.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'")}function i(e,...n){const t=Object.create(null);for(const n in e)t[n]=e[n];return n.forEach((function(e){for(const n in e)t[n]=e[n]})),t}const s=e=>!!e.scope;class a{constructor(e,n){this.buffer="",this.classPrefix=n.classPrefix,e.walk(this)}addText(e){this.buffer+=o(e)}openNode(e){if(!s(e))return;const n=((e,{prefix:n})=>{if(e.startsWith("language:"))return e.replace("language:","language-");if(e.includes(".")){const t=e.split(".");return[`${n}${t.shift()}`,...t.map(((e,n)=>`${e}${"_".repeat(n+1)}`))].join(" ")}return`${n}${e}`})(e.scope,{prefix:this.classPrefix});this.span(n)}closeNode(e){s(e)&&(this.buffer+="")}value(){return this.buffer}span(e){this.buffer+=``}}const r=(e={})=>{const n={children:[]};return Object.assign(n,e),n};class c{constructor(){this.rootNode=r(),this.stack=[this.rootNode]}get top(){return this.stack[this.stack.length-1]}get root(){return this.rootNode}add(e){this.top.children.push(e)}openNode(e){const n=r({scope:e});this.add(n),this.stack.push(n)}closeNode(){if(this.stack.length>1)return this.stack.pop()}closeAllNodes(){for(;this.closeNode(););}toJSON(){return JSON.stringify(this.rootNode,null,4)}walk(e){return this.constructor._walk(e,this.rootNode)}static _walk(e,n){return"string"==typeof n?e.addText(n):n.children&&(e.openNode(n),n.children.forEach((n=>this._walk(e,n))),e.closeNode(n)),e}static _collapse(e){"string"!=typeof e&&e.children&&(e.children.every((e=>"string"==typeof e))?e.children=[e.children.join("")]:e.children.forEach((e=>{c._collapse(e)})))}}class l extends c{constructor(e){super(),this.options=e}addText(e){""!==e&&this.add(e)}startScope(e){this.openNode(e)}endScope(){this.closeNode()}__addSublanguage(e,n){const t=e.root;n&&(t.scope=`language:${n}`),this.add(t)}toHTML(){return new a(this,this.options).value()}finalize(){return this.closeAllNodes(),!0}}function d(e){return e?"string"==typeof e?e:e.source:null}function u(e){return f("(?=",e,")")}function g(e){return f("(?:",e,")*")}function h(e){return f("(?:",e,")?")}function f(...e){return e.map((e=>d(e))).join("")}function p(...e){const n=function(e){const n=e[e.length-1];return"object"==typeof n&&n.constructor===Object?(e.splice(e.length-1,1),n):{}}(e);return"("+(n.capture?"":"?:")+e.map((e=>d(e))).join("|")+")"}function b(e){return new RegExp(e.toString()+"|").exec("").length-1}const m=/\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./;function k(e,{joinWith:n}){let t=0;return e.map((e=>{t+=1;const n=t;let o=d(e),i="";for(;o.length>0;){const e=m.exec(o);if(!e){i+=o;break}i+=o.substring(0,e.index),o=o.substring(e.index+e[0].length),"\\"===e[0][0]&&e[1]?i+="\\"+String(Number(e[1])+n):(i+=e[0],"("===e[0]&&t++)}return i})).map((e=>`(${e})`)).join(n)}const w="[a-zA-Z]\\w*",y="[a-zA-Z_]\\w*",E="\\b\\d+(\\.\\d+)?",v="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",x="\\b(0b[01]+)",_={begin:"\\\\[\\s\\S]",relevance:0},S={scope:"string",begin:"'",end:"'",illegal:"\\n",contains:[_]},C={scope:"string",begin:'"',end:'"',illegal:"\\n",contains:[_]},$=function(e,n,t={}){const o=i({scope:"comment",begin:e,end:n,contains:[]},t);o.contains.push({scope:"doctag",begin:"[ ]*(?=(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):)",end:/(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):/,excludeBegin:!0,relevance:0});const s=p("I","a","is","so","us","to","at","if","in","it","on",/[A-Za-z]+['](d|ve|re|ll|t|s|n)/,/[A-Za-z]+[-][a-z]+/,/[A-Za-z][a-z]{2,}/);return o.contains.push({begin:f(/[ ]+/,"(",s,/[.]?[:]?([.][ ]|[ ])/,"){3}")}),o},A=$("//","$"),M=$("/\\*","\\*/"),N=$("#","$"),O={scope:"number",begin:E,relevance:0},R={scope:"number",begin:v,relevance:0},T={scope:"number",begin:x,relevance:0},j={scope:"regexp",begin:/\/(?=[^/\n]*\/)/,end:/\/[gimuy]*/,contains:[_,{begin:/\[/,end:/\]/,relevance:0,contains:[_]}]},I={scope:"title",begin:w,relevance:0},L={scope:"title",begin:y,relevance:0},B={begin:"\\.\\s*"+y,relevance:0};var H=Object.freeze({__proto__:null,APOS_STRING_MODE:S,BACKSLASH_ESCAPE:_,BINARY_NUMBER_MODE:T,BINARY_NUMBER_RE:x,COMMENT:$,C_BLOCK_COMMENT_MODE:M,C_LINE_COMMENT_MODE:A,C_NUMBER_MODE:R,C_NUMBER_RE:v,END_SAME_AS_BEGIN:function(e){return Object.assign(e,{"on:begin":(e,n)=>{n.data._beginMatch=e[1]},"on:end":(e,n)=>{n.data._beginMatch!==e[1]&&n.ignoreMatch()}})},HASH_COMMENT_MODE:N,IDENT_RE:w,MATCH_NOTHING_RE:/\b\B/,METHOD_GUARD:B,NUMBER_MODE:O,NUMBER_RE:E,PHRASAL_WORDS_MODE:{begin:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/},QUOTE_STRING_MODE:C,REGEXP_MODE:j,RE_STARTERS_RE:"!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",SHEBANG:(e={})=>{const n=/^#![ ]*\//;return e.binary&&(e.begin=f(n,/.*\b/,e.binary,/\b.*/)),i({scope:"meta",begin:n,end:/$/,relevance:0,"on:begin":(e,n)=>{0!==e.index&&n.ignoreMatch()}},e)},TITLE_MODE:I,UNDERSCORE_IDENT_RE:y,UNDERSCORE_TITLE_MODE:L});function P(e,n){"."===e.input[e.index-1]&&n.ignoreMatch()}function D(e,n){void 0!==e.className&&(e.scope=e.className,delete e.className)}function z(e,n){n&&e.beginKeywords&&(e.begin="\\b("+e.beginKeywords.split(" ").join("|")+")(?!\\.)(?=\\b|\\s)",e.__beforeBegin=P,e.keywords=e.keywords||e.beginKeywords,delete e.beginKeywords,void 0===e.relevance&&(e.relevance=0))}function U(e,n){Array.isArray(e.illegal)&&(e.illegal=p(...e.illegal))}function G(e,n){if(e.match){if(e.begin||e.end)throw new Error("begin & end are not supported with match");e.begin=e.match,delete e.match}}function Z(e,n){void 0===e.relevance&&(e.relevance=1)}const F=(e,n)=>{if(!e.beforeMatch)return;if(e.starts)throw new Error("beforeMatch cannot be used with starts");const t=Object.assign({},e);Object.keys(e).forEach((n=>{delete e[n]})),e.keywords=t.keywords,e.begin=f(t.beforeMatch,u(t.begin)),e.starts={relevance:0,contains:[Object.assign(t,{endsParent:!0})]},e.relevance=0,delete t.beforeMatch},W=["of","and","for","in","not","or","if","then","parent","list","value"],X="keyword";function q(e,n,t=X){const o=Object.create(null);return"string"==typeof e?i(t,e.split(" ")):Array.isArray(e)?i(t,e):Object.keys(e).forEach((function(t){Object.assign(o,q(e[t],n,t))})),o;function i(e,t){n&&(t=t.map((e=>e.toLowerCase()))),t.forEach((function(n){const t=n.split("|");o[t[0]]=[e,K(t[0],t[1])]}))}}function K(e,n){return n?Number(n):function(e){return W.includes(e.toLowerCase())}(e)?0:1}const V={},J=e=>{console.error(e)},Y=(e,...n)=>{console.log(`WARN: ${e}`,...n)},Q=(e,n)=>{V[`${e}/${n}`]||(console.log(`Deprecated as of ${e}. ${n}`),V[`${e}/${n}`]=!0)},ee=new Error;function ne(e,n,{key:t}){let o=0;const i=e[t],s={},a={};for(let e=1;e<=n.length;e++)a[e+o]=i[e],s[e+o]=!0,o+=b(n[e-1]);e[t]=a,e[t]._emit=s,e[t]._multi=!0}function te(e){!function(e){e.scope&&"object"==typeof e.scope&&null!==e.scope&&(e.beginScope=e.scope,delete e.scope)}(e),"string"==typeof e.beginScope&&(e.beginScope={_wrap:e.beginScope}),"string"==typeof e.endScope&&(e.endScope={_wrap:e.endScope}),function(e){if(Array.isArray(e.begin)){if(e.skip||e.excludeBegin||e.returnBegin)throw J("skip, excludeBegin, returnBegin not compatible with beginScope: {}"),ee;if("object"!=typeof e.beginScope||null===e.beginScope)throw J("beginScope must be object"),ee;ne(e,e.begin,{key:"beginScope"}),e.begin=k(e.begin,{joinWith:""})}}(e),function(e){if(Array.isArray(e.end)){if(e.skip||e.excludeEnd||e.returnEnd)throw J("skip, excludeEnd, returnEnd not compatible with endScope: {}"),ee;if("object"!=typeof e.endScope||null===e.endScope)throw J("endScope must be object"),ee;ne(e,e.end,{key:"endScope"}),e.end=k(e.end,{joinWith:""})}}(e)}function oe(e){function n(n,t){return new RegExp(d(n),"m"+(e.case_insensitive?"i":"")+(e.unicodeRegex?"u":"")+(t?"g":""))}class t{constructor(){this.matchIndexes={},this.regexes=[],this.matchAt=1,this.position=0}addRule(e,n){n.position=this.position++,this.matchIndexes[this.matchAt]=n,this.regexes.push([n,e]),this.matchAt+=b(e)+1}compile(){0===this.regexes.length&&(this.exec=()=>null);const e=this.regexes.map((e=>e[1]));this.matcherRe=n(k(e,{joinWith:"|"}),!0),this.lastIndex=0}exec(e){this.matcherRe.lastIndex=this.lastIndex;const n=this.matcherRe.exec(e);if(!n)return null;const t=n.findIndex(((e,n)=>n>0&&void 0!==e)),o=this.matchIndexes[t];return n.splice(0,t),Object.assign(n,o)}}class o{constructor(){this.rules=[],this.multiRegexes=[],this.count=0,this.lastIndex=0,this.regexIndex=0}getMatcher(e){if(this.multiRegexes[e])return this.multiRegexes[e];const n=new t;return this.rules.slice(e).forEach((([e,t])=>n.addRule(e,t))),n.compile(),this.multiRegexes[e]=n,n}resumingScanAtSamePosition(){return 0!==this.regexIndex}considerAll(){this.regexIndex=0}addRule(e,n){this.rules.push([e,n]),"begin"===n.type&&this.count++}exec(e){const n=this.getMatcher(this.regexIndex);n.lastIndex=this.lastIndex;let t=n.exec(e);if(this.resumingScanAtSamePosition())if(t&&t.index===this.lastIndex);else{const n=this.getMatcher(0);n.lastIndex=this.lastIndex+1,t=n.exec(e)}return t&&(this.regexIndex+=t.position+1,this.regexIndex===this.count&&this.considerAll()),t}}if(e.compilerExtensions||(e.compilerExtensions=[]),e.contains&&e.contains.includes("self"))throw new Error("ERR: contains `self` is not supported at the top-level of a language. See documentation.");return e.classNameAliases=i(e.classNameAliases||{}),function t(s,a){const r=s;if(s.isCompiled)return r;[D,G,te,F].forEach((e=>e(s,a))),e.compilerExtensions.forEach((e=>e(s,a))),s.__beforeBegin=null,[z,U,Z].forEach((e=>e(s,a))),s.isCompiled=!0;let c=null;return"object"==typeof s.keywords&&s.keywords.$pattern&&(s.keywords=Object.assign({},s.keywords),c=s.keywords.$pattern,delete s.keywords.$pattern),c=c||/\w+/,s.keywords&&(s.keywords=q(s.keywords,e.case_insensitive)),r.keywordPatternRe=n(c,!0),a&&(s.begin||(s.begin=/\B|\b/),r.beginRe=n(r.begin),s.end||s.endsWithParent||(s.end=/\B|\b/),s.end&&(r.endRe=n(r.end)),r.terminatorEnd=d(r.end)||"",s.endsWithParent&&a.terminatorEnd&&(r.terminatorEnd+=(s.end?"|":"")+a.terminatorEnd)),s.illegal&&(r.illegalRe=n(s.illegal)),s.contains||(s.contains=[]),s.contains=[].concat(...s.contains.map((function(e){return function(e){return e.variants&&!e.cachedVariants&&(e.cachedVariants=e.variants.map((function(n){return i(e,{variants:null},n)}))),e.cachedVariants?e.cachedVariants:ie(e)?i(e,{starts:e.starts?i(e.starts):null}):Object.isFrozen(e)?i(e):e}("self"===e?s:e)}))),s.contains.forEach((function(e){t(e,r)})),s.starts&&t(s.starts,a),r.matcher=function(e){const n=new o;return e.contains.forEach((e=>n.addRule(e.begin,{rule:e,type:"begin"}))),e.terminatorEnd&&n.addRule(e.terminatorEnd,{type:"end"}),e.illegal&&n.addRule(e.illegal,{type:"illegal"}),n}(r),r}(e)}function ie(e){return!!e&&(e.endsWithParent||ie(e.starts))}class se extends Error{constructor(e,n){super(e),this.name="HTMLInjectionError",this.html=n}}const ae=o,re=i,ce=Symbol("nomatch"),le=function(e){const o=Object.create(null),i=Object.create(null),s=[];let a=!0;const r="Could not find the language '{}', did you forget to load/include a language module?",c={disableAutodetect:!0,name:"Plain text",contains:[]};let d={ignoreUnescapedHTML:!1,throwUnescapedHTML:!1,noHighlightRe:/^(no-?highlight)$/i,languageDetectRe:/\blang(?:uage)?-([\w-]+)\b/i,classPrefix:"hljs-",cssSelector:"pre code",languages:null,__emitter:l};function b(e){return d.noHighlightRe.test(e)}function m(e,n,t){let o="",i="";"object"==typeof n?(o=e,t=n.ignoreIllegals,i=n.language):(Q("10.7.0","highlight(lang, code, ...args) has been deprecated."),Q("10.7.0","Please use highlight(code, options) instead.\nhttps://github.com/highlightjs/highlight.js/issues/2277"),i=e,o=n),void 0===t&&(t=!0);const s={code:o,language:i};C("before:highlight",s);const a=s.result?s.result:k(s.language,s.code,t);return a.code=s.code,C("after:highlight",a),a}function k(e,n,i,s){const c=Object.create(null);function l(){if(!C.keywords)return void A.addText(M);let e=0;C.keywordPatternRe.lastIndex=0;let n=C.keywordPatternRe.exec(M),t="";for(;n;){t+=M.substring(e,n.index);const i=v.case_insensitive?n[0].toLowerCase():n[0],s=(o=i,C.keywords[o]);if(s){const[e,o]=s;if(A.addText(t),t="",c[i]=(c[i]||0)+1,c[i]<=7&&(N+=o),e.startsWith("_"))t+=n[0];else{const t=v.classNameAliases[e]||e;g(n[0],t)}}else t+=n[0];e=C.keywordPatternRe.lastIndex,n=C.keywordPatternRe.exec(M)}var o;t+=M.substring(e),A.addText(t)}function u(){null!=C.subLanguage?function(){if(""===M)return;let e=null;if("string"==typeof C.subLanguage){if(!o[C.subLanguage])return void A.addText(M);e=k(C.subLanguage,M,!0,$[C.subLanguage]),$[C.subLanguage]=e._top}else e=w(M,C.subLanguage.length?C.subLanguage:null);C.relevance>0&&(N+=e.relevance),A.__addSublanguage(e._emitter,e.language)}():l(),M=""}function g(e,n){""!==e&&(A.startScope(n),A.addText(e),A.endScope())}function h(e,n){let t=1;const o=n.length-1;for(;t<=o;){if(!e._emit[t]){t++;continue}const o=v.classNameAliases[e[t]]||e[t],i=n[t];o?g(i,o):(M=i,l(),M=""),t++}}function f(e,n){return e.scope&&"string"==typeof e.scope&&A.openNode(v.classNameAliases[e.scope]||e.scope),e.beginScope&&(e.beginScope._wrap?(g(M,v.classNameAliases[e.beginScope._wrap]||e.beginScope._wrap),M=""):e.beginScope._multi&&(h(e.beginScope,n),M="")),C=Object.create(e,{parent:{value:C}}),C}function p(e,n,o){let i=function(e,n){const t=e&&e.exec(n);return t&&0===t.index}(e.endRe,o);if(i){if(e["on:end"]){const o=new t(e);e["on:end"](n,o),o.isMatchIgnored&&(i=!1)}if(i){for(;e.endsParent&&e.parent;)e=e.parent;return e}}if(e.endsWithParent)return p(e.parent,n,o)}function b(e){return 0===C.matcher.regexIndex?(M+=e[0],1):(T=!0,0)}function m(e){const t=e[0],o=n.substring(e.index),i=p(C,e,o);if(!i)return ce;const s=C;C.endScope&&C.endScope._wrap?(u(),g(t,C.endScope._wrap)):C.endScope&&C.endScope._multi?(u(),h(C.endScope,e)):s.skip?M+=t:(s.returnEnd||s.excludeEnd||(M+=t),u(),s.excludeEnd&&(M=t));do{C.scope&&A.closeNode(),C.skip||C.subLanguage||(N+=C.relevance),C=C.parent}while(C!==i.parent);return i.starts&&f(i.starts,e),s.returnEnd?0:t.length}let y={};function E(o,s){const r=s&&s[0];if(M+=o,null==r)return u(),0;if("begin"===y.type&&"end"===s.type&&y.index===s.index&&""===r){if(M+=n.slice(s.index,s.index+1),!a){const n=new Error(`0 width match regex (${e})`);throw n.languageName=e,n.badRule=y.rule,n}return 1}if(y=s,"begin"===s.type)return function(e){const n=e[0],o=e.rule,i=new t(o),s=[o.__beforeBegin,o["on:begin"]];for(const t of s)if(t&&(t(e,i),i.isMatchIgnored))return b(n);return o.skip?M+=n:(o.excludeBegin&&(M+=n),u(),o.returnBegin||o.excludeBegin||(M=n)),f(o,e),o.returnBegin?0:n.length}(s);if("illegal"===s.type&&!i){const e=new Error('Illegal lexeme "'+r+'" for mode "'+(C.scope||"")+'"');throw e.mode=C,e}if("end"===s.type){const e=m(s);if(e!==ce)return e}if("illegal"===s.type&&""===r)return 1;if(R>1e5&&R>3*s.index)throw new Error("potential infinite loop, way more iterations than matches");return M+=r,r.length}const v=x(e);if(!v)throw J(r.replace("{}",e)),new Error('Unknown language: "'+e+'"');const _=oe(v);let S="",C=s||_;const $={},A=new d.__emitter(d);!function(){const e=[];for(let n=C;n!==v;n=n.parent)n.scope&&e.unshift(n.scope);e.forEach((e=>A.openNode(e)))}();let M="",N=0,O=0,R=0,T=!1;try{if(v.__emitTokens)v.__emitTokens(n,A);else{for(C.matcher.considerAll();;){R++,T?T=!1:C.matcher.considerAll(),C.matcher.lastIndex=O;const e=C.matcher.exec(n);if(!e)break;const t=E(n.substring(O,e.index),e);O=e.index+t}E(n.substring(O))}return A.finalize(),S=A.toHTML(),{language:e,value:S,relevance:N,illegal:!1,_emitter:A,_top:C}}catch(t){if(t.message&&t.message.includes("Illegal"))return{language:e,value:ae(n),illegal:!0,relevance:0,_illegalBy:{message:t.message,index:O,context:n.slice(O-100,O+100),mode:t.mode,resultSoFar:S},_emitter:A};if(a)return{language:e,value:ae(n),illegal:!1,relevance:0,errorRaised:t,_emitter:A,_top:C};throw t}}function w(e,n){n=n||d.languages||Object.keys(o);const t=function(e){const n={value:ae(e),illegal:!1,relevance:0,_top:c,_emitter:new d.__emitter(d)};return n._emitter.addText(e),n}(e),i=n.filter(x).filter(S).map((n=>k(n,e,!1)));i.unshift(t);const s=i.sort(((e,n)=>{if(e.relevance!==n.relevance)return n.relevance-e.relevance;if(e.language&&n.language){if(x(e.language).supersetOf===n.language)return 1;if(x(n.language).supersetOf===e.language)return-1}return 0})),[a,r]=s,l=a;return l.secondBest=r,l}function y(e){let n=null;const t=function(e){let n=e.className+" ";n+=e.parentNode?e.parentNode.className:"";const t=d.languageDetectRe.exec(n);if(t){const n=x(t[1]);return n||(Y(r.replace("{}",t[1])),Y("Falling back to no-highlight mode for this block.",e)),n?t[1]:"no-highlight"}return n.split(/\s+/).find((e=>b(e)||x(e)))}(e);if(b(t))return;if(C("before:highlightElement",{el:e,language:t}),e.dataset.highlighted)return void console.log("Element previously highlighted. To highlight again, first unset `dataset.highlighted`.",e);if(e.children.length>0&&(d.ignoreUnescapedHTML||(console.warn("One of your code blocks includes unescaped HTML. This is a potentially serious security risk."),console.warn("https://github.com/highlightjs/highlight.js/wiki/security"),console.warn("The element with unescaped HTML:"),console.warn(e)),d.throwUnescapedHTML))throw new se("One of your code blocks includes unescaped HTML.",e.innerHTML);n=e;const o=n.textContent,s=t?m(o,{language:t,ignoreIllegals:!0}):w(o);e.innerHTML=s.value,e.dataset.highlighted="yes",function(e,n,t){const o=n&&i[n]||t;e.classList.add("hljs"),e.classList.add(`language-${o}`)}(e,t,s.language),e.result={language:s.language,re:s.relevance,relevance:s.relevance},s.secondBest&&(e.secondBest={language:s.secondBest.language,relevance:s.secondBest.relevance}),C("after:highlightElement",{el:e,result:s,text:o})}let E=!1;function v(){"loading"!==document.readyState?document.querySelectorAll(d.cssSelector).forEach(y):E=!0}function x(e){return e=(e||"").toLowerCase(),o[e]||o[i[e]]}function _(e,{languageName:n}){"string"==typeof e&&(e=[e]),e.forEach((e=>{i[e.toLowerCase()]=n}))}function S(e){const n=x(e);return n&&!n.disableAutodetect}function C(e,n){const t=e;s.forEach((function(e){e[t]&&e[t](n)}))}"undefined"!=typeof window&&window.addEventListener&&window.addEventListener("DOMContentLoaded",(function(){E&&v()}),!1),Object.assign(e,{highlight:m,highlightAuto:w,highlightAll:v,highlightElement:y,highlightBlock:function(e){return Q("10.7.0","highlightBlock will be removed entirely in v12.0"),Q("10.7.0","Please use highlightElement now."),y(e)},configure:function(e){d=re(d,e)},initHighlighting:()=>{v(),Q("10.6.0","initHighlighting() deprecated. Use highlightAll() now.")},initHighlightingOnLoad:function(){v(),Q("10.6.0","initHighlightingOnLoad() deprecated. Use highlightAll() now.")},registerLanguage:function(n,t){let i=null;try{i=t(e)}catch(e){if(J("Language definition for '{}' could not be registered.".replace("{}",n)),!a)throw e;J(e),i=c}i.name||(i.name=n),o[n]=i,i.rawDefinition=t.bind(null,e),i.aliases&&_(i.aliases,{languageName:n})},unregisterLanguage:function(e){delete o[e];for(const n of Object.keys(i))i[n]===e&&delete i[n]},listLanguages:function(){return Object.keys(o)},getLanguage:x,registerAliases:_,autoDetection:S,inherit:re,addPlugin:function(e){!function(e){e["before:highlightBlock"]&&!e["before:highlightElement"]&&(e["before:highlightElement"]=n=>{e["before:highlightBlock"](Object.assign({block:n.el},n))}),e["after:highlightBlock"]&&!e["after:highlightElement"]&&(e["after:highlightElement"]=n=>{e["after:highlightBlock"](Object.assign({block:n.el},n))})}(e),s.push(e)},removePlugin:function(e){const n=s.indexOf(e);-1!==n&&s.splice(n,1)}}),e.debugMode=function(){a=!1},e.safeMode=function(){a=!0},e.versionString="11.9.0",e.regex={concat:f,lookahead:u,either:p,optional:h,anyNumberOfTimes:g};for(const e in H)"object"==typeof H[e]&&n(H[e]);return Object.assign(e,H),e},de=le({});de.newInstance=()=>le({}),e.exports=de,de.HighlightJS=de,de.default=de}},t={};function o(e){var i=t[e];if(void 0!==i)return i.exports;var s=t[e]={exports:{}};return n[e](s,s.exports,o),s.exports}o.d=(e,n)=>{for(var t in n)o.o(n,t)&&!o.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:n[t]})},o.o=(e,n)=>Object.prototype.hasOwnProperty.call(e,n),o.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})};var i={};return(()=>{"use strict";o.r(i),o.d(i,{isLocked:()=>r}),o(230),o(826);const e=o(390);e.registerLanguage("r",(function(e){const n=e.regex,t=/(?:(?:[a-zA-Z]|\.[._a-zA-Z])[._a-zA-Z0-9]*)|\.(?!\d)/,o=n.either(/0[xX][0-9a-fA-F]+\.[0-9a-fA-F]*[pP][+-]?\d+i?/,/0[xX][0-9a-fA-F]+(?:[pP][+-]?\d+)?[Li]?/,/(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?[Li]?/),i=/[=!<>:]=|\|\||&&|:::?|<-|<<-|->>|->|\|>|[-+*\/?!$&|:<=>@^~]|\*\*/,s=n.either(/[()]/,/[{}]/,/\[\[/,/[[\]]/,/\\/,/,/);return{name:"R",keywords:{$pattern:t,keyword:"function if in break next repeat else for while",literal:"NULL NA TRUE FALSE Inf NaN NA_integer_|10 NA_real_|10 NA_character_|10 NA_complex_|10",built_in:"LETTERS letters month.abb month.name pi T F abs acos acosh all any anyNA Arg as.call as.character as.complex as.double as.environment as.integer as.logical as.null.default as.numeric as.raw asin asinh atan atanh attr attributes baseenv browser c call ceiling class Conj cos cosh cospi cummax cummin cumprod cumsum digamma dim dimnames emptyenv exp expression floor forceAndCall gamma gc.time globalenv Im interactive invisible is.array is.atomic is.call is.character is.complex is.double is.environment is.expression is.finite is.function is.infinite is.integer is.language is.list is.logical is.matrix is.na is.name is.nan is.null is.numeric is.object is.pairlist is.raw is.recursive is.single is.symbol lazyLoadDBfetch length lgamma list log max min missing Mod names nargs nzchar oldClass on.exit pos.to.env proc.time prod quote range Re rep retracemem return round seq_along seq_len seq.int sign signif sin sinh sinpi sqrt standardGeneric substitute sum switch tan tanh tanpi tracemem trigamma trunc unclass untracemem UseMethod xtfrm"},contains:[e.COMMENT(/#'/,/$/,{contains:[{scope:"doctag",match:/@examples/,starts:{end:n.lookahead(n.either(/\n^#'\s*(?=@[a-zA-Z]+)/,/\n^(?!#')/)),endsParent:!0}},{scope:"doctag",begin:"@param",end:/$/,contains:[{scope:"variable",variants:[{match:t},{match:/`(?:\\.|[^`\\])+`/}],endsParent:!0}]},{scope:"doctag",match:/@[a-zA-Z]+/},{scope:"keyword",match:/\\[a-zA-Z]+/}]}),e.HASH_COMMENT_MODE,{scope:"string",contains:[e.BACKSLASH_ESCAPE],variants:[e.END_SAME_AS_BEGIN({begin:/[rR]"(-*)\(/,end:/\)(-*)"/}),e.END_SAME_AS_BEGIN({begin:/[rR]"(-*)\{/,end:/\}(-*)"/}),e.END_SAME_AS_BEGIN({begin:/[rR]"(-*)\[/,end:/\](-*)"/}),e.END_SAME_AS_BEGIN({begin:/[rR]'(-*)\(/,end:/\)(-*)'/}),e.END_SAME_AS_BEGIN({begin:/[rR]'(-*)\{/,end:/\}(-*)'/}),e.END_SAME_AS_BEGIN({begin:/[rR]'(-*)\[/,end:/\](-*)'/}),{begin:'"',end:'"',relevance:0},{begin:"'",end:"'",relevance:0}]},{relevance:0,variants:[{scope:{1:"operator",2:"number"},match:[i,o]},{scope:{1:"operator",2:"number"},match:[/%[^%]*%/,o]},{scope:{1:"punctuation",2:"number"},match:[s,o]},{scope:{2:"number"},match:[/[^a-zA-Z0-9._]|^/,o]}]},{scope:{3:"operator"},match:[t,/\s+/,/<-/,/\s+/]},{scope:"operator",relevance:0,variants:[{match:i},{match:/%[^%]*%/}]},{scope:"punctuation",relevance:0,match:s},{begin:"`",end:"`",contains:[{begin:/\\./}]}]}})),$((()=>{$(document).on("shiny:value",(n=>{n.name.match(/-code$/)&&($(`#${n.name}`).addClass("language-r"),setTimeout((()=>{delete document.getElementById(n.name).dataset.highlighted,e.highlightElement(document.getElementById(n.name))}),250))}))})),$((()=>{s(),t(),n()}));const n=()=>{$("body").on("click",".stack-edit-toggle",(e=>{$(e.currentTarget).find("i").toggleClass("fa-chevron-up fa-chevron-down")})),$("body").on("click",".block-output-toggle",(e=>{$(e.currentTarget).find("i").toggleClass("fa-chevron-up fa-chevron-down")}))},t=()=>{$("body").on("click",".block-output-toggle",(e=>{const n=$(e.target).closest(".block");n.find(".block-inputs").is(":visible")?(n.find(".block-inputs").addClass("d-none"),n.find(".block-output").addClass("d-none")):(n.find(".block-inputs").removeClass("d-none"),n.find(".block-output").removeClass("d-none"));let t="shown";n.find(".block-output").hasClass("d-none")&&(t="hidden"),n.find(".block-inputs").trigger(t),n.find(".block-output").trigger(t)}))},s=()=>{$("body").on("click",".stack-edit-toggle",(e=>{const n=$(e.target).closest(".stack").find(".block");$(e.currentTarget).toggleClass("editable");const t=$(e.currentTarget).hasClass("editable");n.each(((e,o)=>{const i=$(o);if(t){if(i.removeClass("d-none"),i.find(".block-title").removeClass("d-none"),i.find(".block-download").removeClass("d-none"),i.find(".block-code-toggle").removeClass("d-none"),i.find(".block-output-toggle").removeClass("d-none"),e==n.length-1)return i.find(".block-output").addClass("show"),i.find(".block-output").removeClass("d-none"),i.find(".block-output").trigger("shown"),window.bootstrap.Collapse.getOrCreateInstance(i.find(".block-code")[0],{toggle:!1}).hide(),i.find(".block-inputs").removeClass("d-none"),void i.find(".block-inputs").trigger("shown");i.find(".block-loading").addClass("d-none")}else{if(i.find(".block-download").addClass("d-none"),i.find(".block-code-toggle").addClass("d-none"),i.find(".block-output-toggle").addClass("d-none"),i.find(".block-output-toggle").find("i").addClass("fa-chevron-up"),i.find(".block-output-toggle").find("i").removeClass("fa-chevron-down"),i.find(".block-title").addClass("d-none"),e==n.length-1)return i.removeClass("d-none"),i.find(".block-output").addClass("show"),i.find(".block-output").removeClass("d-none"),i.find(".block-output").trigger("shown"),window.bootstrap.Collapse.getOrCreateInstance(i.find(".block-code")[0],{toggle:!1}).hide(),i.find(".block-inputs").addClass("d-none"),void i.find(".block-inputs").trigger("hidden");i.addClass("d-none")}}))}))};o(603);let a=!1;window.Shiny.addCustomMessageHandler("lock",(e=>{a=e.locked,l(),c(e.locked)}));const r=()=>a,c=e=>{const n=new CustomEvent("blockr:lock",{detail:{locked:e}});document.dispatchEvent(n)},l=()=>{a&&($(".block-code-toggle").hide(),$(".block-output-toggle").hide(),$(".stack-remove").hide(),$(".stack-add-block").hide(),$(".stack-edit-toggle").hide(),$(".block-remove").hide(),$(".stack-title").off(),$(".stack").each(((e,n)=>{const t=$(n).find(".stack-edit-toggle");t.find("i").hasClass("fa-chevron-up")||t.trigger("click")})))};window.Shiny.addCustomMessageHandler("blockr-render-stack",(e=>{const n=`#${e.stack}`;(e=>{const n=$(e).find(".block").last(),t=n.find(".block-output"),o=n.find(".block-title"),i=n.find(".block-inputs");o.addClass("d-none"),i.addClass("d-none"),n.find(".block-download").addClass("d-none"),n.find(".block-code-toggle").addClass("d-none"),n.find(".block-output-toggle").addClass("d-none");const s=t.find(".shiny-bound-output").first().attr("id");$(document).on("shiny:value",(e=>{e.name===s&&t.find(".block-loading").addClass("d-none")}))})(n),((e,n)=>{a=n,a&&(e=>{if(!a)return;let n=$(e);n.find(".block-code-toggle").hide(),n.find(".block-output-toggle").hide(),n.find(".stack-remove").hide(),n.find(".stack-add-block").hide(),n.find(".stack-edit-toggle").hide(),n.find(".block-remove").hide(),n.find(".stack-title").off();const t=n.find(".stack-edit-toggle");t.find("i").hasClass("fa-chevron-up")||t.trigger("click")})(e)})(n,e.locked),(e=>{$(document).on("shiny:outputinvalidated",(n=>{n.name.match(`^${e}`)&&n.name.match("res$|plot$")&&($(`#${n.name}`).addClass("d-none"),$(`#${n.name}`).closest(".block").find(".block-loading").removeClass("d-none"))})),$(document).on("shiny:value shiny:error",(n=>{n.name.match(`^${e}`)&&n.name.match("res$|plot$")&&($(`#${n.name}`).removeClass("d-none"),$(`#${n.name}`).closest(".block").find(".block-loading").addClass("d-none"))}))})(e.stack);const t=new CustomEvent("blockr:stack-render",{detail:e});document.dispatchEvent(t)})),window.Shiny.addCustomMessageHandler("blockr-add-block",(e=>{const n=`#${e.stack}`;$(n).removeClass("d-none"),setTimeout((()=>{((e,n)=>{const t=$(e).find(".block-output-toggle");$(t).each(((e,t)=>{$(t).closest(".block").data("value")!=`${n}-block`&&($(t).find("i").hasClass("fa-chevron-down")||$(t).trigger("click"))}))})(n,e.block)}),350)})),window.Shiny.addCustomMessageHandler("validate-block",(e=>{})),window.Shiny.addCustomMessageHandler("validate-input",(e=>{})),window.Shiny.addCustomMessageHandler("toggle-submit",(e=>{$(`#${e.id}`).toggleClass("disabled",!e.state).attr("aria-disabled",!e.state)})),o(294)})(),i})())); \ No newline at end of file diff --git a/man/generate_server.Rd b/man/generate_server.Rd index 6ad2f85d..6ccaea49 100644 --- a/man/generate_server.Rd +++ b/man/generate_server.Rd @@ -14,7 +14,7 @@ generate_server(x, ...) \method{generate_server}{result_field}(x, ...) -\method{generate_server}{data_block}(x, id, ...) +\method{generate_server}{data_block}(x, id, linked_stack, ...) \method{generate_server}{transform_block}(x, in_dat, id, is_prev_valid, ...) @@ -25,6 +25,7 @@ generate_server(x, ...) id = NULL, new_block = NULL, workspace = get_workspace(), + prev_stack, ... ) diff --git a/man/validate_field.Rd b/man/validate_field.Rd index 5a4a00fe..4bad79b1 100644 --- a/man/validate_field.Rd +++ b/man/validate_field.Rd @@ -17,6 +17,8 @@ \alias{validate_field.default} \alias{is_valid} \alias{is_valid.field} +\alias{is_valid.block} +\alias{is_valid.stack} \alias{validation_failure} \title{Validate field generic} \usage{ @@ -52,6 +54,10 @@ is_valid(x) \method{is_valid}{field}(x) +\method{is_valid}{block}(x) + +\method{is_valid}{stack}(x) + validation_failure(..., class = character()) } \arguments{ diff --git a/srcjs/copy.js b/srcjs/copy.js index 7b35315a..c1feaafa 100644 --- a/srcjs/copy.js +++ b/srcjs/copy.js @@ -17,3 +17,11 @@ window.Shiny.addCustomMessageHandler("blockr-copy-code", (msg) => { type: "message", }); }); + +window.Shiny.addCustomMessageHandler("toggle-copy-code", (msg) => { + if (!msg.state) { + $(`#${msg.id}`).hide() + } else { + $(`#${msg.id}`).show() + } +}); diff --git a/tests/testthat/_snaps/stack/stack-app-003_.new.png b/tests/testthat/_snaps/stack/stack-app-003_.new.png index b6a99d73..1cf73f00 100644 Binary files a/tests/testthat/_snaps/stack/stack-app-003_.new.png and b/tests/testthat/_snaps/stack/stack-app-003_.new.png differ diff --git a/tests/testthat/_snaps/stack/stack-app-004_.new.png b/tests/testthat/_snaps/stack/stack-app-004_.new.png index 3cd83561..c28c7651 100644 Binary files a/tests/testthat/_snaps/stack/stack-app-004_.new.png and b/tests/testthat/_snaps/stack/stack-app-004_.new.png differ diff --git a/tests/testthat/_snaps/stack/stack-app-005_.new.png b/tests/testthat/_snaps/stack/stack-app-005_.new.png index e4785146..24dbc958 100644 Binary files a/tests/testthat/_snaps/stack/stack-app-005_.new.png and b/tests/testthat/_snaps/stack/stack-app-005_.new.png differ diff --git a/tests/testthat/_snaps/stack/stack-app-006_.new.png b/tests/testthat/_snaps/stack/stack-app-006_.new.png index f4b7011e..b8c022cd 100644 Binary files a/tests/testthat/_snaps/stack/stack-app-006_.new.png and b/tests/testthat/_snaps/stack/stack-app-006_.new.png differ diff --git a/tests/testthat/_snaps/workspace/restore-workspace-app-001_.new.png b/tests/testthat/_snaps/workspace/restore-workspace-app-001_.new.png index 83cfc7a3..73527f68 100644 Binary files a/tests/testthat/_snaps/workspace/restore-workspace-app-001_.new.png and b/tests/testthat/_snaps/workspace/restore-workspace-app-001_.new.png differ diff --git a/tests/testthat/test-validate-block.R b/tests/testthat/test-validate-block.R index 8513dc8b..b254abfe 100644 --- a/tests/testthat/test-validate-block.R +++ b/tests/testthat/test-validate-block.R @@ -1,32 +1,35 @@ -testServer(module_server_test, { - # Init values - expect_false(is_valid$block) - expect_null(is_valid$message) - session$flushReact() - expect_true(is_valid$block) - expect_true( - all.equal( - out_dat(), - iris |> select(colnames(datasets::iris)[[1]]) +testServer(module_server_test, + { + # Init values + expect_false(is_valid$block) + expect_null(is_valid$message) + session$flushReact() + expect_true(is_valid$block) + expect_true( + all.equal( + out_dat(), + iris |> select(colnames(datasets::iris)[[1]]) + ) ) - ) - # Invalidate - session$setInputs("columns" = "") - session$flushReact() - expect_error(out_dat()) - expect_false(is_valid$block) - expect_identical(is_valid$message, "selected value(s) not among provided choices") - expect_identical(is_valid$fields, "columns") + # Invalidate + session$setInputs("columns" = "") + session$flushReact() + expect_error(out_dat()) + expect_false(is_valid$block) + expect_identical(is_valid$message[["columns"]], "selected value(s) not among provided choices") + expect_identical(is_valid$fields, "columns") - # Re-validate - session$setInputs("columns" = "Species") - session$flushReact() - expect_true(is_valid$block) - expect_null(is_valid$message) -}, args = list( - id = "test", - x = new_select_block(colnames(datasets::iris)[[1]]), - in_dat = reactive(datasets::iris), - is_prev_valid = reactive(TRUE) -)) + # Re-validate + session$setInputs("columns" = "Species") + session$flushReact() + expect_true(is_valid$block) + expect_null(is_valid$message) + }, + args = list( + id = "test", + x = new_select_block(colnames(datasets::iris)[[1]]), + in_dat = reactive(datasets::iris), + is_prev_valid = reactive(TRUE) + ) +) diff --git a/tests/testthat/test-validation.R b/tests/testthat/test-validation.R index 56a10d2d..dcb2275e 100644 --- a/tests/testthat/test-validation.R +++ b/tests/testthat/test-validation.R @@ -1,5 +1,4 @@ test_that("validation works", { - expect_null( validate_field( structure(1L, class = c("field_that_does_not_exist", "field")) @@ -7,7 +6,8 @@ test_that("validation works", { ) expect_error(validate_field(structure(1L, class = "not_a_field")), - class = "no_validator") + class = "no_validator" + ) expect_true(is_valid(new_string_field("foo"))) expect_false(is_valid(new_string_field())) @@ -17,7 +17,8 @@ test_that("validation works", { expect_null(validate_character("foo", "bar")) expect_error(validate_character(character(), "bar"), - class = "character_failure") + class = "character_failure" + ) expect_null(validate_bool(TRUE, "bar")) expect_error(validate_bool(logical(), "bar"), class = "bool_failure") @@ -27,4 +28,27 @@ test_that("validation works", { expect_null(validate_range(1L, 0L, 1L)) expect_error(validate_range(2L, 0L, 1L), class = "range_failure") + + + # block validation + is_select_valid <- is_valid(new_select_block()) + expect_false(is_select_valid) + expect_length(attr(is_select_valid, "msgs"), 1) + expect_identical(attr(is_select_valid, "fields"), names(new_select_block())) + + # stack validation + stack <- new_stack(new_dataset_block, new_select_block) + is_valid_stack <- is_valid(stack) + expect_false(is_valid_stack) + expect_identical(attr(is_valid_stack, "msgs"), 1L:2L) + + stack <- new_stack(new_dataset_block("iris"), new_select_block) + is_valid_stack <- is_valid(stack) + expect_false(is_valid_stack) + expect_identical(attr(is_valid_stack, "msgs"), 2L) + + stack <- new_stack(new_dataset_block("iris"), new_select_block("Species")) + is_valid_stack <- is_valid(stack) + expect_true(is_valid_stack) + expect_length(attr(is_valid_stack, "msgs"), 0) })