*! version 1.5.2 02aug2026 Eric A. Booth *! - also escape the double quote. A value or label containing one, e.g. a *! 6" pipe or a label like the "quoted" column, aborted -build- with r(198) *! and left a truncated .html on disk. It is emitted as the JS escape *! backslash-u-0022, so no quote character survives into the Stata string *! that -file write- has to parse. *! version 1.5.1 02aug2026 Eric A. Booth *! - escape ' and \ in string values, variable labels and value labels before *! they reach the generated JavaScript. A value like O'Brien, or a label like *! "Client's score", used to close the JS string early: the whole data array *! then failed to parse and the table rendered empty. *! - build reports how many controls and panels it wrote, so a dashboard built *! without -replace- no longer silently inherits the previous one's panels. *! - build explains itself when a table asks for variables that are not in *! memory, instead of stopping on a bare "variable ... not found". *! version 1.5.0 26may2026 Eric A. Booth *! - output(table) now emits value-label text for labeled numeric variables *! (e.g. "Foreign" instead of 1); falls back to the number when unlabeled. program define statashiny version 16 * 1. Backward Compatibility Check local first : word 1 of `0' if inlist("`first'", "init", "input_num", "input_check", "output_val", "output_table", "output_raw", "data_from_memory", "calc", "build") { gettoken subcmd 0 : 0, parse(" ,") _statashiny_`subcmd' `0' exit } * 2. Raw HTML / Calc Check (Bypass syntax for complex strings) local check : copy local 0 if ustrpos(`"`macval(check)'"', "output(raw)") > 0 { _statashiny_handle_raw `0' exit } if ustrpos(`"`macval(check)'"', "calc(") > 0 { _statashiny_handle_calc `0' exit } * 3. Consolidated Syntax syntax [anything(name=id)] [, /// Input(string) Output(string) Calc(string) Build /// Title(string) Subtitle(string) Replace Open /// Label(string) Val(string) Min(real -1000) Max(real 1000) Step(real 1) /// Vars(varlist) Checked Toggles /// Prefix(string) Suffix(string) /// EXPort(string) ] * Initialization if "`title'" != "" | "`replace'" != "" { _statashiny_init, title(`"`title'"') subtitle(`"`subtitle'"') `replace' } * Inputs if "`input'" != "" { if inlist("`input'", "num", "number") { _statashiny_input_num, var("`id'") label("`label'") val("`val'") min(`min') max(`max') step(`step') } else if inlist("`input'", "check", "checkbox") { _statashiny_input_check, var("`id'") label("`label'") `checked' } } * Outputs if "`output'" != "" { if inlist("`output'", "val", "value") { _statashiny_output_val, id("`id'") label("`label'") prefix("`prefix'") suffix("`suffix'") } else if "`output'" == "table" { _statashiny_output_table, id("`id'") label("`label'") _statashiny_data_from_memory, id("`id'") vars(`vars') if "`toggles'" != "" { local i = 0 foreach v of varlist `vars' { local lab : var label `v' if "`lab'" == "" local lab "`v'" _statashiny_input_check, var("toggle_`id'_`i'") label("Show `lab'") checked _statashiny_calc "if (window.table_`id') table_`id'.column(`i').visible(document.getElementById('toggle_`id'_`i'').checked);" local ++i } } } } * Build if "`build'" != "" { _statashiny_build, `open' export(`"`export'"') } * Help fallback if "`id'`input'`output'`calc'`build'`title'" == "" { help statashiny } end program define _statashiny_handle_raw local js : copy local 0 local start = ustrpos(`"`macval(js)'"', "label(") + 6 local rest = usubstr(`"`macval(js)'"', `start', .) local end = ustrrpos(`"`macval(rest)'"', ")") local html = usubstr(`"`macval(rest)'"', 1, `end'-1) local html = strtrim(`"`macval(html)'"') if usubstr(`"`macval(html)'"', 1, 2) == `"`""' & usubstr(`"`macval(html)'"', -2, 2) == `""'"' { local html = usubstr(`"`macval(html)'"', 3, ustrlen(`"`macval(html)'"')-4) } else if usubstr(`"`macval(html)'"', 1, 1) == `"""' & usubstr(`"`macval(html)'"', -1, 1) == `"""' { local html = usubstr(`"`macval(html)'"', 2, ustrlen(`"`macval(html)'"')-2) } _statashiny_output_raw `macval(html)' end program define _statashiny_handle_calc local js : copy local 0 local start = ustrpos(`"`macval(js)'"', "calc(") + 5 local rest = usubstr(`"`macval(js)'"', `start', .) local end = ustrrpos(`"`macval(rest)'"', ")") local code = usubstr(`"`macval(rest)'"', 1, `end'-1) local code = strtrim(`"`macval(code)'"') if usubstr(`"`macval(code)'"', 1, 2) == `"`""' & usubstr(`"`macval(code)'"', -2, 2) == `""'"' { local code = usubstr(`"`macval(code)'"', 3, ustrlen(`"`macval(code)'"')-4) } else if usubstr(`"`macval(code)'"', 1, 1) == `"""' & usubstr(`"`macval(code)'"', -1, 1) == `"""' { local code = usubstr(`"`macval(code)'"', 2, ustrlen(`"`macval(code)'"')-2) } _statashiny_calc `macval(code)' end program define _statashiny_init syntax [, Title(string) Subtitle(string) Replace] if "`title'" == "" local title "StataShiny Dashboard" global S_title `"`title'"' global S_subtitle `"`subtitle'"' global S_inputs "" global S_outputs "" global S_calc_js "" global S_table_list "" di as txt "StataShiny initialized: `title'" end program define _statashiny_input_num syntax, Var(string) Label(string) [Val(string) Min(real -1000) Max(real 1000) Step(real 1)] if "`val'" == "" local val 0 local html `"