├── .DS_Store
├── README.md
├── dashboard
├── dashboard.html
├── dashboard.qmd
├── dashboard_files
│ └── libs
│ │ ├── bootstrap
│ │ ├── bootstrap-icons.css
│ │ ├── bootstrap-icons.woff
│ │ ├── bootstrap.min.css
│ │ └── bootstrap.min.js
│ │ ├── clipboard
│ │ └── clipboard.min.js
│ │ ├── quarto-dashboard
│ │ ├── components.js
│ │ ├── quarto-dashboard.js
│ │ └── web-components.js
│ │ ├── quarto-html
│ │ ├── anchor.min.js
│ │ ├── popper.min.js
│ │ ├── quarto-syntax-highlighting.css
│ │ ├── quarto.js
│ │ ├── tippy.css
│ │ └── tippy.umd.min.js
│ │ └── quarto-ojs
│ │ ├── quarto-ojs-runtime.js
│ │ └── quarto-ojs.css
├── data
│ └── tswift.csv
├── theme.scss
└── tswift.jpeg
└── presentation
├── .DS_Store
├── data
└── big_tech_stock_prices.csv
├── images
├── headshot.jpeg
├── intro-observable.qmd
├── intro.webp
├── observable.png
├── observable.webp
├── philly.jpeg
└── title-slide.png
├── intro-observable.html
├── intro-observable.qmd
└── theme.scss
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tashapiro/intro-observable/2c44e8f252d49de3a5271a096c458331ae908f01/.DS_Store
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Intro to Observable Plot
2 |
3 | ## Overview
4 |
5 | This is an introductory talk covering a range of possibilities with Observable Plot From constructing simple plots, to layering in interactive components, to seeing it in action with Quarto - participants will get to sample a variety of Observable Plot applications.
6 |
7 | The first section participants are invited to walk through an Observable Notebook analyzing Taylor Swift songs.
8 |
9 | In the second section, participants will learn how they can integrate Observable Plot with Quarto (qmd) files using RStudio.
10 |
11 | 
12 |
13 | ## Additional Resources
14 |
15 | - [Observable Notebook](https://observablehq.com/d/f002f055d6fa2f8d)
16 |
17 | - [Slides](https://tashapiro.github.io/intro-observable/presentation/intro-observable.html#/TitleSlide)
18 |
19 | - [Quarto Dashboard Example](https://tashapiro.github.io/intro-observable/dashboard/dashboard.html)
20 |
21 | **Note**: The Quarto Dashboard uses the pre-release version of Quarto, 1.4. To run this example, users will need to download this version.
22 |
--------------------------------------------------------------------------------
/dashboard/dashboard.qmd:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Taylor Swift Song Analysis"
3 | format:
4 | dashboard:
5 | logo: tswift.jpeg
6 | theme: "theme.scss"
7 | ---
8 |
9 | ```{ojs}
10 | //| output: false
11 |
12 | //upload data
13 | songs = FileAttachment("data/tswift.csv").csv({ typed: true })
14 |
15 | //get list of alums
16 | albums = [...new Set(songs.map(d => d.album_name))].sort()
17 |
18 | filtered_albums = [...new Set(filtered_songs.map(d => d.album_name))].sort()
19 |
20 |
21 | //list of audio features
22 | features = ["acousticness","danceability","energy", "speechiness", "instrumentalness", "liveness", "valence"]
23 |
24 | // function from Fil's notebook to calculate correlation matrix
25 | function corr(x, y) {
26 | const n = x.length;
27 | if (y.length !== n)
28 | throw new Error("The two columns must have the same length.");
29 | const x_ = d3.mean(x);
30 | const y_ = d3.mean(y);
31 | const XY = d3.sum(x, (_, i) => (x[i] - x_) * (y[i] - y_));
32 | const XX = d3.sum(x, (d) => (d - x_) ** 2);
33 | const YY = d3.sum(y, (d) => (d - y_) ** 2);
34 | return XY / Math.sqrt(XX * YY);
35 | }
36 |
37 | //set up correlation matrix
38 | correlations = d3.cross(features, features).map(([a, b]) => ({
39 | a,
40 | b,
41 | correlation: corr(Plot.valueof(filtered_songs, a), Plot.valueof(filtered_songs, b))
42 | }))
43 |
44 |
45 | filtered_songs_album = songs.filter(d => selectAlbums.includes(d.album_name))
46 |
47 | ```
48 |
49 | # Sidebar {.sidebar}
50 |
51 | ```{ojs}
52 | //select album input, multi-select
53 | viewof selectAlbums = Inputs.select(albums, {label:"Albums", value:albums, multiple:10})
54 |
55 | ```
56 |
57 | **Minimums**
58 |
59 | ```{ojs}
60 |
61 | //Input form of audio feature sliders
62 | viewof sliders = (
63 | Inputs.form({
64 | energy : Inputs.range([0,1], {label:"Energy", step:0.01, value:0}),
65 | valence : Inputs.range([0,1], {label:"Valence", step:0.01, value:0}),
66 | acousticness : Inputs.range([0,1], {label:"Acousticness", step:0.01, value:0}),
67 | danceability : Inputs.range([0,1], {label:"Danceability", step:0.01, value:0})
68 | })
69 | )
70 | ```
71 |
72 | # Summary
73 |
74 | ## Value Boxes {height="150px"}
75 |
76 | ### Albums
77 |
78 | ```{ojs}
79 | //| component: valuebox
80 | //| title: "Albums"
81 | //| icon: vinyl-fill
82 | //| color: light
83 | filtered_albums.length
84 | ```
85 |
86 | ### Songs
87 |
88 | ```{ojs}
89 | //| component: valuebox
90 | //| title: "Songs"
91 | //| icon: music-note-beamed
92 | //| color: light
93 | filtered_songs.length
94 | ```
95 |
96 | ### Avg Energy
97 |
98 | ```{ojs}
99 | //| component: valuebox
100 | //| title: "Avg Energy"
101 | //| icon: lightning-fill
102 | //| color: light
103 | parseFloat(d3.mean(filtered_songs, d => d.energy).toFixed(2))
104 | ```
105 |
106 | ## Plots
107 |
108 | #### Plot 1
109 |
110 | ::: {.card title="Feature Correlation Matrix"}
111 | ```{ojs}
112 |
113 |
114 |
115 | Plot.plot({
116 | marginBottom:70,
117 | marginLeft:100,
118 | //adjust plot dimensions
119 | //adjust plot labels
120 | label: null, //specific for x and y axis labels
121 | style: {fontFamily: "Roboto"},
122 | //customize x and y axis
123 | y: {tickSize:0},
124 | x: {tickSize:0},
125 | //customize legend
126 | color: { scheme: "PuOr", pivot: 0, legend: true, label: "Correlation" },
127 | marks: [
128 | Plot.cell(correlations, { x: "a", y: "b", fill: "correlation" }),
129 | Plot.text(correlations, {
130 | x: "a",
131 | y: "b",
132 | fontSize: 15,
133 | //map using functions to do something additional with data
134 | text: d => d.correlation.toFixed(2), // round corr values to 2nd decimal
135 | fill: d => (Math.abs(d.correlation) > 0.6 ? "white" : "black") //if then function for text color
136 | })
137 | ]
138 | })
139 | ```
140 | :::
141 |
142 | #### Plot 2
143 |
144 | ::: {.card title="Feature Cross Analysis"}
145 |
146 | ```{ojs}
147 | viewof filters = (
148 | Inputs.form({
149 | selectX : Inputs.select(features, {label:"Select X", value:"energy"}),
150 | selectY : Inputs.select(features, {label:"Select Y", value:"acousticness"}),
151 | },
152 | {template})
153 | )
154 | ```
155 |
156 | ```{ojs}
157 |
158 | Plot.plot({
159 | grid: true,
160 | marginBottom:20,
161 | marks: [
162 | Plot.ruleY([0]),
163 | Plot.dot(filtered_songs, {x: filters.selectX,
164 | y: filters.selectY,
165 | title: "track_name",
166 | fill: "#401487",
167 | stroke: "white",
168 | opacity: 0.9,
169 | r: 6})
170 | ]
171 | })
172 |
173 | ```
174 | :::
175 |
176 | # Songs
177 |
178 | ## Table
179 |
180 | ```{ojs}
181 | // | output: false
182 |
183 | // set up DuckDB for querying
184 | db = DuckDBClient.of({
185 | songs: filtered_songs_album,
186 | })
187 |
188 | ```
189 |
190 | ```{ojs}
191 | // | output: false
192 |
193 | //use SQL to apply filters and return a new array
194 | filtered_songs = db.sql`SELECT
195 | track_name
196 | ,album_name
197 | ,round(liveness,2) as liveness
198 | ,round(valence,2) as valence
199 | ,round(energy,2) as energy
200 | ,round(acousticness,2) as acousticness
201 | ,danceability
202 | ,speechiness
203 | ,instrumentalness
204 | FROM songs
205 | WHERE
206 | valence >= ${sliders.valence}
207 | and album_name <> 'NA'
208 | and energy >= ${sliders.energy}
209 | and danceability>= ${sliders.danceability}
210 | and acousticness >= ${sliders.acousticness}`
211 | ```
212 |
213 | ```{ojs}
214 | // | output: false
215 | function heatmap(value, minValue, maxValue) {
216 | // Calculate the color scale based on the value's position
217 | var scale = (value - minValue) / (maxValue - minValue);
218 | var color = getColorFromScale(scale);
219 |
220 | var fontColor = value > 0.3 ? "white" : "black";
221 |
222 | // Create the output div with the scaled background color
223 | return htl.html`
224 |
225 | ${value}
226 |
227 | `;
228 | }
229 |
230 |
231 | function getColorFromScale(scale) {
232 |
233 | var colors = d3.schemeBuPu[7]
234 |
235 | // Calculate the index of the color in the color ramp based on the scale
236 | var index = Math.floor(scale * (colors.length - 1));
237 |
238 | // Get the start and end colors from the color ramp
239 | var startColor = hexToRgb(colors[index]);
240 | var endColor = hexToRgb(colors[index + 1]);
241 |
242 | // Helper function to convert hex color to RGB
243 | function hexToRgb(hex) {
244 | var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
245 | hex = hex.replace(shorthandRegex, function (m, r, g, b) {
246 | return r + r + g + g + b + b;
247 | });
248 |
249 | var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
250 | return result ? [
251 | parseInt(result[1], 16),
252 | parseInt(result[2], 16),
253 | parseInt(result[3], 16)
254 | ] : [255, 255, 255]; // Default to white if invalid hex color
255 | }
256 |
257 | // Calculate the intermediate color based on the scale
258 | var color = startColor.map(function (channel, i) {
259 | var range = endColor[i] - channel;
260 | return Math.round(channel + range * (scale * (colors.length - 1) - index));
261 | });
262 |
263 | // Return the color in RGB format
264 | return "rgb(" + color.join(",") + ")";
265 | }
266 |
267 | ```
268 |
269 | ::: {.card title="Songs"}
270 | ```{ojs}
271 | Inputs.table(filtered_songs, {
272 | //how many rows to display
273 | rows:40,
274 | //format table column names
275 | header: {
276 | album_name: "Album Name",
277 | track_name: "Track",
278 | liveness: "Liveness",
279 | energy: "Energy",
280 | acousticness: "Acousticness",
281 | valence: "Valence",
282 | speechiness: "Speechiness"
283 | },
284 | //adjust column width
285 | width: {
286 | track_name: "25%",
287 | album_name: "15%",
288 | liveness: "12%",
289 | valence: "12%",
290 | acousticness: "12%",
291 | energy: "12%",
292 | speechiness: "12%"
293 | },
294 | //align columns
295 | align: {
296 | liveness: "center",
297 | valence: "center",
298 | energy: "center",
299 | acousticness: "center",
300 | speechiness: "center"
301 | },
302 | //format with additional functions
303 | format: {
304 | liveness: x => heatmap(x,0,1),
305 | energy: x => heatmap(x,0,1),
306 | valence: x => heatmap(x,0,1),
307 | acousticness: x => heatmap(x,0,1),
308 | speechiness: x => heatmap(x,0,1)
309 | }
310 | })
311 | ```
312 | :::
313 |
314 | ```{ojs}
315 | //| output: false
316 | //|
317 | // Template credit: This layout is updated from Martien van Steenbergen @martien/horizontal-inputs
318 |
319 | template = (inputs) =>
320 | htl.html`${Object.values(inputs)}
321 | `
334 |
335 | ```
--------------------------------------------------------------------------------
/dashboard/dashboard_files/libs/bootstrap/bootstrap-icons.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tashapiro/intro-observable/2c44e8f252d49de3a5271a096c458331ae908f01/dashboard/dashboard_files/libs/bootstrap/bootstrap-icons.woff
--------------------------------------------------------------------------------
/dashboard/dashboard_files/libs/clipboard/clipboard.min.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * clipboard.js v2.0.11
3 | * https://clipboardjs.com/
4 | *
5 | * Licensed MIT © Zeno Rocha
6 | */
7 | !function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.ClipboardJS=e():t.ClipboardJS=e()}(this,function(){return n={686:function(t,e,n){"use strict";n.d(e,{default:function(){return b}});var e=n(279),i=n.n(e),e=n(370),u=n.n(e),e=n(817),r=n.n(e);function c(t){try{return document.execCommand(t)}catch(t){return}}var a=function(t){t=r()(t);return c("cut"),t};function o(t,e){var n,o,t=(n=t,o="rtl"===document.documentElement.getAttribute("dir"),(t=document.createElement("textarea")).style.fontSize="12pt",t.style.border="0",t.style.padding="0",t.style.margin="0",t.style.position="absolute",t.style[o?"right":"left"]="-9999px",o=window.pageYOffset||document.documentElement.scrollTop,t.style.top="".concat(o,"px"),t.setAttribute("readonly",""),t.value=n,t);return e.container.appendChild(t),e=r()(t),c("copy"),t.remove(),e}var f=function(t){var e=1 {
4 | // srcts/src/components/_utils.ts
5 | var InputBinding = window.Shiny ? Shiny.InputBinding : class {
6 | };
7 | function registerBinding(inputBindingClass, name) {
8 | if (window.Shiny) {
9 | Shiny.inputBindings.register(new inputBindingClass(), "bslib." + name);
10 | }
11 | }
12 | function hasDefinedProperty(obj, prop) {
13 | return Object.prototype.hasOwnProperty.call(obj, prop) && obj[prop] !== void 0;
14 | }
15 | function getAllFocusableChildren(el) {
16 | const base = [
17 | "a[href]",
18 | "area[href]",
19 | "button",
20 | "details summary",
21 | "input",
22 | "iframe",
23 | "select",
24 | "textarea",
25 | '[contentEditable=""]',
26 | '[contentEditable="true"]',
27 | '[contentEditable="TRUE"]',
28 | "[tabindex]"
29 | ];
30 | const modifiers = [':not([tabindex="-1"])', ":not([disabled])"];
31 | const selectors = base.map((b) => b + modifiers.join(""));
32 | const focusable = el.querySelectorAll(selectors.join(", "));
33 | return Array.from(focusable);
34 | }
35 |
36 | // srcts/src/components/accordion.ts
37 | var AccordionInputBinding = class extends InputBinding {
38 | find(scope) {
39 | return $(scope).find(".accordion.bslib-accordion-input");
40 | }
41 | getValue(el) {
42 | const items = this._getItemInfo(el);
43 | const selected = items.filter((x) => x.isOpen()).map((x) => x.value);
44 | return selected.length === 0 ? null : selected;
45 | }
46 | subscribe(el, callback) {
47 | $(el).on(
48 | "shown.bs.collapse.accordionInputBinding hidden.bs.collapse.accordionInputBinding",
49 | // eslint-disable-next-line @typescript-eslint/no-unused-vars
50 | function(event) {
51 | callback(true);
52 | }
53 | );
54 | }
55 | unsubscribe(el) {
56 | $(el).off(".accordionInputBinding");
57 | }
58 | receiveMessage(el, data) {
59 | const method = data.method;
60 | if (method === "set") {
61 | this._setItems(el, data);
62 | } else if (method === "open") {
63 | this._openItems(el, data);
64 | } else if (method === "close") {
65 | this._closeItems(el, data);
66 | } else if (method === "remove") {
67 | this._removeItem(el, data);
68 | } else if (method === "insert") {
69 | this._insertItem(el, data);
70 | } else if (method === "update") {
71 | this._updateItem(el, data);
72 | } else {
73 | throw new Error(`Method not yet implemented: ${method}`);
74 | }
75 | }
76 | _setItems(el, data) {
77 | const items = this._getItemInfo(el);
78 | const vals = this._getValues(el, items, data.values);
79 | items.forEach((x) => {
80 | vals.indexOf(x.value) > -1 ? x.show() : x.hide();
81 | });
82 | }
83 | _openItems(el, data) {
84 | const items = this._getItemInfo(el);
85 | const vals = this._getValues(el, items, data.values);
86 | items.forEach((x) => {
87 | if (vals.indexOf(x.value) > -1)
88 | x.show();
89 | });
90 | }
91 | _closeItems(el, data) {
92 | const items = this._getItemInfo(el);
93 | const vals = this._getValues(el, items, data.values);
94 | items.forEach((x) => {
95 | if (vals.indexOf(x.value) > -1)
96 | x.hide();
97 | });
98 | }
99 | _insertItem(el, data) {
100 | let targetItem = this._findItem(el, data.target);
101 | if (!targetItem) {
102 | targetItem = data.position === "before" ? el.firstElementChild : el.lastElementChild;
103 | }
104 | const panel = data.panel;
105 | if (targetItem) {
106 | Shiny.renderContent(
107 | targetItem,
108 | panel,
109 | data.position === "before" ? "beforeBegin" : "afterEnd"
110 | );
111 | } else {
112 | Shiny.renderContent(el, panel);
113 | }
114 | if (this._isAutoClosing(el)) {
115 | const val = $(panel.html).attr("data-value");
116 | $(el).find(`[data-value="${val}"] .accordion-collapse`).attr("data-bs-parent", "#" + el.id);
117 | }
118 | }
119 | _removeItem(el, data) {
120 | const targetItems = this._getItemInfo(el).filter(
121 | (x) => data.target.indexOf(x.value) > -1
122 | );
123 | const unbindAll = Shiny == null ? void 0 : Shiny.unbindAll;
124 | targetItems.forEach((x) => {
125 | if (unbindAll)
126 | unbindAll(x.item);
127 | x.item.remove();
128 | });
129 | }
130 | _updateItem(el, data) {
131 | const target = this._findItem(el, data.target);
132 | if (!target) {
133 | throw new Error(
134 | `Unable to find an accordion_panel() with a value of ${data.target}`
135 | );
136 | }
137 | if (hasDefinedProperty(data, "value")) {
138 | target.dataset.value = data.value;
139 | }
140 | if (hasDefinedProperty(data, "body")) {
141 | const body = target.querySelector(".accordion-body");
142 | Shiny.renderContent(body, data.body);
143 | }
144 | const header = target.querySelector(".accordion-header");
145 | if (hasDefinedProperty(data, "title")) {
146 | const title = header.querySelector(".accordion-title");
147 | Shiny.renderContent(title, data.title);
148 | }
149 | if (hasDefinedProperty(data, "icon")) {
150 | const icon = header.querySelector(
151 | ".accordion-button > .accordion-icon"
152 | );
153 | Shiny.renderContent(icon, data.icon);
154 | }
155 | }
156 | _getItemInfo(el) {
157 | const items = Array.from(
158 | el.querySelectorAll(":scope > .accordion-item")
159 | );
160 | return items.map((x) => this._getSingleItemInfo(x));
161 | }
162 | _getSingleItemInfo(x) {
163 | const collapse = x.querySelector(".accordion-collapse");
164 | const isOpen = () => $(collapse).hasClass("show");
165 | return {
166 | item: x,
167 | value: x.dataset.value,
168 | isOpen,
169 | show: () => {
170 | if (!isOpen())
171 | $(collapse).collapse("show");
172 | },
173 | hide: () => {
174 | if (isOpen())
175 | $(collapse).collapse("hide");
176 | }
177 | };
178 | }
179 | _getValues(el, items, values) {
180 | let vals = values !== true ? values : items.map((x) => x.value);
181 | const autoclose = this._isAutoClosing(el);
182 | if (autoclose) {
183 | vals = vals.slice(vals.length - 1, vals.length);
184 | }
185 | return vals;
186 | }
187 | _findItem(el, value) {
188 | return el.querySelector(`[data-value="${value}"]`);
189 | }
190 | _isAutoClosing(el) {
191 | return el.classList.contains("autoclose");
192 | }
193 | };
194 | registerBinding(AccordionInputBinding, "accordion");
195 |
196 | // srcts/src/components/_shinyResizeObserver.ts
197 | var ShinyResizeObserver = class {
198 | /**
199 | * Watch containers for size changes and ensure that Shiny outputs and
200 | * htmlwidgets within resize appropriately.
201 | *
202 | * @details
203 | * The ShinyResizeObserver is used to watch the containers, such as Sidebars
204 | * and Cards for size changes, in particular when the sidebar state is toggled
205 | * or the card body is expanded full screen. It performs two primary tasks:
206 | *
207 | * 1. Dispatches a `resize` event on the window object. This is necessary to
208 | * ensure that Shiny outputs resize appropriately. In general, the window
209 | * resizing is throttled and the output update occurs when the transition
210 | * is complete.
211 | * 2. If an output with a resize method on the output binding is detected, we
212 | * directly call the `.onResize()` method of the binding. This ensures that
213 | * htmlwidgets transition smoothly. In static mode, htmlwidgets does this
214 | * already.
215 | *
216 | * @note
217 | * This resize observer also handles race conditions in some complex
218 | * fill-based layouts with multiple outputs (e.g., plotly), where shiny
219 | * initializes with the correct sizing, but in-between the 1st and last
220 | * renderValue(), the size of the output containers can change, meaning every
221 | * output but the 1st gets initialized with the wrong size during their
222 | * renderValue(). Then, after the render phase, shiny won't know to trigger a
223 | * resize since all the widgets will return to their original size (and thus,
224 | * Shiny thinks there isn't any resizing to do). The resize observer works
225 | * around this by ensuring that the output is resized whenever its container
226 | * size changes.
227 | * @constructor
228 | */
229 | constructor() {
230 | this.resizeObserverEntries = [];
231 | this.resizeObserver = new ResizeObserver((entries) => {
232 | const resizeEvent = new Event("resize");
233 | window.dispatchEvent(resizeEvent);
234 | if (!window.Shiny)
235 | return;
236 | const resized = [];
237 | for (const entry of entries) {
238 | if (!(entry.target instanceof HTMLElement))
239 | continue;
240 | if (!entry.target.querySelector(".shiny-bound-output"))
241 | continue;
242 | entry.target.querySelectorAll(".shiny-bound-output").forEach((el) => {
243 | if (resized.includes(el))
244 | return;
245 | const { binding, onResize } = $(el).data("shinyOutputBinding");
246 | if (!binding || !binding.resize)
247 | return;
248 | const owner = el.shinyResizeObserver;
249 | if (owner && owner !== this)
250 | return;
251 | if (!owner)
252 | el.shinyResizeObserver = this;
253 | onResize(el);
254 | resized.push(el);
255 | if (!el.classList.contains("shiny-plot-output"))
256 | return;
257 | const img = el.querySelector(
258 | 'img:not([width="100%"])'
259 | );
260 | if (img)
261 | img.setAttribute("width", "100%");
262 | });
263 | }
264 | });
265 | }
266 | /**
267 | * Observe an element for size changes.
268 | * @param {HTMLElement} el - The element to observe.
269 | */
270 | observe(el) {
271 | this.resizeObserver.observe(el);
272 | this.resizeObserverEntries.push(el);
273 | }
274 | /**
275 | * Stop observing an element for size changes.
276 | * @param {HTMLElement} el - The element to stop observing.
277 | */
278 | unobserve(el) {
279 | const idxEl = this.resizeObserverEntries.indexOf(el);
280 | if (idxEl < 0)
281 | return;
282 | this.resizeObserver.unobserve(el);
283 | this.resizeObserverEntries.splice(idxEl, 1);
284 | }
285 | /**
286 | * This method checks that we're not continuing to watch elements that no
287 | * longer exist in the DOM. If any are found, we stop observing them and
288 | * remove them from our array of observed elements.
289 | *
290 | * @private
291 | * @static
292 | */
293 | flush() {
294 | this.resizeObserverEntries.forEach((el) => {
295 | if (!document.body.contains(el))
296 | this.unobserve(el);
297 | });
298 | }
299 | };
300 |
301 | // srcts/src/components/card.ts
302 | var _Card = class {
303 | /**
304 | * Creates an instance of a bslib Card component.
305 | *
306 | * @constructor
307 | * @param {HTMLElement} card
308 | */
309 | constructor(card) {
310 | var _a;
311 | card.removeAttribute(_Card.attr.ATTR_INIT);
312 | (_a = card.querySelector(`script[${_Card.attr.ATTR_INIT}]`)) == null ? void 0 : _a.remove();
313 | this.card = card;
314 | _Card.instanceMap.set(card, this);
315 | _Card.shinyResizeObserver.observe(this.card);
316 | this._addEventListeners();
317 | this.overlay = this._createOverlay();
318 | this._exitFullScreenOnEscape = this._exitFullScreenOnEscape.bind(this);
319 | this._trapFocusExit = this._trapFocusExit.bind(this);
320 | }
321 | /**
322 | * Enter the card's full screen mode, either programmatically or via an event
323 | * handler. Full screen mode is activated by adding a class to the card that
324 | * positions it absolutely and expands it to fill the viewport. In addition,
325 | * we add a full screen overlay element behind the card and we trap focus in
326 | * the expanded card while in full screen mode.
327 | *
328 | * @param {?Event} [event]
329 | */
330 | enterFullScreen(event) {
331 | var _a;
332 | if (event)
333 | event.preventDefault();
334 | document.addEventListener("keydown", this._exitFullScreenOnEscape, false);
335 | document.addEventListener("keydown", this._trapFocusExit, true);
336 | this.card.setAttribute(_Card.attr.ATTR_FULL_SCREEN, "true");
337 | document.body.classList.add(_Card.attr.CLASS_HAS_FULL_SCREEN);
338 | this.card.insertAdjacentElement("beforebegin", this.overlay.container);
339 | if (!this.card.contains(document.activeElement) || ((_a = document.activeElement) == null ? void 0 : _a.classList.contains(
340 | _Card.attr.CLASS_FULL_SCREEN_ENTER
341 | ))) {
342 | this.card.setAttribute("tabindex", "-1");
343 | this.card.focus();
344 | }
345 | }
346 | /**
347 | * Exit full screen mode. This removes the full screen overlay element,
348 | * removes the full screen class from the card, and removes the keyboard event
349 | * listeners that were added when entering full screen mode.
350 | */
351 | exitFullScreen() {
352 | document.removeEventListener(
353 | "keydown",
354 | this._exitFullScreenOnEscape,
355 | false
356 | );
357 | document.removeEventListener("keydown", this._trapFocusExit, true);
358 | this.overlay.container.remove();
359 | this.card.setAttribute(_Card.attr.ATTR_FULL_SCREEN, "false");
360 | this.card.removeAttribute("tabindex");
361 | document.body.classList.remove(_Card.attr.CLASS_HAS_FULL_SCREEN);
362 | }
363 | /**
364 | * Adds general card-specific event listeners.
365 | * @private
366 | */
367 | _addEventListeners() {
368 | const btnFullScreen = this.card.querySelector(
369 | `:scope > * > .${_Card.attr.CLASS_FULL_SCREEN_ENTER}`
370 | );
371 | if (!btnFullScreen)
372 | return;
373 | btnFullScreen.addEventListener("click", (ev) => this.enterFullScreen(ev));
374 | }
375 | /**
376 | * An event handler to exit full screen mode when the Escape key is pressed.
377 | * @private
378 | * @param {KeyboardEvent} event
379 | */
380 | _exitFullScreenOnEscape(event) {
381 | if (!(event.target instanceof HTMLElement))
382 | return;
383 | const selOpenSelectInput = ["select[open]", "input[aria-expanded='true']"];
384 | if (event.target.matches(selOpenSelectInput.join(", ")))
385 | return;
386 | if (event.key === "Escape") {
387 | this.exitFullScreen();
388 | }
389 | }
390 | /**
391 | * An event handler to trap focus within the card when in full screen mode.
392 | *
393 | * @description
394 | * This keyboard event handler ensures that tab focus stays within the card
395 | * when in full screen mode. When the card is first expanded,
396 | * we move focus to the card element itself. If focus somehow leaves the card,
397 | * we returns focus to the card container.
398 | *
399 | * Within the card, we handle only tabbing from the close anchor or the last
400 | * focusable element and only when tab focus would have otherwise left the
401 | * card. In those cases, we cycle focus to the last focusable element or back
402 | * to the anchor. If the card doesn't have any focusable elements, we move
403 | * focus to the close anchor.
404 | *
405 | * @note
406 | * Because the card contents may change, we check for focusable elements
407 | * every time the handler is called.
408 | *
409 | * @private
410 | * @param {KeyboardEvent} event
411 | */
412 | _trapFocusExit(event) {
413 | if (!(event instanceof KeyboardEvent))
414 | return;
415 | if (event.key !== "Tab")
416 | return;
417 | const isFocusedContainer = event.target === this.card;
418 | const isFocusedAnchor = event.target === this.overlay.anchor;
419 | const isFocusedWithin = this.card.contains(event.target);
420 | const stopEvent = () => {
421 | event.preventDefault();
422 | event.stopImmediatePropagation();
423 | };
424 | if (!(isFocusedWithin || isFocusedContainer || isFocusedAnchor)) {
425 | stopEvent();
426 | this.card.focus();
427 | return;
428 | }
429 | const focusableElements = getAllFocusableChildren(this.card).filter(
430 | (el) => !el.classList.contains(_Card.attr.CLASS_FULL_SCREEN_ENTER)
431 | );
432 | const hasFocusableElements = focusableElements.length > 0;
433 | if (!hasFocusableElements) {
434 | stopEvent();
435 | this.overlay.anchor.focus();
436 | return;
437 | }
438 | if (isFocusedContainer)
439 | return;
440 | const lastFocusable = focusableElements[focusableElements.length - 1];
441 | const isFocusedLast = event.target === lastFocusable;
442 | if (isFocusedAnchor && event.shiftKey) {
443 | stopEvent();
444 | lastFocusable.focus();
445 | return;
446 | }
447 | if (isFocusedLast && !event.shiftKey) {
448 | stopEvent();
449 | this.overlay.anchor.focus();
450 | return;
451 | }
452 | }
453 | /**
454 | * Creates the full screen overlay.
455 | * @private
456 | * @returns {CardFullScreenOverlay}
457 | */
458 | _createOverlay() {
459 | const container = document.createElement("div");
460 | container.id = _Card.attr.ID_FULL_SCREEN_OVERLAY;
461 | container.onclick = this.exitFullScreen.bind(this);
462 | const anchor = this._createOverlayCloseAnchor();
463 | container.appendChild(anchor);
464 | return { container, anchor };
465 | }
466 | /**
467 | * Creates the anchor element used to exit the full screen mode.
468 | * @private
469 | * @returns {HTMLAnchorElement}
470 | */
471 | _createOverlayCloseAnchor() {
472 | const anchor = document.createElement("a");
473 | anchor.classList.add(_Card.attr.CLASS_FULL_SCREEN_EXIT);
474 | anchor.tabIndex = 0;
475 | anchor.onclick = () => this.exitFullScreen();
476 | anchor.onkeydown = (ev) => {
477 | if (ev.key === "Enter" || ev.key === " ") {
478 | this.exitFullScreen();
479 | }
480 | };
481 | anchor.innerHTML = this._overlayCloseHtml();
482 | return anchor;
483 | }
484 | /**
485 | * Returns the HTML for the close icon.
486 | * @private
487 | * @returns {string}
488 | */
489 | _overlayCloseHtml() {
490 | return "Close ";
491 | }
492 | /**
493 | * Returns the card instance associated with the given element, if any.
494 | * @public
495 | * @static
496 | * @param {HTMLElement} el
497 | * @returns {(Card | undefined)}
498 | */
499 | static getInstance(el) {
500 | return _Card.instanceMap.get(el);
501 | }
502 | /**
503 | * Initializes all cards that require initialization on the page, or schedules
504 | * initialization if the DOM is not yet ready.
505 | * @public
506 | * @static
507 | * @param {boolean} [flushResizeObserver=true]
508 | */
509 | static initializeAllCards(flushResizeObserver = true) {
510 | if (document.readyState === "loading") {
511 | if (!_Card.onReadyScheduled) {
512 | _Card.onReadyScheduled = true;
513 | document.addEventListener("DOMContentLoaded", () => {
514 | _Card.initializeAllCards(false);
515 | });
516 | }
517 | return;
518 | }
519 | if (flushResizeObserver) {
520 | _Card.shinyResizeObserver.flush();
521 | }
522 | const initSelector = `.${_Card.attr.CLASS_CARD}[${_Card.attr.ATTR_INIT}]`;
523 | if (!document.querySelector(initSelector)) {
524 | return;
525 | }
526 | const cards = document.querySelectorAll(initSelector);
527 | cards.forEach((card) => new _Card(card));
528 | }
529 | };
530 | var Card = _Card;
531 | /**
532 | * Key bslib-specific classes and attributes used by the card component.
533 | * @private
534 | * @static
535 | * @type {{ ATTR_INIT: string; CLASS_CARD: string; CLASS_FULL_SCREEN: string; CLASS_HAS_FULL_SCREEN: string; CLASS_FULL_SCREEN_ENTER: string; CLASS_FULL_SCREEN_EXIT: string; ID_FULL_SCREEN_OVERLAY: string; }}
536 | */
537 | Card.attr = {
538 | // eslint-disable-next-line @typescript-eslint/naming-convention
539 | ATTR_INIT: "data-bslib-card-init",
540 | // eslint-disable-next-line @typescript-eslint/naming-convention
541 | CLASS_CARD: "bslib-card",
542 | // eslint-disable-next-line @typescript-eslint/naming-convention
543 | ATTR_FULL_SCREEN: "data-full-screen",
544 | // eslint-disable-next-line @typescript-eslint/naming-convention
545 | CLASS_HAS_FULL_SCREEN: "bslib-has-full-screen",
546 | // eslint-disable-next-line @typescript-eslint/naming-convention
547 | CLASS_FULL_SCREEN_ENTER: "bslib-full-screen-enter",
548 | // eslint-disable-next-line @typescript-eslint/naming-convention
549 | CLASS_FULL_SCREEN_EXIT: "bslib-full-screen-exit",
550 | // eslint-disable-next-line @typescript-eslint/naming-convention
551 | ID_FULL_SCREEN_OVERLAY: "bslib-full-screen-overlay"
552 | };
553 | /**
554 | * A Shiny-specific resize observer that ensures Shiny outputs in within the
555 | * card resize appropriately.
556 | * @private
557 | * @type {ShinyResizeObserver}
558 | * @static
559 | */
560 | Card.shinyResizeObserver = new ShinyResizeObserver();
561 | /**
562 | * The registry of card instances and their associated DOM elements.
563 | * @private
564 | * @static
565 | * @type {WeakMap}
566 | */
567 | Card.instanceMap = /* @__PURE__ */ new WeakMap();
568 | /**
569 | * If cards are initialized before the DOM is ready, we re-schedule the
570 | * initialization to occur on DOMContentLoaded.
571 | * @private
572 | * @static
573 | * @type {boolean}
574 | */
575 | Card.onReadyScheduled = false;
576 | window.bslib = window.bslib || {};
577 | window.bslib.Card = Card;
578 |
579 | // srcts/src/components/sidebar.ts
580 | var _Sidebar = class {
581 | /**
582 | * Creates an instance of a collapsible bslib Sidebar.
583 | * @constructor
584 | * @param {HTMLElement} container
585 | */
586 | constructor(container) {
587 | var _a;
588 | _Sidebar.instanceMap.set(container, this);
589 | this.layout = {
590 | container,
591 | main: container.querySelector(":scope > .main"),
592 | sidebar: container.querySelector(":scope > .sidebar"),
593 | toggle: container.querySelector(
594 | ":scope > .collapse-toggle"
595 | )
596 | };
597 | const sideAccordion = this.layout.sidebar.querySelector(
598 | ":scope > .sidebar-content > .accordion"
599 | );
600 | if (sideAccordion) {
601 | (_a = sideAccordion == null ? void 0 : sideAccordion.parentElement) == null ? void 0 : _a.classList.add("has-accordion");
602 | sideAccordion.classList.add("accordion-flush");
603 | }
604 | if (this.layout.toggle) {
605 | this._initEventListeners();
606 | this._initSidebarCounters();
607 | this._initDesktop();
608 | }
609 | _Sidebar.shinyResizeObserver.observe(this.layout.main);
610 | container.removeAttribute("data-bslib-sidebar-init");
611 | const initScript = container.querySelector(
612 | ":scope > script[data-bslib-sidebar-init]"
613 | );
614 | if (initScript) {
615 | container.removeChild(initScript);
616 | }
617 | }
618 | /**
619 | * Read the current state of the sidebar. Note that, when calling this method,
620 | * the sidebar may be transitioning into the state returned by this method.
621 | *
622 | * @description
623 | * The sidebar state works as follows, starting from the open state. When the
624 | * sidebar is closed:
625 | * 1. We add both the `COLLAPSE` and `TRANSITIONING` classes to the sidebar.
626 | * 2. The sidebar collapse begins to animate. On desktop devices, and where it
627 | * is supported, we transition the `grid-template-columns` property of the
628 | * sidebar layout. On mobile, the sidebar is hidden immediately. In both
629 | * cases, the collapse icon rotates and we use this rotation to determine
630 | * when the transition is complete.
631 | * 3. If another sidebar state toggle is requested while closing the sidebar,
632 | * we remove the `COLLAPSE` class and the animation immediately starts to
633 | * reverse.
634 | * 4. When the `transition` is complete, we remove the `TRANSITIONING` class.
635 | * @readonly
636 | * @type {boolean}
637 | */
638 | get isClosed() {
639 | return this.layout.container.classList.contains(_Sidebar.classes.COLLAPSE);
640 | }
641 | /**
642 | * Given a sidebar container, return the Sidebar instance associated with it.
643 | * @public
644 | * @static
645 | * @param {HTMLElement} el
646 | * @returns {(Sidebar | undefined)}
647 | */
648 | static getInstance(el) {
649 | return _Sidebar.instanceMap.get(el);
650 | }
651 | /**
652 | * Initialize all collapsible sidebars on the page.
653 | * @public
654 | * @static
655 | * @param {boolean} [flushResizeObserver=true] When `true`, we remove
656 | * non-existent elements from the ResizeObserver. This is required
657 | * periodically to prevent memory leaks. To avoid over-checking, we only flush
658 | * the ResizeObserver when initializing sidebars after page load.
659 | */
660 | static initCollapsibleAll(flushResizeObserver = true) {
661 | if (document.readyState === "loading") {
662 | if (!_Sidebar.onReadyScheduled) {
663 | _Sidebar.onReadyScheduled = true;
664 | document.addEventListener("DOMContentLoaded", () => {
665 | _Sidebar.initCollapsibleAll(false);
666 | });
667 | }
668 | return;
669 | }
670 | const initSelector = `.${_Sidebar.classes.LAYOUT}[data-bslib-sidebar-init]`;
671 | if (!document.querySelector(initSelector)) {
672 | return;
673 | }
674 | if (flushResizeObserver)
675 | _Sidebar.shinyResizeObserver.flush();
676 | const containers = document.querySelectorAll(initSelector);
677 | containers.forEach((container) => new _Sidebar(container));
678 | }
679 | /**
680 | * Initialize event listeners for the sidebar toggle button.
681 | * @private
682 | */
683 | _initEventListeners() {
684 | var _a;
685 | const { toggle } = this.layout;
686 | toggle.addEventListener("click", (ev) => {
687 | ev.preventDefault();
688 | this.toggle("toggle");
689 | });
690 | (_a = toggle.querySelector(".collapse-icon")) == null ? void 0 : _a.addEventListener("transitionend", () => this._finalizeState());
691 | }
692 | /**
693 | * Initialize nested sidebar counters.
694 | *
695 | * @description
696 | * This function walks up the DOM tree, adding CSS variables to each direct
697 | * parent sidebar layout that count the layout's position in the stack of
698 | * nested layouts. We use these counters to keep the collapse toggles from
699 | * overlapping. Note that always-open sidebars that don't have collapse
700 | * toggles break the chain of nesting.
701 | * @private
702 | */
703 | _initSidebarCounters() {
704 | const { container } = this.layout;
705 | const selectorChildLayouts = `.${_Sidebar.classes.LAYOUT}> .main > .${_Sidebar.classes.LAYOUT}:not([data-bslib-sidebar-open="always"])`;
706 | const isInnermostLayout = container.querySelector(selectorChildLayouts) === null;
707 | if (!isInnermostLayout) {
708 | return;
709 | }
710 | function nextSidebarParent(el) {
711 | el = el ? el.parentElement : null;
712 | if (el && el.classList.contains("main")) {
713 | el = el.parentElement;
714 | }
715 | if (el && el.classList.contains(_Sidebar.classes.LAYOUT)) {
716 | return el;
717 | }
718 | return null;
719 | }
720 | const layouts = [container];
721 | let parent = nextSidebarParent(container);
722 | while (parent) {
723 | layouts.unshift(parent);
724 | parent = nextSidebarParent(parent);
725 | }
726 | const count = { left: 0, right: 0 };
727 | layouts.forEach(function(x, i) {
728 | x.style.setProperty("--bslib-sidebar-counter", i.toString());
729 | const isRight = x.classList.contains("sidebar-right");
730 | const thisCount = isRight ? count.right++ : count.left++;
731 | x.style.setProperty(
732 | "--bslib-sidebar-overlap-counter",
733 | thisCount.toString()
734 | );
735 | });
736 | }
737 | /**
738 | * Initialize the sidebar's initial state when `open = "desktop"`.
739 | * @private
740 | */
741 | _initDesktop() {
742 | var _a;
743 | const { container } = this.layout;
744 | if (((_a = container.dataset.bslibSidebarOpen) == null ? void 0 : _a.trim()) !== "desktop") {
745 | return;
746 | }
747 | const initCollapsed = window.getComputedStyle(container).getPropertyValue("--bslib-sidebar-js-init-collapsed");
748 | if (initCollapsed.trim() === "true") {
749 | this.toggle("close");
750 | }
751 | }
752 | /**
753 | * Toggle the sidebar's open/closed state.
754 | * @public
755 | * @param {SidebarToggleMethod | undefined} method Whether to `"open"`,
756 | * `"close"` or `"toggle"` the sidebar. If `.toggle()` is called without an
757 | * argument, it will toggle the sidebar's state.
758 | */
759 | toggle(method) {
760 | if (typeof method === "undefined") {
761 | method = "toggle";
762 | }
763 | const { container, sidebar } = this.layout;
764 | const isClosed = this.isClosed;
765 | if (["open", "close", "toggle"].indexOf(method) === -1) {
766 | throw new Error(`Unknown method ${method}`);
767 | }
768 | if (method === "toggle") {
769 | method = isClosed ? "open" : "close";
770 | }
771 | if (isClosed && method === "close" || !isClosed && method === "open") {
772 | return;
773 | }
774 | if (method === "open") {
775 | sidebar.hidden = false;
776 | }
777 | container.classList.add(_Sidebar.classes.TRANSITIONING);
778 | container.classList.toggle(_Sidebar.classes.COLLAPSE);
779 | }
780 | /**
781 | * When the sidebar open/close transition ends, finalize the sidebar's state.
782 | * @private
783 | */
784 | _finalizeState() {
785 | const { container, sidebar, toggle } = this.layout;
786 | container.classList.remove(_Sidebar.classes.TRANSITIONING);
787 | sidebar.hidden = this.isClosed;
788 | toggle.setAttribute("aria-expanded", this.isClosed ? "false" : "true");
789 | const event = new CustomEvent("bslib.sidebar", {
790 | bubbles: true,
791 | detail: { open: !this.isClosed }
792 | });
793 | sidebar.dispatchEvent(event);
794 | $(sidebar).trigger("toggleCollapse.sidebarInputBinding");
795 | $(sidebar).trigger(this.isClosed ? "hidden" : "shown");
796 | }
797 | };
798 | var Sidebar = _Sidebar;
799 | /**
800 | * A Shiny-specific resize observer that ensures Shiny outputs in the main
801 | * content areas of the sidebar resize appropriately.
802 | * @private
803 | * @type {ShinyResizeObserver}
804 | * @static
805 | */
806 | Sidebar.shinyResizeObserver = new ShinyResizeObserver();
807 | /**
808 | * Static classes related to the sidebar layout or state.
809 | * @public
810 | * @static
811 | * @readonly
812 | * @type {{ LAYOUT: string; COLLAPSE: string; TRANSITIONING: string; }}
813 | */
814 | Sidebar.classes = {
815 | // eslint-disable-next-line @typescript-eslint/naming-convention
816 | LAYOUT: "bslib-sidebar-layout",
817 | // eslint-disable-next-line @typescript-eslint/naming-convention
818 | COLLAPSE: "sidebar-collapsed",
819 | // eslint-disable-next-line @typescript-eslint/naming-convention
820 | TRANSITIONING: "transitioning"
821 | };
822 | /**
823 | * If sidebars are initialized before the DOM is ready, we re-schedule the
824 | * initialization to occur on DOMContentLoaded.
825 | * @private
826 | * @static
827 | * @type {boolean}
828 | */
829 | Sidebar.onReadyScheduled = false;
830 | /**
831 | * A map of initialized sidebars to their respective Sidebar instances.
832 | * @private
833 | * @static
834 | * @type {WeakMap}
835 | */
836 | Sidebar.instanceMap = /* @__PURE__ */ new WeakMap();
837 | var SidebarInputBinding = class extends InputBinding {
838 | find(scope) {
839 | return $(scope).find(`.${Sidebar.classes.LAYOUT} > .bslib-sidebar-input`);
840 | }
841 | getValue(el) {
842 | const sb = Sidebar.getInstance(el.parentElement);
843 | if (!sb)
844 | return false;
845 | return !sb.isClosed;
846 | }
847 | setValue(el, value) {
848 | const method = value ? "open" : "close";
849 | this.receiveMessage(el, { method });
850 | }
851 | subscribe(el, callback) {
852 | $(el).on(
853 | "toggleCollapse.sidebarInputBinding",
854 | // eslint-disable-next-line @typescript-eslint/no-unused-vars
855 | function(event) {
856 | callback(true);
857 | }
858 | );
859 | }
860 | unsubscribe(el) {
861 | $(el).off(".sidebarInputBinding");
862 | }
863 | receiveMessage(el, data) {
864 | const sb = Sidebar.getInstance(el.parentElement);
865 | if (sb)
866 | sb.toggle(data.method);
867 | }
868 | };
869 | registerBinding(SidebarInputBinding, "sidebar");
870 | window.bslib = window.bslib || {};
871 | window.bslib.Sidebar = Sidebar;
872 |
873 | // srcts/src/components/_shinyAddCustomMessageHandlers.ts
874 | function shinyAddCustomMessageHandlers(handlers) {
875 | if (!window.Shiny) {
876 | return;
877 | }
878 | for (const [name, handler] of Object.entries(handlers)) {
879 | Shiny.addCustomMessageHandler(name, handler);
880 | }
881 | }
882 |
883 | // srcts/src/components/index.ts
884 | var bslibMessageHandlers = {
885 | // eslint-disable-next-line @typescript-eslint/naming-convention
886 | "bslib.toggle-input-binary": (msg) => {
887 | const el = document.getElementById(msg.id);
888 | if (!el) {
889 | console.warn("[bslib.toggle-input-binary] No element found", msg);
890 | }
891 | const binding = $(el).data("shiny-input-binding");
892 | if (!(binding instanceof InputBinding)) {
893 | console.warn("[bslib.toggle-input-binary] No input binding found", msg);
894 | return;
895 | }
896 | let value = msg.value;
897 | if (typeof value === "undefined") {
898 | value = !binding.getValue(el);
899 | }
900 | binding.receiveMessage(el, { value });
901 | }
902 | };
903 | if (window.Shiny) {
904 | shinyAddCustomMessageHandlers(bslibMessageHandlers);
905 | }
906 | function insertSvgGradient() {
907 | const temp = document.createElement("div");
908 | temp.innerHTML = `
909 | `;
921 | document.body.appendChild(temp.children[0]);
922 | }
923 | if (document.readyState === "complete") {
924 | insertSvgGradient();
925 | } else {
926 | document.addEventListener("DOMContentLoaded", insertSvgGradient);
927 | }
928 | })();
929 |
930 |
--------------------------------------------------------------------------------
/dashboard/dashboard_files/libs/quarto-dashboard/quarto-dashboard.js:
--------------------------------------------------------------------------------
1 | const fillDivClasseses = ["widget-subarea", "lm-Widget", "leaflet-container"];
2 |
3 | function requiresFill(el) {
4 | if (el.tagName === "DIV") {
5 | return fillDivClasseses.some((cls) => {
6 | return el.classList.contains(cls);
7 | });
8 | }
9 | return false;
10 | }
11 |
12 | function ensureWidgetFills(el) {
13 | if (!el.classList.contains("html-fill-item")) {
14 | el.classList.add("html-fill-item");
15 | }
16 |
17 | if (!el.classList.contains("html-fill-container")) {
18 | el.classList.add("html-fill-container");
19 | }
20 | }
21 |
22 | function ensureWidgetsFill() {
23 | // Find any jupyter widget containers and keep an eye on them
24 | const widgetNodes = document.querySelectorAll(".widget-subarea");
25 | for (const widgetEl of widgetNodes) {
26 | ensureWidgetFills(widgetEl);
27 | }
28 | }
29 |
30 | window.document.addEventListener("DOMContentLoaded", function (_event) {
31 | ensureWidgetsFill();
32 |
33 | // Fixup any sharing links that require urls
34 | // Append url to any sharing urls
35 | const sharingLinks = window.document.querySelectorAll(
36 | "a.quarto-dashboard-link"
37 | );
38 | for (let i = 0; i < sharingLinks.length; i++) {
39 | const sharingLink = sharingLinks[i];
40 | const href = sharingLink.getAttribute("href");
41 | if (href) {
42 | sharingLink.setAttribute(
43 | "href",
44 | href.replace("|url|", window.location.href)
45 | );
46 | }
47 | }
48 |
49 | // Try to process the hash and activate a tab
50 | const hash = window.decodeURIComponent(window.location.hash);
51 | if (hash.length > 0) {
52 | QuartoDashboardUtils.showPage(hash);
53 | }
54 |
55 | // navigate to a tab when the history changes
56 | window.addEventListener("popstate", function (e) {
57 | const hash = window.decodeURIComponent(window.location.hash);
58 | QuartoDashboardUtils.showPage(hash);
59 | });
60 |
61 | // Hook tabs and use that to update history / active tabs
62 | const navItems = document.querySelectorAll(".navbar .nav-item .nav-link");
63 | for (const navItem of navItems) {
64 | const linkHref = navItem.getAttribute("href");
65 | navItem.addEventListener("click", () => {
66 | const baseUrl = QuartoDashboardUtils.urlWithoutHash(window.location.href);
67 | const hash = QuartoDashboardUtils.urlHash(linkHref);
68 | const href = baseUrl + hash;
69 | QuartoDashboardUtils.setLocation(href);
70 | return false;
71 | });
72 | }
73 |
74 | // Hook links in the body so users can link to pages
75 | const linkEls = document.querySelectorAll(
76 | ".quarto-dashboard-content a:not(.nav-link)"
77 | );
78 | for (const linkEl of linkEls) {
79 | const linkHref = linkEl.getAttribute("href");
80 | linkEl.addEventListener("click", () => {
81 | QuartoDashboardUtils.showPage(linkHref);
82 | return false;
83 | });
84 | }
85 | const sidebar = window.document.querySelector(
86 | ".quarto-dashboard-content .bslib-sidebar-layout"
87 | );
88 | let prevWidth = window.document.body.clientWidth;
89 | const sidebarCollapseClass = "sidebar-collapsed";
90 | if (sidebar) {
91 | const resizeObserver = new ResizeObserver(
92 | throttle(function () {
93 | const clientWidth = window.document.body.clientWidth;
94 | if (prevWidth !== clientWidth) {
95 | if (clientWidth <= 576) {
96 | // Hide the sidebar
97 | if (!sidebar.classList.contains(sidebarCollapseClass)) {
98 | sidebar.classList.add(sidebarCollapseClass);
99 | }
100 | } else {
101 | // Show the sidebar
102 | if (sidebar.classList.contains(sidebarCollapseClass)) {
103 | sidebar.classList.remove(sidebarCollapseClass);
104 | }
105 | }
106 | prevWidth = clientWidth;
107 | }
108 | }, 2)
109 | );
110 | resizeObserver.observe(window.document.body);
111 | }
112 |
113 | const observer = new MutationObserver(function (mutations) {
114 | mutations.forEach(function (mutation) {
115 | mutation.addedNodes.forEach(function (addedNode) {
116 | if (requiresFill(addedNode)) {
117 | ensureWidgetFills(addedNode);
118 | }
119 | });
120 | });
121 | });
122 | observer.observe(document.body, { childList: true, subtree: true });
123 | });
124 |
125 | // utils
126 | window.QuartoDashboardUtils = {
127 | setLocation: function (href) {
128 | if (history && history.pushState) {
129 | history.pushState({}, null, href);
130 | // post "hashchange" for tools looking for it
131 | if (window.parent?.postMessage) {
132 | window.parent.postMessage(
133 | {
134 | type: "hashchange",
135 | href: window.location.href,
136 | },
137 | "*"
138 | );
139 | }
140 | } else {
141 | window.location.replace(href);
142 | }
143 | setTimeout(function () {
144 | window.scrollTo(0, 0);
145 | }, 10);
146 | },
147 | isPage: function (hash) {
148 | const tabPaneEl = document.querySelector(`.dashboard-page.tab-pane${hash}`);
149 | return tabPaneEl !== null;
150 | },
151 | showPage: function (hash) {
152 | // If the hash is empty, just select the first tab and activate that
153 | if (hash === "") {
154 | const firstTabPaneEl = document.querySelector(".dashboard-page.tab-pane");
155 | if (firstTabPaneEl !== null) {
156 | hash = `#${firstTabPaneEl.id}`;
157 | }
158 | }
159 |
160 | // Find the tab and activate it
161 | const tabNodes = document.querySelectorAll(".navbar .nav-item .nav-link");
162 | for (const tabEl of tabNodes) {
163 | const target = tabEl.getAttribute("data-bs-target");
164 | if (target === hash) {
165 | tabEl.classList.add("active");
166 | } else {
167 | tabEl.classList.remove("active");
168 | }
169 | }
170 |
171 | // Find the tabpanes and activate the hash tab
172 | const tabPaneNodes = document.querySelectorAll(".dashboard-page.tab-pane");
173 | for (const tabPaneEl of tabPaneNodes) {
174 | if (`#${tabPaneEl.id}` === hash) {
175 | tabPaneEl.classList.add("active");
176 | } else {
177 | tabPaneEl.classList.remove("active");
178 | }
179 | }
180 | },
181 | showLinkedValue: function (href) {
182 | // check for a page link
183 | if (this.isPage(href)) {
184 | this.showPage(href);
185 | } else {
186 | window.open(href);
187 | }
188 | },
189 | urlWithoutHash: function (url) {
190 | const hashLoc = url.indexOf("#");
191 | if (hashLoc != -1) return url.substring(0, hashLoc);
192 | else return url;
193 | },
194 | urlHash: function (url) {
195 | const hashLoc = url.indexOf("#");
196 | if (hashLoc != -1) return url.substring(hashLoc);
197 | else return "";
198 | },
199 | };
200 |
201 | function throttle(func, wait) {
202 | let waiting = false;
203 | return function () {
204 | if (!waiting) {
205 | func.apply(this, arguments);
206 | waiting = true;
207 | setTimeout(function () {
208 | waiting = false;
209 | }, wait);
210 | }
211 | };
212 | }
213 |
--------------------------------------------------------------------------------
/dashboard/dashboard_files/libs/quarto-html/anchor.min.js:
--------------------------------------------------------------------------------
1 | // @license magnet:?xt=urn:btih:d3d9a9a6595521f9666a5e94cc830dab83b65699&dn=expat.txt Expat
2 | //
3 | // AnchorJS - v5.0.0 - 2023-01-18
4 | // https://www.bryanbraun.com/anchorjs/
5 | // Copyright (c) 2023 Bryan Braun; Licensed MIT
6 | //
7 | // @license magnet:?xt=urn:btih:d3d9a9a6595521f9666a5e94cc830dab83b65699&dn=expat.txt Expat
8 | !function(A,e){"use strict";"function"==typeof define&&define.amd?define([],e):"object"==typeof module&&module.exports?module.exports=e():(A.AnchorJS=e(),A.anchors=new A.AnchorJS)}(globalThis,function(){"use strict";return function(A){function u(A){A.icon=Object.prototype.hasOwnProperty.call(A,"icon")?A.icon:"",A.visible=Object.prototype.hasOwnProperty.call(A,"visible")?A.visible:"hover",A.placement=Object.prototype.hasOwnProperty.call(A,"placement")?A.placement:"right",A.ariaLabel=Object.prototype.hasOwnProperty.call(A,"ariaLabel")?A.ariaLabel:"Anchor",A.class=Object.prototype.hasOwnProperty.call(A,"class")?A.class:"",A.base=Object.prototype.hasOwnProperty.call(A,"base")?A.base:"",A.truncate=Object.prototype.hasOwnProperty.call(A,"truncate")?Math.floor(A.truncate):64,A.titleText=Object.prototype.hasOwnProperty.call(A,"titleText")?A.titleText:""}function d(A){var e;if("string"==typeof A||A instanceof String)e=[].slice.call(document.querySelectorAll(A));else{if(!(Array.isArray(A)||A instanceof NodeList))throw new TypeError("The selector provided to AnchorJS was invalid.");e=[].slice.call(A)}return e}this.options=A||{},this.elements=[],u(this.options),this.add=function(A){var e,t,o,i,n,s,a,r,l,c,h,p=[];if(u(this.options),0!==(e=d(A=A||"h2, h3, h4, h5, h6")).length){for(null===document.head.querySelector("style.anchorjs")&&((A=document.createElement("style")).className="anchorjs",A.appendChild(document.createTextNode("")),void 0===(h=document.head.querySelector('[rel="stylesheet"],style'))?document.head.appendChild(A):document.head.insertBefore(A,h),A.sheet.insertRule(".anchorjs-link{opacity:0;text-decoration:none;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}",A.sheet.cssRules.length),A.sheet.insertRule(":hover>.anchorjs-link,.anchorjs-link:focus{opacity:1}",A.sheet.cssRules.length),A.sheet.insertRule("[data-anchorjs-icon]::after{content:attr(data-anchorjs-icon)}",A.sheet.cssRules.length),A.sheet.insertRule('@font-face{font-family:anchorjs-icons;src:url(data:n/a;base64,AAEAAAALAIAAAwAwT1MvMg8yG2cAAAE4AAAAYGNtYXDp3gC3AAABpAAAAExnYXNwAAAAEAAAA9wAAAAIZ2x5ZlQCcfwAAAH4AAABCGhlYWQHFvHyAAAAvAAAADZoaGVhBnACFwAAAPQAAAAkaG10eASAADEAAAGYAAAADGxvY2EACACEAAAB8AAAAAhtYXhwAAYAVwAAARgAAAAgbmFtZQGOH9cAAAMAAAAAunBvc3QAAwAAAAADvAAAACAAAQAAAAEAAHzE2p9fDzz1AAkEAAAAAADRecUWAAAAANQA6R8AAAAAAoACwAAAAAgAAgAAAAAAAAABAAADwP/AAAACgAAA/9MCrQABAAAAAAAAAAAAAAAAAAAAAwABAAAAAwBVAAIAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAMCQAGQAAUAAAKZAswAAACPApkCzAAAAesAMwEJAAAAAAAAAAAAAAAAAAAAARAAAAAAAAAAAAAAAAAAAAAAQAAg//0DwP/AAEADwABAAAAAAQAAAAAAAAAAAAAAIAAAAAAAAAIAAAACgAAxAAAAAwAAAAMAAAAcAAEAAwAAABwAAwABAAAAHAAEADAAAAAIAAgAAgAAACDpy//9//8AAAAg6cv//f///+EWNwADAAEAAAAAAAAAAAAAAAAACACEAAEAAAAAAAAAAAAAAAAxAAACAAQARAKAAsAAKwBUAAABIiYnJjQ3NzY2MzIWFxYUBwcGIicmNDc3NjQnJiYjIgYHBwYUFxYUBwYGIwciJicmNDc3NjIXFhQHBwYUFxYWMzI2Nzc2NCcmNDc2MhcWFAcHBgYjARQGDAUtLXoWOR8fORYtLTgKGwoKCjgaGg0gEhIgDXoaGgkJBQwHdR85Fi0tOAobCgoKOBoaDSASEiANehoaCQkKGwotLXoWOR8BMwUFLYEuehYXFxYugC44CQkKGwo4GkoaDQ0NDXoaShoKGwoFBe8XFi6ALjgJCQobCjgaShoNDQ0NehpKGgobCgoKLYEuehYXAAAADACWAAEAAAAAAAEACAAAAAEAAAAAAAIAAwAIAAEAAAAAAAMACAAAAAEAAAAAAAQACAAAAAEAAAAAAAUAAQALAAEAAAAAAAYACAAAAAMAAQQJAAEAEAAMAAMAAQQJAAIABgAcAAMAAQQJAAMAEAAMAAMAAQQJAAQAEAAMAAMAAQQJAAUAAgAiAAMAAQQJAAYAEAAMYW5jaG9yanM0MDBAAGEAbgBjAGgAbwByAGoAcwA0ADAAMABAAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAH//wAP) format("truetype")}',A.sheet.cssRules.length)),h=document.querySelectorAll("[id]"),t=[].map.call(h,function(A){return A.id}),i=0;i\]./()*\\\n\t\b\v\u00A0]/g,"-").replace(/-{2,}/g,"-").substring(0,this.options.truncate).replace(/^-+|-+$/gm,"").toLowerCase()},this.hasAnchorJSLink=function(A){var e=A.firstChild&&-1<(" "+A.firstChild.className+" ").indexOf(" anchorjs-link "),A=A.lastChild&&-1<(" "+A.lastChild.className+" ").indexOf(" anchorjs-link ");return e||A||!1}}});
9 | // @license-end
--------------------------------------------------------------------------------
/dashboard/dashboard_files/libs/quarto-html/popper.min.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @popperjs/core v2.11.7 - MIT License
3 | */
4 |
5 | !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).Popper={})}(this,(function(e){"use strict";function t(e){if(null==e)return window;if("[object Window]"!==e.toString()){var t=e.ownerDocument;return t&&t.defaultView||window}return e}function n(e){return e instanceof t(e).Element||e instanceof Element}function r(e){return e instanceof t(e).HTMLElement||e instanceof HTMLElement}function o(e){return"undefined"!=typeof ShadowRoot&&(e instanceof t(e).ShadowRoot||e instanceof ShadowRoot)}var i=Math.max,a=Math.min,s=Math.round;function f(){var e=navigator.userAgentData;return null!=e&&e.brands&&Array.isArray(e.brands)?e.brands.map((function(e){return e.brand+"/"+e.version})).join(" "):navigator.userAgent}function c(){return!/^((?!chrome|android).)*safari/i.test(f())}function p(e,o,i){void 0===o&&(o=!1),void 0===i&&(i=!1);var a=e.getBoundingClientRect(),f=1,p=1;o&&r(e)&&(f=e.offsetWidth>0&&s(a.width)/e.offsetWidth||1,p=e.offsetHeight>0&&s(a.height)/e.offsetHeight||1);var u=(n(e)?t(e):window).visualViewport,l=!c()&&i,d=(a.left+(l&&u?u.offsetLeft:0))/f,h=(a.top+(l&&u?u.offsetTop:0))/p,m=a.width/f,v=a.height/p;return{width:m,height:v,top:h,right:d+m,bottom:h+v,left:d,x:d,y:h}}function u(e){var n=t(e);return{scrollLeft:n.pageXOffset,scrollTop:n.pageYOffset}}function l(e){return e?(e.nodeName||"").toLowerCase():null}function d(e){return((n(e)?e.ownerDocument:e.document)||window.document).documentElement}function h(e){return p(d(e)).left+u(e).scrollLeft}function m(e){return t(e).getComputedStyle(e)}function v(e){var t=m(e),n=t.overflow,r=t.overflowX,o=t.overflowY;return/auto|scroll|overlay|hidden/.test(n+o+r)}function y(e,n,o){void 0===o&&(o=!1);var i,a,f=r(n),c=r(n)&&function(e){var t=e.getBoundingClientRect(),n=s(t.width)/e.offsetWidth||1,r=s(t.height)/e.offsetHeight||1;return 1!==n||1!==r}(n),m=d(n),y=p(e,c,o),g={scrollLeft:0,scrollTop:0},b={x:0,y:0};return(f||!f&&!o)&&(("body"!==l(n)||v(m))&&(g=(i=n)!==t(i)&&r(i)?{scrollLeft:(a=i).scrollLeft,scrollTop:a.scrollTop}:u(i)),r(n)?((b=p(n,!0)).x+=n.clientLeft,b.y+=n.clientTop):m&&(b.x=h(m))),{x:y.left+g.scrollLeft-b.x,y:y.top+g.scrollTop-b.y,width:y.width,height:y.height}}function g(e){var t=p(e),n=e.offsetWidth,r=e.offsetHeight;return Math.abs(t.width-n)<=1&&(n=t.width),Math.abs(t.height-r)<=1&&(r=t.height),{x:e.offsetLeft,y:e.offsetTop,width:n,height:r}}function b(e){return"html"===l(e)?e:e.assignedSlot||e.parentNode||(o(e)?e.host:null)||d(e)}function x(e){return["html","body","#document"].indexOf(l(e))>=0?e.ownerDocument.body:r(e)&&v(e)?e:x(b(e))}function w(e,n){var r;void 0===n&&(n=[]);var o=x(e),i=o===(null==(r=e.ownerDocument)?void 0:r.body),a=t(o),s=i?[a].concat(a.visualViewport||[],v(o)?o:[]):o,f=n.concat(s);return i?f:f.concat(w(b(s)))}function O(e){return["table","td","th"].indexOf(l(e))>=0}function j(e){return r(e)&&"fixed"!==m(e).position?e.offsetParent:null}function E(e){for(var n=t(e),i=j(e);i&&O(i)&&"static"===m(i).position;)i=j(i);return i&&("html"===l(i)||"body"===l(i)&&"static"===m(i).position)?n:i||function(e){var t=/firefox/i.test(f());if(/Trident/i.test(f())&&r(e)&&"fixed"===m(e).position)return null;var n=b(e);for(o(n)&&(n=n.host);r(n)&&["html","body"].indexOf(l(n))<0;){var i=m(n);if("none"!==i.transform||"none"!==i.perspective||"paint"===i.contain||-1!==["transform","perspective"].indexOf(i.willChange)||t&&"filter"===i.willChange||t&&i.filter&&"none"!==i.filter)return n;n=n.parentNode}return null}(e)||n}var D="top",A="bottom",L="right",P="left",M="auto",k=[D,A,L,P],W="start",B="end",H="viewport",T="popper",R=k.reduce((function(e,t){return e.concat([t+"-"+W,t+"-"+B])}),[]),S=[].concat(k,[M]).reduce((function(e,t){return e.concat([t,t+"-"+W,t+"-"+B])}),[]),V=["beforeRead","read","afterRead","beforeMain","main","afterMain","beforeWrite","write","afterWrite"];function q(e){var t=new Map,n=new Set,r=[];function o(e){n.add(e.name),[].concat(e.requires||[],e.requiresIfExists||[]).forEach((function(e){if(!n.has(e)){var r=t.get(e);r&&o(r)}})),r.push(e)}return e.forEach((function(e){t.set(e.name,e)})),e.forEach((function(e){n.has(e.name)||o(e)})),r}function C(e){return e.split("-")[0]}function N(e,t){var n=t.getRootNode&&t.getRootNode();if(e.contains(t))return!0;if(n&&o(n)){var r=t;do{if(r&&e.isSameNode(r))return!0;r=r.parentNode||r.host}while(r)}return!1}function I(e){return Object.assign({},e,{left:e.x,top:e.y,right:e.x+e.width,bottom:e.y+e.height})}function _(e,r,o){return r===H?I(function(e,n){var r=t(e),o=d(e),i=r.visualViewport,a=o.clientWidth,s=o.clientHeight,f=0,p=0;if(i){a=i.width,s=i.height;var u=c();(u||!u&&"fixed"===n)&&(f=i.offsetLeft,p=i.offsetTop)}return{width:a,height:s,x:f+h(e),y:p}}(e,o)):n(r)?function(e,t){var n=p(e,!1,"fixed"===t);return n.top=n.top+e.clientTop,n.left=n.left+e.clientLeft,n.bottom=n.top+e.clientHeight,n.right=n.left+e.clientWidth,n.width=e.clientWidth,n.height=e.clientHeight,n.x=n.left,n.y=n.top,n}(r,o):I(function(e){var t,n=d(e),r=u(e),o=null==(t=e.ownerDocument)?void 0:t.body,a=i(n.scrollWidth,n.clientWidth,o?o.scrollWidth:0,o?o.clientWidth:0),s=i(n.scrollHeight,n.clientHeight,o?o.scrollHeight:0,o?o.clientHeight:0),f=-r.scrollLeft+h(e),c=-r.scrollTop;return"rtl"===m(o||n).direction&&(f+=i(n.clientWidth,o?o.clientWidth:0)-a),{width:a,height:s,x:f,y:c}}(d(e)))}function F(e,t,o,s){var f="clippingParents"===t?function(e){var t=w(b(e)),o=["absolute","fixed"].indexOf(m(e).position)>=0&&r(e)?E(e):e;return n(o)?t.filter((function(e){return n(e)&&N(e,o)&&"body"!==l(e)})):[]}(e):[].concat(t),c=[].concat(f,[o]),p=c[0],u=c.reduce((function(t,n){var r=_(e,n,s);return t.top=i(r.top,t.top),t.right=a(r.right,t.right),t.bottom=a(r.bottom,t.bottom),t.left=i(r.left,t.left),t}),_(e,p,s));return u.width=u.right-u.left,u.height=u.bottom-u.top,u.x=u.left,u.y=u.top,u}function U(e){return e.split("-")[1]}function z(e){return["top","bottom"].indexOf(e)>=0?"x":"y"}function X(e){var t,n=e.reference,r=e.element,o=e.placement,i=o?C(o):null,a=o?U(o):null,s=n.x+n.width/2-r.width/2,f=n.y+n.height/2-r.height/2;switch(i){case D:t={x:s,y:n.y-r.height};break;case A:t={x:s,y:n.y+n.height};break;case L:t={x:n.x+n.width,y:f};break;case P:t={x:n.x-r.width,y:f};break;default:t={x:n.x,y:n.y}}var c=i?z(i):null;if(null!=c){var p="y"===c?"height":"width";switch(a){case W:t[c]=t[c]-(n[p]/2-r[p]/2);break;case B:t[c]=t[c]+(n[p]/2-r[p]/2)}}return t}function Y(e){return Object.assign({},{top:0,right:0,bottom:0,left:0},e)}function G(e,t){return t.reduce((function(t,n){return t[n]=e,t}),{})}function J(e,t){void 0===t&&(t={});var r=t,o=r.placement,i=void 0===o?e.placement:o,a=r.strategy,s=void 0===a?e.strategy:a,f=r.boundary,c=void 0===f?"clippingParents":f,u=r.rootBoundary,l=void 0===u?H:u,h=r.elementContext,m=void 0===h?T:h,v=r.altBoundary,y=void 0!==v&&v,g=r.padding,b=void 0===g?0:g,x=Y("number"!=typeof b?b:G(b,k)),w=m===T?"reference":T,O=e.rects.popper,j=e.elements[y?w:m],E=F(n(j)?j:j.contextElement||d(e.elements.popper),c,l,s),P=p(e.elements.reference),M=X({reference:P,element:O,strategy:"absolute",placement:i}),W=I(Object.assign({},O,M)),B=m===T?W:P,R={top:E.top-B.top+x.top,bottom:B.bottom-E.bottom+x.bottom,left:E.left-B.left+x.left,right:B.right-E.right+x.right},S=e.modifiersData.offset;if(m===T&&S){var V=S[i];Object.keys(R).forEach((function(e){var t=[L,A].indexOf(e)>=0?1:-1,n=[D,A].indexOf(e)>=0?"y":"x";R[e]+=V[n]*t}))}return R}var K={placement:"bottom",modifiers:[],strategy:"absolute"};function Q(){for(var e=arguments.length,t=new Array(e),n=0;n=0?-1:1,i="function"==typeof n?n(Object.assign({},t,{placement:e})):n,a=i[0],s=i[1];return a=a||0,s=(s||0)*o,[P,L].indexOf(r)>=0?{x:s,y:a}:{x:a,y:s}}(n,t.rects,i),e}),{}),s=a[t.placement],f=s.x,c=s.y;null!=t.modifiersData.popperOffsets&&(t.modifiersData.popperOffsets.x+=f,t.modifiersData.popperOffsets.y+=c),t.modifiersData[r]=a}},se={left:"right",right:"left",bottom:"top",top:"bottom"};function fe(e){return e.replace(/left|right|bottom|top/g,(function(e){return se[e]}))}var ce={start:"end",end:"start"};function pe(e){return e.replace(/start|end/g,(function(e){return ce[e]}))}function ue(e,t){void 0===t&&(t={});var n=t,r=n.placement,o=n.boundary,i=n.rootBoundary,a=n.padding,s=n.flipVariations,f=n.allowedAutoPlacements,c=void 0===f?S:f,p=U(r),u=p?s?R:R.filter((function(e){return U(e)===p})):k,l=u.filter((function(e){return c.indexOf(e)>=0}));0===l.length&&(l=u);var d=l.reduce((function(t,n){return t[n]=J(e,{placement:n,boundary:o,rootBoundary:i,padding:a})[C(n)],t}),{});return Object.keys(d).sort((function(e,t){return d[e]-d[t]}))}var le={name:"flip",enabled:!0,phase:"main",fn:function(e){var t=e.state,n=e.options,r=e.name;if(!t.modifiersData[r]._skip){for(var o=n.mainAxis,i=void 0===o||o,a=n.altAxis,s=void 0===a||a,f=n.fallbackPlacements,c=n.padding,p=n.boundary,u=n.rootBoundary,l=n.altBoundary,d=n.flipVariations,h=void 0===d||d,m=n.allowedAutoPlacements,v=t.options.placement,y=C(v),g=f||(y===v||!h?[fe(v)]:function(e){if(C(e)===M)return[];var t=fe(e);return[pe(e),t,pe(t)]}(v)),b=[v].concat(g).reduce((function(e,n){return e.concat(C(n)===M?ue(t,{placement:n,boundary:p,rootBoundary:u,padding:c,flipVariations:h,allowedAutoPlacements:m}):n)}),[]),x=t.rects.reference,w=t.rects.popper,O=new Map,j=!0,E=b[0],k=0;k=0,S=R?"width":"height",V=J(t,{placement:B,boundary:p,rootBoundary:u,altBoundary:l,padding:c}),q=R?T?L:P:T?A:D;x[S]>w[S]&&(q=fe(q));var N=fe(q),I=[];if(i&&I.push(V[H]<=0),s&&I.push(V[q]<=0,V[N]<=0),I.every((function(e){return e}))){E=B,j=!1;break}O.set(B,I)}if(j)for(var _=function(e){var t=b.find((function(t){var n=O.get(t);if(n)return n.slice(0,e).every((function(e){return e}))}));if(t)return E=t,"break"},F=h?3:1;F>0;F--){if("break"===_(F))break}t.placement!==E&&(t.modifiersData[r]._skip=!0,t.placement=E,t.reset=!0)}},requiresIfExists:["offset"],data:{_skip:!1}};function de(e,t,n){return i(e,a(t,n))}var he={name:"preventOverflow",enabled:!0,phase:"main",fn:function(e){var t=e.state,n=e.options,r=e.name,o=n.mainAxis,s=void 0===o||o,f=n.altAxis,c=void 0!==f&&f,p=n.boundary,u=n.rootBoundary,l=n.altBoundary,d=n.padding,h=n.tether,m=void 0===h||h,v=n.tetherOffset,y=void 0===v?0:v,b=J(t,{boundary:p,rootBoundary:u,padding:d,altBoundary:l}),x=C(t.placement),w=U(t.placement),O=!w,j=z(x),M="x"===j?"y":"x",k=t.modifiersData.popperOffsets,B=t.rects.reference,H=t.rects.popper,T="function"==typeof y?y(Object.assign({},t.rects,{placement:t.placement})):y,R="number"==typeof T?{mainAxis:T,altAxis:T}:Object.assign({mainAxis:0,altAxis:0},T),S=t.modifiersData.offset?t.modifiersData.offset[t.placement]:null,V={x:0,y:0};if(k){if(s){var q,N="y"===j?D:P,I="y"===j?A:L,_="y"===j?"height":"width",F=k[j],X=F+b[N],Y=F-b[I],G=m?-H[_]/2:0,K=w===W?B[_]:H[_],Q=w===W?-H[_]:-B[_],Z=t.elements.arrow,$=m&&Z?g(Z):{width:0,height:0},ee=t.modifiersData["arrow#persistent"]?t.modifiersData["arrow#persistent"].padding:{top:0,right:0,bottom:0,left:0},te=ee[N],ne=ee[I],re=de(0,B[_],$[_]),oe=O?B[_]/2-G-re-te-R.mainAxis:K-re-te-R.mainAxis,ie=O?-B[_]/2+G+re+ne+R.mainAxis:Q+re+ne+R.mainAxis,ae=t.elements.arrow&&E(t.elements.arrow),se=ae?"y"===j?ae.clientTop||0:ae.clientLeft||0:0,fe=null!=(q=null==S?void 0:S[j])?q:0,ce=F+ie-fe,pe=de(m?a(X,F+oe-fe-se):X,F,m?i(Y,ce):Y);k[j]=pe,V[j]=pe-F}if(c){var ue,le="x"===j?D:P,he="x"===j?A:L,me=k[M],ve="y"===M?"height":"width",ye=me+b[le],ge=me-b[he],be=-1!==[D,P].indexOf(x),xe=null!=(ue=null==S?void 0:S[M])?ue:0,we=be?ye:me-B[ve]-H[ve]-xe+R.altAxis,Oe=be?me+B[ve]+H[ve]-xe-R.altAxis:ge,je=m&&be?function(e,t,n){var r=de(e,t,n);return r>n?n:r}(we,me,Oe):de(m?we:ye,me,m?Oe:ge);k[M]=je,V[M]=je-me}t.modifiersData[r]=V}},requiresIfExists:["offset"]};var me={name:"arrow",enabled:!0,phase:"main",fn:function(e){var t,n=e.state,r=e.name,o=e.options,i=n.elements.arrow,a=n.modifiersData.popperOffsets,s=C(n.placement),f=z(s),c=[P,L].indexOf(s)>=0?"height":"width";if(i&&a){var p=function(e,t){return Y("number"!=typeof(e="function"==typeof e?e(Object.assign({},t.rects,{placement:t.placement})):e)?e:G(e,k))}(o.padding,n),u=g(i),l="y"===f?D:P,d="y"===f?A:L,h=n.rects.reference[c]+n.rects.reference[f]-a[f]-n.rects.popper[c],m=a[f]-n.rects.reference[f],v=E(i),y=v?"y"===f?v.clientHeight||0:v.clientWidth||0:0,b=h/2-m/2,x=p[l],w=y-u[c]-p[d],O=y/2-u[c]/2+b,j=de(x,O,w),M=f;n.modifiersData[r]=((t={})[M]=j,t.centerOffset=j-O,t)}},effect:function(e){var t=e.state,n=e.options.element,r=void 0===n?"[data-popper-arrow]":n;null!=r&&("string"!=typeof r||(r=t.elements.popper.querySelector(r)))&&N(t.elements.popper,r)&&(t.elements.arrow=r)},requires:["popperOffsets"],requiresIfExists:["preventOverflow"]};function ve(e,t,n){return void 0===n&&(n={x:0,y:0}),{top:e.top-t.height-n.y,right:e.right-t.width+n.x,bottom:e.bottom-t.height+n.y,left:e.left-t.width-n.x}}function ye(e){return[D,L,A,P].some((function(t){return e[t]>=0}))}var ge={name:"hide",enabled:!0,phase:"main",requiresIfExists:["preventOverflow"],fn:function(e){var t=e.state,n=e.name,r=t.rects.reference,o=t.rects.popper,i=t.modifiersData.preventOverflow,a=J(t,{elementContext:"reference"}),s=J(t,{altBoundary:!0}),f=ve(a,r),c=ve(s,o,i),p=ye(f),u=ye(c);t.modifiersData[n]={referenceClippingOffsets:f,popperEscapeOffsets:c,isReferenceHidden:p,hasPopperEscaped:u},t.attributes.popper=Object.assign({},t.attributes.popper,{"data-popper-reference-hidden":p,"data-popper-escaped":u})}},be=Z({defaultModifiers:[ee,te,oe,ie]}),xe=[ee,te,oe,ie,ae,le,he,me,ge],we=Z({defaultModifiers:xe});e.applyStyles=ie,e.arrow=me,e.computeStyles=oe,e.createPopper=we,e.createPopperLite=be,e.defaultModifiers=xe,e.detectOverflow=J,e.eventListeners=ee,e.flip=le,e.hide=ge,e.offset=ae,e.popperGenerator=Z,e.popperOffsets=te,e.preventOverflow=he,Object.defineProperty(e,"__esModule",{value:!0})}));
6 |
7 |
--------------------------------------------------------------------------------
/dashboard/dashboard_files/libs/quarto-html/quarto-syntax-highlighting.css:
--------------------------------------------------------------------------------
1 | /* quarto syntax highlight colors */
2 | :root {
3 | --quarto-hl-ot-color: #003B4F;
4 | --quarto-hl-at-color: #657422;
5 | --quarto-hl-ss-color: #20794D;
6 | --quarto-hl-an-color: #5E5E5E;
7 | --quarto-hl-fu-color: #4758AB;
8 | --quarto-hl-st-color: #20794D;
9 | --quarto-hl-cf-color: #003B4F;
10 | --quarto-hl-op-color: #5E5E5E;
11 | --quarto-hl-er-color: #AD0000;
12 | --quarto-hl-bn-color: #AD0000;
13 | --quarto-hl-al-color: #AD0000;
14 | --quarto-hl-va-color: #111111;
15 | --quarto-hl-bu-color: inherit;
16 | --quarto-hl-ex-color: inherit;
17 | --quarto-hl-pp-color: #AD0000;
18 | --quarto-hl-in-color: #5E5E5E;
19 | --quarto-hl-vs-color: #20794D;
20 | --quarto-hl-wa-color: #5E5E5E;
21 | --quarto-hl-do-color: #5E5E5E;
22 | --quarto-hl-im-color: #00769E;
23 | --quarto-hl-ch-color: #20794D;
24 | --quarto-hl-dt-color: #AD0000;
25 | --quarto-hl-fl-color: #AD0000;
26 | --quarto-hl-co-color: #5E5E5E;
27 | --quarto-hl-cv-color: #5E5E5E;
28 | --quarto-hl-cn-color: #8f5902;
29 | --quarto-hl-sc-color: #5E5E5E;
30 | --quarto-hl-dv-color: #AD0000;
31 | --quarto-hl-kw-color: #003B4F;
32 | }
33 |
34 | /* other quarto variables */
35 | :root {
36 | --quarto-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
37 | }
38 |
39 | pre > code.sourceCode > span {
40 | color: #003B4F;
41 | }
42 |
43 | code span {
44 | color: #003B4F;
45 | }
46 |
47 | code.sourceCode > span {
48 | color: #003B4F;
49 | }
50 |
51 | div.sourceCode,
52 | div.sourceCode pre.sourceCode {
53 | color: #003B4F;
54 | }
55 |
56 | code span.ot {
57 | color: #003B4F;
58 | font-style: inherit;
59 | }
60 |
61 | code span.at {
62 | color: #657422;
63 | font-style: inherit;
64 | }
65 |
66 | code span.ss {
67 | color: #20794D;
68 | font-style: inherit;
69 | }
70 |
71 | code span.an {
72 | color: #5E5E5E;
73 | font-style: inherit;
74 | }
75 |
76 | code span.fu {
77 | color: #4758AB;
78 | font-style: inherit;
79 | }
80 |
81 | code span.st {
82 | color: #20794D;
83 | font-style: inherit;
84 | }
85 |
86 | code span.cf {
87 | color: #003B4F;
88 | font-style: inherit;
89 | }
90 |
91 | code span.op {
92 | color: #5E5E5E;
93 | font-style: inherit;
94 | }
95 |
96 | code span.er {
97 | color: #AD0000;
98 | font-style: inherit;
99 | }
100 |
101 | code span.bn {
102 | color: #AD0000;
103 | font-style: inherit;
104 | }
105 |
106 | code span.al {
107 | color: #AD0000;
108 | font-style: inherit;
109 | }
110 |
111 | code span.va {
112 | color: #111111;
113 | font-style: inherit;
114 | }
115 |
116 | code span.bu {
117 | font-style: inherit;
118 | }
119 |
120 | code span.ex {
121 | font-style: inherit;
122 | }
123 |
124 | code span.pp {
125 | color: #AD0000;
126 | font-style: inherit;
127 | }
128 |
129 | code span.in {
130 | color: #5E5E5E;
131 | font-style: inherit;
132 | }
133 |
134 | code span.vs {
135 | color: #20794D;
136 | font-style: inherit;
137 | }
138 |
139 | code span.wa {
140 | color: #5E5E5E;
141 | font-style: italic;
142 | }
143 |
144 | code span.do {
145 | color: #5E5E5E;
146 | font-style: italic;
147 | }
148 |
149 | code span.im {
150 | color: #00769E;
151 | font-style: inherit;
152 | }
153 |
154 | code span.ch {
155 | color: #20794D;
156 | font-style: inherit;
157 | }
158 |
159 | code span.dt {
160 | color: #AD0000;
161 | font-style: inherit;
162 | }
163 |
164 | code span.fl {
165 | color: #AD0000;
166 | font-style: inherit;
167 | }
168 |
169 | code span.co {
170 | color: #5E5E5E;
171 | font-style: inherit;
172 | }
173 |
174 | code span.cv {
175 | color: #5E5E5E;
176 | font-style: italic;
177 | }
178 |
179 | code span.cn {
180 | color: #8f5902;
181 | font-style: inherit;
182 | }
183 |
184 | code span.sc {
185 | color: #5E5E5E;
186 | font-style: inherit;
187 | }
188 |
189 | code span.dv {
190 | color: #AD0000;
191 | font-style: inherit;
192 | }
193 |
194 | code span.kw {
195 | color: #003B4F;
196 | font-style: inherit;
197 | }
198 |
199 | .prevent-inlining {
200 | content: "";
201 | }
202 |
203 | /*# sourceMappingURL=debc5d5d77c3f9108843748ff7464032.css.map */
204 |
--------------------------------------------------------------------------------
/dashboard/dashboard_files/libs/quarto-html/quarto.js:
--------------------------------------------------------------------------------
1 | const sectionChanged = new CustomEvent("quarto-sectionChanged", {
2 | detail: {},
3 | bubbles: true,
4 | cancelable: false,
5 | composed: false,
6 | });
7 |
8 | const layoutMarginEls = () => {
9 | // Find any conflicting margin elements and add margins to the
10 | // top to prevent overlap
11 | const marginChildren = window.document.querySelectorAll(
12 | ".column-margin.column-container > * "
13 | );
14 |
15 | let lastBottom = 0;
16 | for (const marginChild of marginChildren) {
17 | if (marginChild.offsetParent !== null) {
18 | // clear the top margin so we recompute it
19 | marginChild.style.marginTop = null;
20 | const top = marginChild.getBoundingClientRect().top + window.scrollY;
21 | if (top < lastBottom) {
22 | const margin = lastBottom - top;
23 | marginChild.style.marginTop = `${margin}px`;
24 | }
25 | const styles = window.getComputedStyle(marginChild);
26 | const marginTop = parseFloat(styles["marginTop"]);
27 | lastBottom = top + marginChild.getBoundingClientRect().height + marginTop;
28 | }
29 | }
30 | };
31 |
32 | window.document.addEventListener("DOMContentLoaded", function (_event) {
33 | // Recompute the position of margin elements anytime the body size changes
34 | if (window.ResizeObserver) {
35 | const resizeObserver = new window.ResizeObserver(
36 | throttle(layoutMarginEls, 50)
37 | );
38 | resizeObserver.observe(window.document.body);
39 | }
40 |
41 | const tocEl = window.document.querySelector('nav.toc-active[role="doc-toc"]');
42 | const sidebarEl = window.document.getElementById("quarto-sidebar");
43 | const leftTocEl = window.document.getElementById("quarto-sidebar-toc-left");
44 | const marginSidebarEl = window.document.getElementById(
45 | "quarto-margin-sidebar"
46 | );
47 | // function to determine whether the element has a previous sibling that is active
48 | const prevSiblingIsActiveLink = (el) => {
49 | const sibling = el.previousElementSibling;
50 | if (sibling && sibling.tagName === "A") {
51 | return sibling.classList.contains("active");
52 | } else {
53 | return false;
54 | }
55 | };
56 |
57 | // fire slideEnter for bootstrap tab activations (for htmlwidget resize behavior)
58 | function fireSlideEnter(e) {
59 | const event = window.document.createEvent("Event");
60 | event.initEvent("slideenter", true, true);
61 | window.document.dispatchEvent(event);
62 | }
63 | const tabs = window.document.querySelectorAll('a[data-bs-toggle="tab"]');
64 | tabs.forEach((tab) => {
65 | tab.addEventListener("shown.bs.tab", fireSlideEnter);
66 | });
67 |
68 | // fire slideEnter for tabby tab activations (for htmlwidget resize behavior)
69 | document.addEventListener("tabby", fireSlideEnter, false);
70 |
71 | // Track scrolling and mark TOC links as active
72 | // get table of contents and sidebar (bail if we don't have at least one)
73 | const tocLinks = tocEl
74 | ? [...tocEl.querySelectorAll("a[data-scroll-target]")]
75 | : [];
76 | const makeActive = (link) => tocLinks[link].classList.add("active");
77 | const removeActive = (link) => tocLinks[link].classList.remove("active");
78 | const removeAllActive = () =>
79 | [...Array(tocLinks.length).keys()].forEach((link) => removeActive(link));
80 |
81 | // activate the anchor for a section associated with this TOC entry
82 | tocLinks.forEach((link) => {
83 | link.addEventListener("click", () => {
84 | if (link.href.indexOf("#") !== -1) {
85 | const anchor = link.href.split("#")[1];
86 | const heading = window.document.querySelector(
87 | `[data-anchor-id=${anchor}]`
88 | );
89 | if (heading) {
90 | // Add the class
91 | heading.classList.add("reveal-anchorjs-link");
92 |
93 | // function to show the anchor
94 | const handleMouseout = () => {
95 | heading.classList.remove("reveal-anchorjs-link");
96 | heading.removeEventListener("mouseout", handleMouseout);
97 | };
98 |
99 | // add a function to clear the anchor when the user mouses out of it
100 | heading.addEventListener("mouseout", handleMouseout);
101 | }
102 | }
103 | });
104 | });
105 |
106 | const sections = tocLinks.map((link) => {
107 | const target = link.getAttribute("data-scroll-target");
108 | if (target.startsWith("#")) {
109 | return window.document.getElementById(decodeURI(`${target.slice(1)}`));
110 | } else {
111 | return window.document.querySelector(decodeURI(`${target}`));
112 | }
113 | });
114 |
115 | const sectionMargin = 200;
116 | let currentActive = 0;
117 | // track whether we've initialized state the first time
118 | let init = false;
119 |
120 | const updateActiveLink = () => {
121 | // The index from bottom to top (e.g. reversed list)
122 | let sectionIndex = -1;
123 | if (
124 | window.innerHeight + window.pageYOffset >=
125 | window.document.body.offsetHeight
126 | ) {
127 | sectionIndex = 0;
128 | } else {
129 | sectionIndex = [...sections].reverse().findIndex((section) => {
130 | if (section) {
131 | return window.pageYOffset >= section.offsetTop - sectionMargin;
132 | } else {
133 | return false;
134 | }
135 | });
136 | }
137 | if (sectionIndex > -1) {
138 | const current = sections.length - sectionIndex - 1;
139 | if (current !== currentActive) {
140 | removeAllActive();
141 | currentActive = current;
142 | makeActive(current);
143 | if (init) {
144 | window.dispatchEvent(sectionChanged);
145 | }
146 | init = true;
147 | }
148 | }
149 | };
150 |
151 | const inHiddenRegion = (top, bottom, hiddenRegions) => {
152 | for (const region of hiddenRegions) {
153 | if (top <= region.bottom && bottom >= region.top) {
154 | return true;
155 | }
156 | }
157 | return false;
158 | };
159 |
160 | const categorySelector = "header.quarto-title-block .quarto-category";
161 | const activateCategories = (href) => {
162 | // Find any categories
163 | // Surround them with a link pointing back to:
164 | // #category=Authoring
165 | try {
166 | const categoryEls = window.document.querySelectorAll(categorySelector);
167 | for (const categoryEl of categoryEls) {
168 | const categoryText = categoryEl.textContent;
169 | if (categoryText) {
170 | const link = `${href}#category=${encodeURIComponent(categoryText)}`;
171 | const linkEl = window.document.createElement("a");
172 | linkEl.setAttribute("href", link);
173 | for (const child of categoryEl.childNodes) {
174 | linkEl.append(child);
175 | }
176 | categoryEl.appendChild(linkEl);
177 | }
178 | }
179 | } catch {
180 | // Ignore errors
181 | }
182 | };
183 | function hasTitleCategories() {
184 | return window.document.querySelector(categorySelector) !== null;
185 | }
186 |
187 | function offsetRelativeUrl(url) {
188 | const offset = getMeta("quarto:offset");
189 | return offset ? offset + url : url;
190 | }
191 |
192 | function offsetAbsoluteUrl(url) {
193 | const offset = getMeta("quarto:offset");
194 | const baseUrl = new URL(offset, window.location);
195 |
196 | const projRelativeUrl = url.replace(baseUrl, "");
197 | if (projRelativeUrl.startsWith("/")) {
198 | return projRelativeUrl;
199 | } else {
200 | return "/" + projRelativeUrl;
201 | }
202 | }
203 |
204 | // read a meta tag value
205 | function getMeta(metaName) {
206 | const metas = window.document.getElementsByTagName("meta");
207 | for (let i = 0; i < metas.length; i++) {
208 | if (metas[i].getAttribute("name") === metaName) {
209 | return metas[i].getAttribute("content");
210 | }
211 | }
212 | return "";
213 | }
214 |
215 | async function findAndActivateCategories() {
216 | const currentPagePath = offsetAbsoluteUrl(window.location.href);
217 | const response = await fetch(offsetRelativeUrl("listings.json"));
218 | if (response.status == 200) {
219 | return response.json().then(function (listingPaths) {
220 | const listingHrefs = [];
221 | for (const listingPath of listingPaths) {
222 | const pathWithoutLeadingSlash = listingPath.listing.substring(1);
223 | for (const item of listingPath.items) {
224 | if (
225 | item === currentPagePath ||
226 | item === currentPagePath + "index.html"
227 | ) {
228 | // Resolve this path against the offset to be sure
229 | // we already are using the correct path to the listing
230 | // (this adjusts the listing urls to be rooted against
231 | // whatever root the page is actually running against)
232 | const relative = offsetRelativeUrl(pathWithoutLeadingSlash);
233 | const baseUrl = window.location;
234 | const resolvedPath = new URL(relative, baseUrl);
235 | listingHrefs.push(resolvedPath.pathname);
236 | break;
237 | }
238 | }
239 | }
240 |
241 | // Look up the tree for a nearby linting and use that if we find one
242 | const nearestListing = findNearestParentListing(
243 | offsetAbsoluteUrl(window.location.pathname),
244 | listingHrefs
245 | );
246 | if (nearestListing) {
247 | activateCategories(nearestListing);
248 | } else {
249 | // See if the referrer is a listing page for this item
250 | const referredRelativePath = offsetAbsoluteUrl(document.referrer);
251 | const referrerListing = listingHrefs.find((listingHref) => {
252 | const isListingReferrer =
253 | listingHref === referredRelativePath ||
254 | listingHref === referredRelativePath + "index.html";
255 | return isListingReferrer;
256 | });
257 |
258 | if (referrerListing) {
259 | // Try to use the referrer if possible
260 | activateCategories(referrerListing);
261 | } else if (listingHrefs.length > 0) {
262 | // Otherwise, just fall back to the first listing
263 | activateCategories(listingHrefs[0]);
264 | }
265 | }
266 | });
267 | }
268 | }
269 | if (hasTitleCategories()) {
270 | findAndActivateCategories();
271 | }
272 |
273 | const findNearestParentListing = (href, listingHrefs) => {
274 | if (!href || !listingHrefs) {
275 | return undefined;
276 | }
277 | // Look up the tree for a nearby linting and use that if we find one
278 | const relativeParts = href.substring(1).split("/");
279 | while (relativeParts.length > 0) {
280 | const path = relativeParts.join("/");
281 | for (const listingHref of listingHrefs) {
282 | if (listingHref.startsWith(path)) {
283 | return listingHref;
284 | }
285 | }
286 | relativeParts.pop();
287 | }
288 |
289 | return undefined;
290 | };
291 |
292 | const manageSidebarVisiblity = (el, placeholderDescriptor) => {
293 | let isVisible = true;
294 | let elRect;
295 |
296 | return (hiddenRegions) => {
297 | if (el === null) {
298 | return;
299 | }
300 |
301 | // Find the last element of the TOC
302 | const lastChildEl = el.lastElementChild;
303 |
304 | if (lastChildEl) {
305 | // Converts the sidebar to a menu
306 | const convertToMenu = () => {
307 | for (const child of el.children) {
308 | child.style.opacity = 0;
309 | child.style.overflow = "hidden";
310 | }
311 |
312 | nexttick(() => {
313 | const toggleContainer = window.document.createElement("div");
314 | toggleContainer.style.width = "100%";
315 | toggleContainer.classList.add("zindex-over-content");
316 | toggleContainer.classList.add("quarto-sidebar-toggle");
317 | toggleContainer.classList.add("headroom-target"); // Marks this to be managed by headeroom
318 | toggleContainer.id = placeholderDescriptor.id;
319 | toggleContainer.style.position = "fixed";
320 |
321 | const toggleIcon = window.document.createElement("i");
322 | toggleIcon.classList.add("quarto-sidebar-toggle-icon");
323 | toggleIcon.classList.add("bi");
324 | toggleIcon.classList.add("bi-caret-down-fill");
325 |
326 | const toggleTitle = window.document.createElement("div");
327 | const titleEl = window.document.body.querySelector(
328 | placeholderDescriptor.titleSelector
329 | );
330 | if (titleEl) {
331 | toggleTitle.append(
332 | titleEl.textContent || titleEl.innerText,
333 | toggleIcon
334 | );
335 | }
336 | toggleTitle.classList.add("zindex-over-content");
337 | toggleTitle.classList.add("quarto-sidebar-toggle-title");
338 | toggleContainer.append(toggleTitle);
339 |
340 | const toggleContents = window.document.createElement("div");
341 | toggleContents.classList = el.classList;
342 | toggleContents.classList.add("zindex-over-content");
343 | toggleContents.classList.add("quarto-sidebar-toggle-contents");
344 | for (const child of el.children) {
345 | if (child.id === "toc-title") {
346 | continue;
347 | }
348 |
349 | const clone = child.cloneNode(true);
350 | clone.style.opacity = 1;
351 | clone.style.display = null;
352 | toggleContents.append(clone);
353 | }
354 | toggleContents.style.height = "0px";
355 | const positionToggle = () => {
356 | // position the element (top left of parent, same width as parent)
357 | if (!elRect) {
358 | elRect = el.getBoundingClientRect();
359 | }
360 | toggleContainer.style.left = `${elRect.left}px`;
361 | toggleContainer.style.top = `${elRect.top}px`;
362 | toggleContainer.style.width = `${elRect.width}px`;
363 | };
364 | positionToggle();
365 |
366 | toggleContainer.append(toggleContents);
367 | el.parentElement.prepend(toggleContainer);
368 |
369 | // Process clicks
370 | let tocShowing = false;
371 | // Allow the caller to control whether this is dismissed
372 | // when it is clicked (e.g. sidebar navigation supports
373 | // opening and closing the nav tree, so don't dismiss on click)
374 | const clickEl = placeholderDescriptor.dismissOnClick
375 | ? toggleContainer
376 | : toggleTitle;
377 |
378 | const closeToggle = () => {
379 | if (tocShowing) {
380 | toggleContainer.classList.remove("expanded");
381 | toggleContents.style.height = "0px";
382 | tocShowing = false;
383 | }
384 | };
385 |
386 | // Get rid of any expanded toggle if the user scrolls
387 | window.document.addEventListener(
388 | "scroll",
389 | throttle(() => {
390 | closeToggle();
391 | }, 50)
392 | );
393 |
394 | // Handle positioning of the toggle
395 | window.addEventListener(
396 | "resize",
397 | throttle(() => {
398 | elRect = undefined;
399 | positionToggle();
400 | }, 50)
401 | );
402 |
403 | window.addEventListener("quarto-hrChanged", () => {
404 | elRect = undefined;
405 | });
406 |
407 | // Process the click
408 | clickEl.onclick = () => {
409 | if (!tocShowing) {
410 | toggleContainer.classList.add("expanded");
411 | toggleContents.style.height = null;
412 | tocShowing = true;
413 | } else {
414 | closeToggle();
415 | }
416 | };
417 | });
418 | };
419 |
420 | // Converts a sidebar from a menu back to a sidebar
421 | const convertToSidebar = () => {
422 | for (const child of el.children) {
423 | child.style.opacity = 1;
424 | child.style.overflow = null;
425 | }
426 |
427 | const placeholderEl = window.document.getElementById(
428 | placeholderDescriptor.id
429 | );
430 | if (placeholderEl) {
431 | placeholderEl.remove();
432 | }
433 |
434 | el.classList.remove("rollup");
435 | };
436 |
437 | if (isReaderMode()) {
438 | convertToMenu();
439 | isVisible = false;
440 | } else {
441 | // Find the top and bottom o the element that is being managed
442 | const elTop = el.offsetTop;
443 | const elBottom =
444 | elTop + lastChildEl.offsetTop + lastChildEl.offsetHeight;
445 |
446 | if (!isVisible) {
447 | // If the element is current not visible reveal if there are
448 | // no conflicts with overlay regions
449 | if (!inHiddenRegion(elTop, elBottom, hiddenRegions)) {
450 | convertToSidebar();
451 | isVisible = true;
452 | }
453 | } else {
454 | // If the element is visible, hide it if it conflicts with overlay regions
455 | // and insert a placeholder toggle (or if we're in reader mode)
456 | if (inHiddenRegion(elTop, elBottom, hiddenRegions)) {
457 | convertToMenu();
458 | isVisible = false;
459 | }
460 | }
461 | }
462 | }
463 | };
464 | };
465 |
466 | const tabEls = document.querySelectorAll('a[data-bs-toggle="tab"]');
467 | for (const tabEl of tabEls) {
468 | const id = tabEl.getAttribute("data-bs-target");
469 | if (id) {
470 | const columnEl = document.querySelector(
471 | `${id} .column-margin, .tabset-margin-content`
472 | );
473 | if (columnEl)
474 | tabEl.addEventListener("shown.bs.tab", function (event) {
475 | const el = event.srcElement;
476 | if (el) {
477 | const visibleCls = `${el.id}-margin-content`;
478 | // walk up until we find a parent tabset
479 | let panelTabsetEl = el.parentElement;
480 | while (panelTabsetEl) {
481 | if (panelTabsetEl.classList.contains("panel-tabset")) {
482 | break;
483 | }
484 | panelTabsetEl = panelTabsetEl.parentElement;
485 | }
486 |
487 | if (panelTabsetEl) {
488 | const prevSib = panelTabsetEl.previousElementSibling;
489 | if (
490 | prevSib &&
491 | prevSib.classList.contains("tabset-margin-container")
492 | ) {
493 | const childNodes = prevSib.querySelectorAll(
494 | ".tabset-margin-content"
495 | );
496 | for (const childEl of childNodes) {
497 | if (childEl.classList.contains(visibleCls)) {
498 | childEl.classList.remove("collapse");
499 | } else {
500 | childEl.classList.add("collapse");
501 | }
502 | }
503 | }
504 | }
505 | }
506 |
507 | layoutMarginEls();
508 | });
509 | }
510 | }
511 |
512 | // Manage the visibility of the toc and the sidebar
513 | const marginScrollVisibility = manageSidebarVisiblity(marginSidebarEl, {
514 | id: "quarto-toc-toggle",
515 | titleSelector: "#toc-title",
516 | dismissOnClick: true,
517 | });
518 | const sidebarScrollVisiblity = manageSidebarVisiblity(sidebarEl, {
519 | id: "quarto-sidebarnav-toggle",
520 | titleSelector: ".title",
521 | dismissOnClick: false,
522 | });
523 | let tocLeftScrollVisibility;
524 | if (leftTocEl) {
525 | tocLeftScrollVisibility = manageSidebarVisiblity(leftTocEl, {
526 | id: "quarto-lefttoc-toggle",
527 | titleSelector: "#toc-title",
528 | dismissOnClick: true,
529 | });
530 | }
531 |
532 | // Find the first element that uses formatting in special columns
533 | const conflictingEls = window.document.body.querySelectorAll(
534 | '[class^="column-"], [class*=" column-"], aside, [class*="margin-caption"], [class*=" margin-caption"], [class*="margin-ref"], [class*=" margin-ref"]'
535 | );
536 |
537 | // Filter all the possibly conflicting elements into ones
538 | // the do conflict on the left or ride side
539 | const arrConflictingEls = Array.from(conflictingEls);
540 | const leftSideConflictEls = arrConflictingEls.filter((el) => {
541 | if (el.tagName === "ASIDE") {
542 | return false;
543 | }
544 | return Array.from(el.classList).find((className) => {
545 | return (
546 | className !== "column-body" &&
547 | className.startsWith("column-") &&
548 | !className.endsWith("right") &&
549 | !className.endsWith("container") &&
550 | className !== "column-margin"
551 | );
552 | });
553 | });
554 | const rightSideConflictEls = arrConflictingEls.filter((el) => {
555 | if (el.tagName === "ASIDE") {
556 | return true;
557 | }
558 |
559 | const hasMarginCaption = Array.from(el.classList).find((className) => {
560 | return className == "margin-caption";
561 | });
562 | if (hasMarginCaption) {
563 | return true;
564 | }
565 |
566 | return Array.from(el.classList).find((className) => {
567 | return (
568 | className !== "column-body" &&
569 | !className.endsWith("container") &&
570 | className.startsWith("column-") &&
571 | !className.endsWith("left")
572 | );
573 | });
574 | });
575 |
576 | const kOverlapPaddingSize = 10;
577 | function toRegions(els) {
578 | return els.map((el) => {
579 | const boundRect = el.getBoundingClientRect();
580 | const top =
581 | boundRect.top +
582 | document.documentElement.scrollTop -
583 | kOverlapPaddingSize;
584 | return {
585 | top,
586 | bottom: top + el.scrollHeight + 2 * kOverlapPaddingSize,
587 | };
588 | });
589 | }
590 |
591 | let hasObserved = false;
592 | const visibleItemObserver = (els) => {
593 | let visibleElements = [...els];
594 | const intersectionObserver = new IntersectionObserver(
595 | (entries, _observer) => {
596 | entries.forEach((entry) => {
597 | if (entry.isIntersecting) {
598 | if (visibleElements.indexOf(entry.target) === -1) {
599 | visibleElements.push(entry.target);
600 | }
601 | } else {
602 | visibleElements = visibleElements.filter((visibleEntry) => {
603 | return visibleEntry !== entry;
604 | });
605 | }
606 | });
607 |
608 | if (!hasObserved) {
609 | hideOverlappedSidebars();
610 | }
611 | hasObserved = true;
612 | },
613 | {}
614 | );
615 | els.forEach((el) => {
616 | intersectionObserver.observe(el);
617 | });
618 |
619 | return {
620 | getVisibleEntries: () => {
621 | return visibleElements;
622 | },
623 | };
624 | };
625 |
626 | const rightElementObserver = visibleItemObserver(rightSideConflictEls);
627 | const leftElementObserver = visibleItemObserver(leftSideConflictEls);
628 |
629 | const hideOverlappedSidebars = () => {
630 | marginScrollVisibility(toRegions(rightElementObserver.getVisibleEntries()));
631 | sidebarScrollVisiblity(toRegions(leftElementObserver.getVisibleEntries()));
632 | if (tocLeftScrollVisibility) {
633 | tocLeftScrollVisibility(
634 | toRegions(leftElementObserver.getVisibleEntries())
635 | );
636 | }
637 | };
638 |
639 | window.quartoToggleReader = () => {
640 | // Applies a slow class (or removes it)
641 | // to update the transition speed
642 | const slowTransition = (slow) => {
643 | const manageTransition = (id, slow) => {
644 | const el = document.getElementById(id);
645 | if (el) {
646 | if (slow) {
647 | el.classList.add("slow");
648 | } else {
649 | el.classList.remove("slow");
650 | }
651 | }
652 | };
653 |
654 | manageTransition("TOC", slow);
655 | manageTransition("quarto-sidebar", slow);
656 | };
657 | const readerMode = !isReaderMode();
658 | setReaderModeValue(readerMode);
659 |
660 | // If we're entering reader mode, slow the transition
661 | if (readerMode) {
662 | slowTransition(readerMode);
663 | }
664 | highlightReaderToggle(readerMode);
665 | hideOverlappedSidebars();
666 |
667 | // If we're exiting reader mode, restore the non-slow transition
668 | if (!readerMode) {
669 | slowTransition(!readerMode);
670 | }
671 | };
672 |
673 | const highlightReaderToggle = (readerMode) => {
674 | const els = document.querySelectorAll(".quarto-reader-toggle");
675 | if (els) {
676 | els.forEach((el) => {
677 | if (readerMode) {
678 | el.classList.add("reader");
679 | } else {
680 | el.classList.remove("reader");
681 | }
682 | });
683 | }
684 | };
685 |
686 | const setReaderModeValue = (val) => {
687 | if (window.location.protocol !== "file:") {
688 | window.localStorage.setItem("quarto-reader-mode", val);
689 | } else {
690 | localReaderMode = val;
691 | }
692 | };
693 |
694 | const isReaderMode = () => {
695 | if (window.location.protocol !== "file:") {
696 | return window.localStorage.getItem("quarto-reader-mode") === "true";
697 | } else {
698 | return localReaderMode;
699 | }
700 | };
701 | let localReaderMode = null;
702 |
703 | const tocOpenDepthStr = tocEl?.getAttribute("data-toc-expanded");
704 | const tocOpenDepth = tocOpenDepthStr ? Number(tocOpenDepthStr) : 1;
705 |
706 | // Walk the TOC and collapse/expand nodes
707 | // Nodes are expanded if:
708 | // - they are top level
709 | // - they have children that are 'active' links
710 | // - they are directly below an link that is 'active'
711 | const walk = (el, depth) => {
712 | // Tick depth when we enter a UL
713 | if (el.tagName === "UL") {
714 | depth = depth + 1;
715 | }
716 |
717 | // It this is active link
718 | let isActiveNode = false;
719 | if (el.tagName === "A" && el.classList.contains("active")) {
720 | isActiveNode = true;
721 | }
722 |
723 | // See if there is an active child to this element
724 | let hasActiveChild = false;
725 | for (child of el.children) {
726 | hasActiveChild = walk(child, depth) || hasActiveChild;
727 | }
728 |
729 | // Process the collapse state if this is an UL
730 | if (el.tagName === "UL") {
731 | if (tocOpenDepth === -1 && depth > 1) {
732 | el.classList.add("collapse");
733 | } else if (
734 | depth <= tocOpenDepth ||
735 | hasActiveChild ||
736 | prevSiblingIsActiveLink(el)
737 | ) {
738 | el.classList.remove("collapse");
739 | } else {
740 | el.classList.add("collapse");
741 | }
742 |
743 | // untick depth when we leave a UL
744 | depth = depth - 1;
745 | }
746 | return hasActiveChild || isActiveNode;
747 | };
748 |
749 | // walk the TOC and expand / collapse any items that should be shown
750 |
751 | if (tocEl) {
752 | walk(tocEl, 0);
753 | updateActiveLink();
754 | }
755 |
756 | // Throttle the scroll event and walk peridiocally
757 | window.document.addEventListener(
758 | "scroll",
759 | throttle(() => {
760 | if (tocEl) {
761 | updateActiveLink();
762 | walk(tocEl, 0);
763 | }
764 | if (!isReaderMode()) {
765 | hideOverlappedSidebars();
766 | }
767 | }, 5)
768 | );
769 | window.addEventListener(
770 | "resize",
771 | throttle(() => {
772 | if (!isReaderMode()) {
773 | hideOverlappedSidebars();
774 | }
775 | }, 10)
776 | );
777 | hideOverlappedSidebars();
778 | highlightReaderToggle(isReaderMode());
779 | });
780 |
781 | // grouped tabsets
782 | window.addEventListener("pageshow", (_event) => {
783 | function getTabSettings() {
784 | const data = localStorage.getItem("quarto-persistent-tabsets-data");
785 | if (!data) {
786 | localStorage.setItem("quarto-persistent-tabsets-data", "{}");
787 | return {};
788 | }
789 | if (data) {
790 | return JSON.parse(data);
791 | }
792 | }
793 |
794 | function setTabSettings(data) {
795 | localStorage.setItem(
796 | "quarto-persistent-tabsets-data",
797 | JSON.stringify(data)
798 | );
799 | }
800 |
801 | function setTabState(groupName, groupValue) {
802 | const data = getTabSettings();
803 | data[groupName] = groupValue;
804 | setTabSettings(data);
805 | }
806 |
807 | function toggleTab(tab, active) {
808 | const tabPanelId = tab.getAttribute("aria-controls");
809 | const tabPanel = document.getElementById(tabPanelId);
810 | if (active) {
811 | tab.classList.add("active");
812 | tabPanel.classList.add("active");
813 | } else {
814 | tab.classList.remove("active");
815 | tabPanel.classList.remove("active");
816 | }
817 | }
818 |
819 | function toggleAll(selectedGroup, selectorsToSync) {
820 | for (const [thisGroup, tabs] of Object.entries(selectorsToSync)) {
821 | const active = selectedGroup === thisGroup;
822 | for (const tab of tabs) {
823 | toggleTab(tab, active);
824 | }
825 | }
826 | }
827 |
828 | function findSelectorsToSyncByLanguage() {
829 | const result = {};
830 | const tabs = Array.from(
831 | document.querySelectorAll(`div[data-group] a[id^='tabset-']`)
832 | );
833 | for (const item of tabs) {
834 | const div = item.parentElement.parentElement.parentElement;
835 | const group = div.getAttribute("data-group");
836 | if (!result[group]) {
837 | result[group] = {};
838 | }
839 | const selectorsToSync = result[group];
840 | const value = item.innerHTML;
841 | if (!selectorsToSync[value]) {
842 | selectorsToSync[value] = [];
843 | }
844 | selectorsToSync[value].push(item);
845 | }
846 | return result;
847 | }
848 |
849 | function setupSelectorSync() {
850 | const selectorsToSync = findSelectorsToSyncByLanguage();
851 | Object.entries(selectorsToSync).forEach(([group, tabSetsByValue]) => {
852 | Object.entries(tabSetsByValue).forEach(([value, items]) => {
853 | items.forEach((item) => {
854 | item.addEventListener("click", (_event) => {
855 | setTabState(group, value);
856 | toggleAll(value, selectorsToSync[group]);
857 | });
858 | });
859 | });
860 | });
861 | return selectorsToSync;
862 | }
863 |
864 | const selectorsToSync = setupSelectorSync();
865 | for (const [group, selectedName] of Object.entries(getTabSettings())) {
866 | const selectors = selectorsToSync[group];
867 | // it's possible that stale state gives us empty selections, so we explicitly check here.
868 | if (selectors) {
869 | toggleAll(selectedName, selectors);
870 | }
871 | }
872 | });
873 |
874 | function throttle(func, wait) {
875 | let waiting = false;
876 | return function () {
877 | if (!waiting) {
878 | func.apply(this, arguments);
879 | waiting = true;
880 | setTimeout(function () {
881 | waiting = false;
882 | }, wait);
883 | }
884 | };
885 | }
886 |
887 | function nexttick(func) {
888 | return setTimeout(func, 0);
889 | }
890 |
--------------------------------------------------------------------------------
/dashboard/dashboard_files/libs/quarto-html/tippy.css:
--------------------------------------------------------------------------------
1 | .tippy-box[data-animation=fade][data-state=hidden]{opacity:0}[data-tippy-root]{max-width:calc(100vw - 10px)}.tippy-box{position:relative;background-color:#333;color:#fff;border-radius:4px;font-size:14px;line-height:1.4;white-space:normal;outline:0;transition-property:transform,visibility,opacity}.tippy-box[data-placement^=top]>.tippy-arrow{bottom:0}.tippy-box[data-placement^=top]>.tippy-arrow:before{bottom:-7px;left:0;border-width:8px 8px 0;border-top-color:initial;transform-origin:center top}.tippy-box[data-placement^=bottom]>.tippy-arrow{top:0}.tippy-box[data-placement^=bottom]>.tippy-arrow:before{top:-7px;left:0;border-width:0 8px 8px;border-bottom-color:initial;transform-origin:center bottom}.tippy-box[data-placement^=left]>.tippy-arrow{right:0}.tippy-box[data-placement^=left]>.tippy-arrow:before{border-width:8px 0 8px 8px;border-left-color:initial;right:-7px;transform-origin:center left}.tippy-box[data-placement^=right]>.tippy-arrow{left:0}.tippy-box[data-placement^=right]>.tippy-arrow:before{left:-7px;border-width:8px 8px 8px 0;border-right-color:initial;transform-origin:center right}.tippy-box[data-inertia][data-state=visible]{transition-timing-function:cubic-bezier(.54,1.5,.38,1.11)}.tippy-arrow{width:16px;height:16px;color:#333}.tippy-arrow:before{content:"";position:absolute;border-color:transparent;border-style:solid}.tippy-content{position:relative;padding:5px 9px;z-index:1}
--------------------------------------------------------------------------------
/dashboard/dashboard_files/libs/quarto-html/tippy.umd.min.js:
--------------------------------------------------------------------------------
1 | !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t(require("@popperjs/core")):"function"==typeof define&&define.amd?define(["@popperjs/core"],t):(e=e||self).tippy=t(e.Popper)}(this,(function(e){"use strict";var t={passive:!0,capture:!0},n=function(){return document.body};function r(e,t,n){if(Array.isArray(e)){var r=e[t];return null==r?Array.isArray(n)?n[t]:n:r}return e}function o(e,t){var n={}.toString.call(e);return 0===n.indexOf("[object")&&n.indexOf(t+"]")>-1}function i(e,t){return"function"==typeof e?e.apply(void 0,t):e}function a(e,t){return 0===t?e:function(r){clearTimeout(n),n=setTimeout((function(){e(r)}),t)};var n}function s(e,t){var n=Object.assign({},e);return t.forEach((function(e){delete n[e]})),n}function u(e){return[].concat(e)}function c(e,t){-1===e.indexOf(t)&&e.push(t)}function p(e){return e.split("-")[0]}function f(e){return[].slice.call(e)}function l(e){return Object.keys(e).reduce((function(t,n){return void 0!==e[n]&&(t[n]=e[n]),t}),{})}function d(){return document.createElement("div")}function v(e){return["Element","Fragment"].some((function(t){return o(e,t)}))}function m(e){return o(e,"MouseEvent")}function g(e){return!(!e||!e._tippy||e._tippy.reference!==e)}function h(e){return v(e)?[e]:function(e){return o(e,"NodeList")}(e)?f(e):Array.isArray(e)?e:f(document.querySelectorAll(e))}function b(e,t){e.forEach((function(e){e&&(e.style.transitionDuration=t+"ms")}))}function y(e,t){e.forEach((function(e){e&&e.setAttribute("data-state",t)}))}function w(e){var t,n=u(e)[0];return null!=n&&null!=(t=n.ownerDocument)&&t.body?n.ownerDocument:document}function E(e,t,n){var r=t+"EventListener";["transitionend","webkitTransitionEnd"].forEach((function(t){e[r](t,n)}))}function O(e,t){for(var n=t;n;){var r;if(e.contains(n))return!0;n=null==n.getRootNode||null==(r=n.getRootNode())?void 0:r.host}return!1}var x={isTouch:!1},C=0;function T(){x.isTouch||(x.isTouch=!0,window.performance&&document.addEventListener("mousemove",A))}function A(){var e=performance.now();e-C<20&&(x.isTouch=!1,document.removeEventListener("mousemove",A)),C=e}function L(){var e=document.activeElement;if(g(e)){var t=e._tippy;e.blur&&!t.state.isVisible&&e.blur()}}var D=!!("undefined"!=typeof window&&"undefined"!=typeof document)&&!!window.msCrypto,R=Object.assign({appendTo:n,aria:{content:"auto",expanded:"auto"},delay:0,duration:[300,250],getReferenceClientRect:null,hideOnClick:!0,ignoreAttributes:!1,interactive:!1,interactiveBorder:2,interactiveDebounce:0,moveTransition:"",offset:[0,10],onAfterUpdate:function(){},onBeforeUpdate:function(){},onCreate:function(){},onDestroy:function(){},onHidden:function(){},onHide:function(){},onMount:function(){},onShow:function(){},onShown:function(){},onTrigger:function(){},onUntrigger:function(){},onClickOutside:function(){},placement:"top",plugins:[],popperOptions:{},render:null,showOnCreate:!1,touch:!0,trigger:"mouseenter focus",triggerTarget:null},{animateFill:!1,followCursor:!1,inlinePositioning:!1,sticky:!1},{allowHTML:!1,animation:"fade",arrow:!0,content:"",inertia:!1,maxWidth:350,role:"tooltip",theme:"",zIndex:9999}),k=Object.keys(R);function P(e){var t=(e.plugins||[]).reduce((function(t,n){var r,o=n.name,i=n.defaultValue;o&&(t[o]=void 0!==e[o]?e[o]:null!=(r=R[o])?r:i);return t}),{});return Object.assign({},e,t)}function j(e,t){var n=Object.assign({},t,{content:i(t.content,[e])},t.ignoreAttributes?{}:function(e,t){return(t?Object.keys(P(Object.assign({},R,{plugins:t}))):k).reduce((function(t,n){var r=(e.getAttribute("data-tippy-"+n)||"").trim();if(!r)return t;if("content"===n)t[n]=r;else try{t[n]=JSON.parse(r)}catch(e){t[n]=r}return t}),{})}(e,t.plugins));return n.aria=Object.assign({},R.aria,n.aria),n.aria={expanded:"auto"===n.aria.expanded?t.interactive:n.aria.expanded,content:"auto"===n.aria.content?t.interactive?null:"describedby":n.aria.content},n}function M(e,t){e.innerHTML=t}function V(e){var t=d();return!0===e?t.className="tippy-arrow":(t.className="tippy-svg-arrow",v(e)?t.appendChild(e):M(t,e)),t}function I(e,t){v(t.content)?(M(e,""),e.appendChild(t.content)):"function"!=typeof t.content&&(t.allowHTML?M(e,t.content):e.textContent=t.content)}function S(e){var t=e.firstElementChild,n=f(t.children);return{box:t,content:n.find((function(e){return e.classList.contains("tippy-content")})),arrow:n.find((function(e){return e.classList.contains("tippy-arrow")||e.classList.contains("tippy-svg-arrow")})),backdrop:n.find((function(e){return e.classList.contains("tippy-backdrop")}))}}function N(e){var t=d(),n=d();n.className="tippy-box",n.setAttribute("data-state","hidden"),n.setAttribute("tabindex","-1");var r=d();function o(n,r){var o=S(t),i=o.box,a=o.content,s=o.arrow;r.theme?i.setAttribute("data-theme",r.theme):i.removeAttribute("data-theme"),"string"==typeof r.animation?i.setAttribute("data-animation",r.animation):i.removeAttribute("data-animation"),r.inertia?i.setAttribute("data-inertia",""):i.removeAttribute("data-inertia"),i.style.maxWidth="number"==typeof r.maxWidth?r.maxWidth+"px":r.maxWidth,r.role?i.setAttribute("role",r.role):i.removeAttribute("role"),n.content===r.content&&n.allowHTML===r.allowHTML||I(a,e.props),r.arrow?s?n.arrow!==r.arrow&&(i.removeChild(s),i.appendChild(V(r.arrow))):i.appendChild(V(r.arrow)):s&&i.removeChild(s)}return r.className="tippy-content",r.setAttribute("data-state","hidden"),I(r,e.props),t.appendChild(n),n.appendChild(r),o(e.props,e.props),{popper:t,onUpdate:o}}N.$$tippy=!0;var B=1,H=[],U=[];function _(o,s){var v,g,h,C,T,A,L,k,M=j(o,Object.assign({},R,P(l(s)))),V=!1,I=!1,N=!1,_=!1,F=[],W=a(we,M.interactiveDebounce),X=B++,Y=(k=M.plugins).filter((function(e,t){return k.indexOf(e)===t})),$={id:X,reference:o,popper:d(),popperInstance:null,props:M,state:{isEnabled:!0,isVisible:!1,isDestroyed:!1,isMounted:!1,isShown:!1},plugins:Y,clearDelayTimeouts:function(){clearTimeout(v),clearTimeout(g),cancelAnimationFrame(h)},setProps:function(e){if($.state.isDestroyed)return;ae("onBeforeUpdate",[$,e]),be();var t=$.props,n=j(o,Object.assign({},t,l(e),{ignoreAttributes:!0}));$.props=n,he(),t.interactiveDebounce!==n.interactiveDebounce&&(ce(),W=a(we,n.interactiveDebounce));t.triggerTarget&&!n.triggerTarget?u(t.triggerTarget).forEach((function(e){e.removeAttribute("aria-expanded")})):n.triggerTarget&&o.removeAttribute("aria-expanded");ue(),ie(),J&&J(t,n);$.popperInstance&&(Ce(),Ae().forEach((function(e){requestAnimationFrame(e._tippy.popperInstance.forceUpdate)})));ae("onAfterUpdate",[$,e])},setContent:function(e){$.setProps({content:e})},show:function(){var e=$.state.isVisible,t=$.state.isDestroyed,o=!$.state.isEnabled,a=x.isTouch&&!$.props.touch,s=r($.props.duration,0,R.duration);if(e||t||o||a)return;if(te().hasAttribute("disabled"))return;if(ae("onShow",[$],!1),!1===$.props.onShow($))return;$.state.isVisible=!0,ee()&&(z.style.visibility="visible");ie(),de(),$.state.isMounted||(z.style.transition="none");if(ee()){var u=re(),p=u.box,f=u.content;b([p,f],0)}A=function(){var e;if($.state.isVisible&&!_){if(_=!0,z.offsetHeight,z.style.transition=$.props.moveTransition,ee()&&$.props.animation){var t=re(),n=t.box,r=t.content;b([n,r],s),y([n,r],"visible")}se(),ue(),c(U,$),null==(e=$.popperInstance)||e.forceUpdate(),ae("onMount",[$]),$.props.animation&&ee()&&function(e,t){me(e,t)}(s,(function(){$.state.isShown=!0,ae("onShown",[$])}))}},function(){var e,t=$.props.appendTo,r=te();e=$.props.interactive&&t===n||"parent"===t?r.parentNode:i(t,[r]);e.contains(z)||e.appendChild(z);$.state.isMounted=!0,Ce()}()},hide:function(){var e=!$.state.isVisible,t=$.state.isDestroyed,n=!$.state.isEnabled,o=r($.props.duration,1,R.duration);if(e||t||n)return;if(ae("onHide",[$],!1),!1===$.props.onHide($))return;$.state.isVisible=!1,$.state.isShown=!1,_=!1,V=!1,ee()&&(z.style.visibility="hidden");if(ce(),ve(),ie(!0),ee()){var i=re(),a=i.box,s=i.content;$.props.animation&&(b([a,s],o),y([a,s],"hidden"))}se(),ue(),$.props.animation?ee()&&function(e,t){me(e,(function(){!$.state.isVisible&&z.parentNode&&z.parentNode.contains(z)&&t()}))}(o,$.unmount):$.unmount()},hideWithInteractivity:function(e){ne().addEventListener("mousemove",W),c(H,W),W(e)},enable:function(){$.state.isEnabled=!0},disable:function(){$.hide(),$.state.isEnabled=!1},unmount:function(){$.state.isVisible&&$.hide();if(!$.state.isMounted)return;Te(),Ae().forEach((function(e){e._tippy.unmount()})),z.parentNode&&z.parentNode.removeChild(z);U=U.filter((function(e){return e!==$})),$.state.isMounted=!1,ae("onHidden",[$])},destroy:function(){if($.state.isDestroyed)return;$.clearDelayTimeouts(),$.unmount(),be(),delete o._tippy,$.state.isDestroyed=!0,ae("onDestroy",[$])}};if(!M.render)return $;var q=M.render($),z=q.popper,J=q.onUpdate;z.setAttribute("data-tippy-root",""),z.id="tippy-"+$.id,$.popper=z,o._tippy=$,z._tippy=$;var G=Y.map((function(e){return e.fn($)})),K=o.hasAttribute("aria-expanded");return he(),ue(),ie(),ae("onCreate",[$]),M.showOnCreate&&Le(),z.addEventListener("mouseenter",(function(){$.props.interactive&&$.state.isVisible&&$.clearDelayTimeouts()})),z.addEventListener("mouseleave",(function(){$.props.interactive&&$.props.trigger.indexOf("mouseenter")>=0&&ne().addEventListener("mousemove",W)})),$;function Q(){var e=$.props.touch;return Array.isArray(e)?e:[e,0]}function Z(){return"hold"===Q()[0]}function ee(){var e;return!(null==(e=$.props.render)||!e.$$tippy)}function te(){return L||o}function ne(){var e=te().parentNode;return e?w(e):document}function re(){return S(z)}function oe(e){return $.state.isMounted&&!$.state.isVisible||x.isTouch||C&&"focus"===C.type?0:r($.props.delay,e?0:1,R.delay)}function ie(e){void 0===e&&(e=!1),z.style.pointerEvents=$.props.interactive&&!e?"":"none",z.style.zIndex=""+$.props.zIndex}function ae(e,t,n){var r;(void 0===n&&(n=!0),G.forEach((function(n){n[e]&&n[e].apply(n,t)})),n)&&(r=$.props)[e].apply(r,t)}function se(){var e=$.props.aria;if(e.content){var t="aria-"+e.content,n=z.id;u($.props.triggerTarget||o).forEach((function(e){var r=e.getAttribute(t);if($.state.isVisible)e.setAttribute(t,r?r+" "+n:n);else{var o=r&&r.replace(n,"").trim();o?e.setAttribute(t,o):e.removeAttribute(t)}}))}}function ue(){!K&&$.props.aria.expanded&&u($.props.triggerTarget||o).forEach((function(e){$.props.interactive?e.setAttribute("aria-expanded",$.state.isVisible&&e===te()?"true":"false"):e.removeAttribute("aria-expanded")}))}function ce(){ne().removeEventListener("mousemove",W),H=H.filter((function(e){return e!==W}))}function pe(e){if(!x.isTouch||!N&&"mousedown"!==e.type){var t=e.composedPath&&e.composedPath()[0]||e.target;if(!$.props.interactive||!O(z,t)){if(u($.props.triggerTarget||o).some((function(e){return O(e,t)}))){if(x.isTouch)return;if($.state.isVisible&&$.props.trigger.indexOf("click")>=0)return}else ae("onClickOutside",[$,e]);!0===$.props.hideOnClick&&($.clearDelayTimeouts(),$.hide(),I=!0,setTimeout((function(){I=!1})),$.state.isMounted||ve())}}}function fe(){N=!0}function le(){N=!1}function de(){var e=ne();e.addEventListener("mousedown",pe,!0),e.addEventListener("touchend",pe,t),e.addEventListener("touchstart",le,t),e.addEventListener("touchmove",fe,t)}function ve(){var e=ne();e.removeEventListener("mousedown",pe,!0),e.removeEventListener("touchend",pe,t),e.removeEventListener("touchstart",le,t),e.removeEventListener("touchmove",fe,t)}function me(e,t){var n=re().box;function r(e){e.target===n&&(E(n,"remove",r),t())}if(0===e)return t();E(n,"remove",T),E(n,"add",r),T=r}function ge(e,t,n){void 0===n&&(n=!1),u($.props.triggerTarget||o).forEach((function(r){r.addEventListener(e,t,n),F.push({node:r,eventType:e,handler:t,options:n})}))}function he(){var e;Z()&&(ge("touchstart",ye,{passive:!0}),ge("touchend",Ee,{passive:!0})),(e=$.props.trigger,e.split(/\s+/).filter(Boolean)).forEach((function(e){if("manual"!==e)switch(ge(e,ye),e){case"mouseenter":ge("mouseleave",Ee);break;case"focus":ge(D?"focusout":"blur",Oe);break;case"focusin":ge("focusout",Oe)}}))}function be(){F.forEach((function(e){var t=e.node,n=e.eventType,r=e.handler,o=e.options;t.removeEventListener(n,r,o)})),F=[]}function ye(e){var t,n=!1;if($.state.isEnabled&&!xe(e)&&!I){var r="focus"===(null==(t=C)?void 0:t.type);C=e,L=e.currentTarget,ue(),!$.state.isVisible&&m(e)&&H.forEach((function(t){return t(e)})),"click"===e.type&&($.props.trigger.indexOf("mouseenter")<0||V)&&!1!==$.props.hideOnClick&&$.state.isVisible?n=!0:Le(e),"click"===e.type&&(V=!n),n&&!r&&De(e)}}function we(e){var t=e.target,n=te().contains(t)||z.contains(t);"mousemove"===e.type&&n||function(e,t){var n=t.clientX,r=t.clientY;return e.every((function(e){var t=e.popperRect,o=e.popperState,i=e.props.interactiveBorder,a=p(o.placement),s=o.modifiersData.offset;if(!s)return!0;var u="bottom"===a?s.top.y:0,c="top"===a?s.bottom.y:0,f="right"===a?s.left.x:0,l="left"===a?s.right.x:0,d=t.top-r+u>i,v=r-t.bottom-c>i,m=t.left-n+f>i,g=n-t.right-l>i;return d||v||m||g}))}(Ae().concat(z).map((function(e){var t,n=null==(t=e._tippy.popperInstance)?void 0:t.state;return n?{popperRect:e.getBoundingClientRect(),popperState:n,props:M}:null})).filter(Boolean),e)&&(ce(),De(e))}function Ee(e){xe(e)||$.props.trigger.indexOf("click")>=0&&V||($.props.interactive?$.hideWithInteractivity(e):De(e))}function Oe(e){$.props.trigger.indexOf("focusin")<0&&e.target!==te()||$.props.interactive&&e.relatedTarget&&z.contains(e.relatedTarget)||De(e)}function xe(e){return!!x.isTouch&&Z()!==e.type.indexOf("touch")>=0}function Ce(){Te();var t=$.props,n=t.popperOptions,r=t.placement,i=t.offset,a=t.getReferenceClientRect,s=t.moveTransition,u=ee()?S(z).arrow:null,c=a?{getBoundingClientRect:a,contextElement:a.contextElement||te()}:o,p=[{name:"offset",options:{offset:i}},{name:"preventOverflow",options:{padding:{top:2,bottom:2,left:5,right:5}}},{name:"flip",options:{padding:5}},{name:"computeStyles",options:{adaptive:!s}},{name:"$$tippy",enabled:!0,phase:"beforeWrite",requires:["computeStyles"],fn:function(e){var t=e.state;if(ee()){var n=re().box;["placement","reference-hidden","escaped"].forEach((function(e){"placement"===e?n.setAttribute("data-placement",t.placement):t.attributes.popper["data-popper-"+e]?n.setAttribute("data-"+e,""):n.removeAttribute("data-"+e)})),t.attributes.popper={}}}}];ee()&&u&&p.push({name:"arrow",options:{element:u,padding:3}}),p.push.apply(p,(null==n?void 0:n.modifiers)||[]),$.popperInstance=e.createPopper(c,z,Object.assign({},n,{placement:r,onFirstUpdate:A,modifiers:p}))}function Te(){$.popperInstance&&($.popperInstance.destroy(),$.popperInstance=null)}function Ae(){return f(z.querySelectorAll("[data-tippy-root]"))}function Le(e){$.clearDelayTimeouts(),e&&ae("onTrigger",[$,e]),de();var t=oe(!0),n=Q(),r=n[0],o=n[1];x.isTouch&&"hold"===r&&o&&(t=o),t?v=setTimeout((function(){$.show()}),t):$.show()}function De(e){if($.clearDelayTimeouts(),ae("onUntrigger",[$,e]),$.state.isVisible){if(!($.props.trigger.indexOf("mouseenter")>=0&&$.props.trigger.indexOf("click")>=0&&["mouseleave","mousemove"].indexOf(e.type)>=0&&V)){var t=oe(!1);t?g=setTimeout((function(){$.state.isVisible&&$.hide()}),t):h=requestAnimationFrame((function(){$.hide()}))}}else ve()}}function F(e,n){void 0===n&&(n={});var r=R.plugins.concat(n.plugins||[]);document.addEventListener("touchstart",T,t),window.addEventListener("blur",L);var o=Object.assign({},n,{plugins:r}),i=h(e).reduce((function(e,t){var n=t&&_(t,o);return n&&e.push(n),e}),[]);return v(e)?i[0]:i}F.defaultProps=R,F.setDefaultProps=function(e){Object.keys(e).forEach((function(t){R[t]=e[t]}))},F.currentInput=x;var W=Object.assign({},e.applyStyles,{effect:function(e){var t=e.state,n={popper:{position:t.options.strategy,left:"0",top:"0",margin:"0"},arrow:{position:"absolute"},reference:{}};Object.assign(t.elements.popper.style,n.popper),t.styles=n,t.elements.arrow&&Object.assign(t.elements.arrow.style,n.arrow)}}),X={mouseover:"mouseenter",focusin:"focus",click:"click"};var Y={name:"animateFill",defaultValue:!1,fn:function(e){var t;if(null==(t=e.props.render)||!t.$$tippy)return{};var n=S(e.popper),r=n.box,o=n.content,i=e.props.animateFill?function(){var e=d();return e.className="tippy-backdrop",y([e],"hidden"),e}():null;return{onCreate:function(){i&&(r.insertBefore(i,r.firstElementChild),r.setAttribute("data-animatefill",""),r.style.overflow="hidden",e.setProps({arrow:!1,animation:"shift-away"}))},onMount:function(){if(i){var e=r.style.transitionDuration,t=Number(e.replace("ms",""));o.style.transitionDelay=Math.round(t/10)+"ms",i.style.transitionDuration=e,y([i],"visible")}},onShow:function(){i&&(i.style.transitionDuration="0ms")},onHide:function(){i&&y([i],"hidden")}}}};var $={clientX:0,clientY:0},q=[];function z(e){var t=e.clientX,n=e.clientY;$={clientX:t,clientY:n}}var J={name:"followCursor",defaultValue:!1,fn:function(e){var t=e.reference,n=w(e.props.triggerTarget||t),r=!1,o=!1,i=!0,a=e.props;function s(){return"initial"===e.props.followCursor&&e.state.isVisible}function u(){n.addEventListener("mousemove",f)}function c(){n.removeEventListener("mousemove",f)}function p(){r=!0,e.setProps({getReferenceClientRect:null}),r=!1}function f(n){var r=!n.target||t.contains(n.target),o=e.props.followCursor,i=n.clientX,a=n.clientY,s=t.getBoundingClientRect(),u=i-s.left,c=a-s.top;!r&&e.props.interactive||e.setProps({getReferenceClientRect:function(){var e=t.getBoundingClientRect(),n=i,r=a;"initial"===o&&(n=e.left+u,r=e.top+c);var s="horizontal"===o?e.top:r,p="vertical"===o?e.right:n,f="horizontal"===o?e.bottom:r,l="vertical"===o?e.left:n;return{width:p-l,height:f-s,top:s,right:p,bottom:f,left:l}}})}function l(){e.props.followCursor&&(q.push({instance:e,doc:n}),function(e){e.addEventListener("mousemove",z)}(n))}function d(){0===(q=q.filter((function(t){return t.instance!==e}))).filter((function(e){return e.doc===n})).length&&function(e){e.removeEventListener("mousemove",z)}(n)}return{onCreate:l,onDestroy:d,onBeforeUpdate:function(){a=e.props},onAfterUpdate:function(t,n){var i=n.followCursor;r||void 0!==i&&a.followCursor!==i&&(d(),i?(l(),!e.state.isMounted||o||s()||u()):(c(),p()))},onMount:function(){e.props.followCursor&&!o&&(i&&(f($),i=!1),s()||u())},onTrigger:function(e,t){m(t)&&($={clientX:t.clientX,clientY:t.clientY}),o="focus"===t.type},onHidden:function(){e.props.followCursor&&(p(),c(),i=!0)}}}};var G={name:"inlinePositioning",defaultValue:!1,fn:function(e){var t,n=e.reference;var r=-1,o=!1,i=[],a={name:"tippyInlinePositioning",enabled:!0,phase:"afterWrite",fn:function(o){var a=o.state;e.props.inlinePositioning&&(-1!==i.indexOf(a.placement)&&(i=[]),t!==a.placement&&-1===i.indexOf(a.placement)&&(i.push(a.placement),e.setProps({getReferenceClientRect:function(){return function(e){return function(e,t,n,r){if(n.length<2||null===e)return t;if(2===n.length&&r>=0&&n[0].left>n[1].right)return n[r]||t;switch(e){case"top":case"bottom":var o=n[0],i=n[n.length-1],a="top"===e,s=o.top,u=i.bottom,c=a?o.left:i.left,p=a?o.right:i.right;return{top:s,bottom:u,left:c,right:p,width:p-c,height:u-s};case"left":case"right":var f=Math.min.apply(Math,n.map((function(e){return e.left}))),l=Math.max.apply(Math,n.map((function(e){return e.right}))),d=n.filter((function(t){return"left"===e?t.left===f:t.right===l})),v=d[0].top,m=d[d.length-1].bottom;return{top:v,bottom:m,left:f,right:l,width:l-f,height:m-v};default:return t}}(p(e),n.getBoundingClientRect(),f(n.getClientRects()),r)}(a.placement)}})),t=a.placement)}};function s(){var t;o||(t=function(e,t){var n;return{popperOptions:Object.assign({},e.popperOptions,{modifiers:[].concat(((null==(n=e.popperOptions)?void 0:n.modifiers)||[]).filter((function(e){return e.name!==t.name})),[t])})}}(e.props,a),o=!0,e.setProps(t),o=!1)}return{onCreate:s,onAfterUpdate:s,onTrigger:function(t,n){if(m(n)){var o=f(e.reference.getClientRects()),i=o.find((function(e){return e.left-2<=n.clientX&&e.right+2>=n.clientX&&e.top-2<=n.clientY&&e.bottom+2>=n.clientY})),a=o.indexOf(i);r=a>-1?a:r}},onHidden:function(){r=-1}}}};var K={name:"sticky",defaultValue:!1,fn:function(e){var t=e.reference,n=e.popper;function r(t){return!0===e.props.sticky||e.props.sticky===t}var o=null,i=null;function a(){var s=r("reference")?(e.popperInstance?e.popperInstance.state.elements.reference:t).getBoundingClientRect():null,u=r("popper")?n.getBoundingClientRect():null;(s&&Q(o,s)||u&&Q(i,u))&&e.popperInstance&&e.popperInstance.update(),o=s,i=u,e.state.isMounted&&requestAnimationFrame(a)}return{onMount:function(){e.props.sticky&&a()}}}};function Q(e,t){return!e||!t||(e.top!==t.top||e.right!==t.right||e.bottom!==t.bottom||e.left!==t.left)}return F.setDefaultProps({plugins:[Y,J,G,K],render:N}),F.createSingleton=function(e,t){var n;void 0===t&&(t={});var r,o=e,i=[],a=[],c=t.overrides,p=[],f=!1;function l(){a=o.map((function(e){return u(e.props.triggerTarget||e.reference)})).reduce((function(e,t){return e.concat(t)}),[])}function v(){i=o.map((function(e){return e.reference}))}function m(e){o.forEach((function(t){e?t.enable():t.disable()}))}function g(e){return o.map((function(t){var n=t.setProps;return t.setProps=function(o){n(o),t.reference===r&&e.setProps(o)},function(){t.setProps=n}}))}function h(e,t){var n=a.indexOf(t);if(t!==r){r=t;var s=(c||[]).concat("content").reduce((function(e,t){return e[t]=o[n].props[t],e}),{});e.setProps(Object.assign({},s,{getReferenceClientRect:"function"==typeof s.getReferenceClientRect?s.getReferenceClientRect:function(){var e;return null==(e=i[n])?void 0:e.getBoundingClientRect()}}))}}m(!1),v(),l();var b={fn:function(){return{onDestroy:function(){m(!0)},onHidden:function(){r=null},onClickOutside:function(e){e.props.showOnCreate&&!f&&(f=!0,r=null)},onShow:function(e){e.props.showOnCreate&&!f&&(f=!0,h(e,i[0]))},onTrigger:function(e,t){h(e,t.currentTarget)}}}},y=F(d(),Object.assign({},s(t,["overrides"]),{plugins:[b].concat(t.plugins||[]),triggerTarget:a,popperOptions:Object.assign({},t.popperOptions,{modifiers:[].concat((null==(n=t.popperOptions)?void 0:n.modifiers)||[],[W])})})),w=y.show;y.show=function(e){if(w(),!r&&null==e)return h(y,i[0]);if(!r||null!=e){if("number"==typeof e)return i[e]&&h(y,i[e]);if(o.indexOf(e)>=0){var t=e.reference;return h(y,t)}return i.indexOf(e)>=0?h(y,e):void 0}},y.showNext=function(){var e=i[0];if(!r)return y.show(0);var t=i.indexOf(r);y.show(i[t+1]||e)},y.showPrevious=function(){var e=i[i.length-1];if(!r)return y.show(e);var t=i.indexOf(r),n=i[t-1]||e;y.show(n)};var E=y.setProps;return y.setProps=function(e){c=e.overrides||c,E(e)},y.setInstances=function(e){m(!0),p.forEach((function(e){return e()})),o=e,m(!1),v(),l(),p=g(y),y.setProps({triggerTarget:a})},p=g(y),y},F.delegate=function(e,n){var r=[],o=[],i=!1,a=n.target,c=s(n,["target"]),p=Object.assign({},c,{trigger:"manual",touch:!1}),f=Object.assign({touch:R.touch},c,{showOnCreate:!0}),l=F(e,p);function d(e){if(e.target&&!i){var t=e.target.closest(a);if(t){var r=t.getAttribute("data-tippy-trigger")||n.trigger||R.trigger;if(!t._tippy&&!("touchstart"===e.type&&"boolean"==typeof f.touch||"touchstart"!==e.type&&r.indexOf(X[e.type])<0)){var s=F(t,f);s&&(o=o.concat(s))}}}}function v(e,t,n,o){void 0===o&&(o=!1),e.addEventListener(t,n,o),r.push({node:e,eventType:t,handler:n,options:o})}return u(l).forEach((function(e){var n=e.destroy,a=e.enable,s=e.disable;e.destroy=function(e){void 0===e&&(e=!0),e&&o.forEach((function(e){e.destroy()})),o=[],r.forEach((function(e){var t=e.node,n=e.eventType,r=e.handler,o=e.options;t.removeEventListener(n,r,o)})),r=[],n()},e.enable=function(){a(),o.forEach((function(e){return e.enable()})),i=!1},e.disable=function(){s(),o.forEach((function(e){return e.disable()})),i=!0},function(e){var n=e.reference;v(n,"touchstart",d,t),v(n,"mouseover",d),v(n,"focusin",d),v(n,"click",d)}(e)})),l},F.hideAll=function(e){var t=void 0===e?{}:e,n=t.exclude,r=t.duration;U.forEach((function(e){var t=!1;if(n&&(t=g(n)?e.reference===n:e.popper===n.popper),!t){var o=e.props.duration;e.setProps({duration:r}),e.hide(),e.state.isDestroyed||e.setProps({duration:o})}}))},F.roundArrow='',F}));
2 |
3 |
--------------------------------------------------------------------------------
/dashboard/dashboard_files/libs/quarto-ojs/quarto-ojs.css:
--------------------------------------------------------------------------------
1 | span.ojs-inline span div {
2 | display: inline-block;
3 | }
4 |
5 | /* add some breathing room between display outputs and text especially */
6 | div.cell + section,
7 | div.cell + h1,
8 | div.cell + h2,
9 | div.cell + h3,
10 | div.cell + h4,
11 | div.cell + h5,
12 | div.cell + h6,
13 | div.cell + p {
14 | margin-top: 1rem;
15 | }
16 |
17 | div[nodetype="declaration"]:not([data-output="all"])
18 | div.observablehq:not(observablehq--error) {
19 | display: none;
20 | }
21 |
22 | /* mimic bootstrap behavior which is always correct in observablehq outputs */
23 | .observablehq a:not([href]):not([class]),
24 | .observablehq a:not([href]):not([class]):hover {
25 | color: inherit;
26 | text-decoration: none;
27 | }
28 |
29 | .observablehq .observablehq--inspect {
30 | --code-fallback: Consolas, monaco, monospace;
31 | font-family: var(--bs-font-monospace, var(--code-fallback));
32 | font-size: 0.8em;
33 | }
34 |
35 | .observablehq--field {
36 | margin-left: 1rem;
37 | }
38 |
39 | .observablehq--caret {
40 | margin-right: 2px;
41 | vertical-align: baseline;
42 | }
43 |
44 | .observablehq--collapsed,
45 | .observablehq--expanded.observablehq--inspect a {
46 | cursor: pointer;
47 | }
48 |
49 | /* classes directly from observable's runtime */
50 | .observablehq--key,
51 | .observablehq--index {
52 | color: var(--quarto-hl-dt-color);
53 | }
54 |
55 | .observablehq--string {
56 | color: var(--quarto-hl-st-color);
57 | }
58 |
59 | .observablehq--bigint,
60 | .observablehq--date,
61 | .observablehq--number,
62 | .observablehq--regexp,
63 | .observablehq--symbol {
64 | color: var(--quarto-hl-dv-color);
65 | }
66 |
67 | .observablehq--null,
68 | .observablehq--boolean,
69 | .observablehq--undefined,
70 | .observablehq--keyword {
71 | color: var(--quarto-hl-kw-color);
72 | }
73 |
74 | /* In addition, their import statements specifically come highlighted by hljs.
75 | (probably some legacy feature of theirs?) We handle those here as well.
76 |
77 | Just to be on the safe side, we select on observable's 'md-pre'
78 | class as well, in case someone else uses hljs and wishes to put
79 | their own highlighting.
80 |
81 | TODO Note that to make our highlighting consistent, we're
82 | overriding the "im" class to present like a keyword. I should make
83 | sure this looks right everywhere, but I don't know how to test it
84 | comprehensively.
85 | */
86 |
87 | code.javascript span.im {
88 | color: var(--quarto-hl-kw-color);
89 | }
90 |
91 | pre.observablehq--md-pre span.hljs-keyword {
92 | color: var(--quarto-hl-kw-color);
93 | }
94 |
95 | pre.observablehq--md-pre span.hljs-string {
96 | color: var(--quarto-hl-st-color);
97 | }
98 |
99 | pre.observablehq--md-pre .span.hljs-date,
100 | pre.observablehq--md-pre .span.hljs-number,
101 | pre.observablehq--md-pre .span.hljs-regexp,
102 | pre.observablehq--md-pre .span.hljs-symbol {
103 | color: var(--quarto-hl-dv-color);
104 | }
105 |
106 | /* Other general niceties, but it's possible that we should do this on a page-by-page basis */
107 |
108 | input {
109 | vertical-align: middle;
110 | }
111 |
112 | input[type="radio"],
113 | input[type="checkbox"] {
114 | margin: 0px 0px 3px 0px;
115 | }
116 |
117 | .observable-in-a-box-waiting-for-module-import {
118 | visibility: hidden;
119 | }
120 |
121 | /* play nicely w/ sidebar layout */
122 | .panel-sidebar .observablehq > form[class^="oi-"] {
123 | flex-wrap: wrap !important;
124 | }
125 |
126 | /* likely that this only makes sense with bootstrap. TODO check with charles */
127 | .observablehq table {
128 | font-size: 0.9em !important;
129 | }
130 |
131 | .quarto-ojs-hide {
132 | display: none;
133 | }
134 |
135 | .quarto-ojs-error-pinpoint {
136 | border-bottom: 2px dotted #e51400;
137 | font-weight: 700;
138 | cursor: pointer;
139 | }
140 |
141 | code span.quarto-ojs-error-pinpoint {
142 | color: inherit;
143 | }
144 |
145 | .observablehq--error .observablehq--inspect {
146 | font-size: 0.875em !important;
147 | }
148 |
149 | .observablehq--error .callout {
150 | margin-bottom: 0;
151 | margin-top: 0;
152 | }
153 |
154 | /* this counteracts the negative margin which observablehq uses in their tables at runtime */
155 | .quarto-ojs-table-fixup {
156 | padding-left: 14px;
157 | }
158 |
159 | div.observablehq input[type="date"] {
160 | border-style: solid;
161 | padding: 0.8em 0.5em 0.8em 0.5em;
162 | border-color: rgba(220, 220, 220, 0.3);
163 | }
164 |
--------------------------------------------------------------------------------
/dashboard/data/tswift.csv:
--------------------------------------------------------------------------------
1 | album_name,ep,album_release,track_number,track_name,artist,featuring,bonus_track,promotional_release,single_release,track_release,danceability,energy,key,loudness,mode,speechiness,acousticness,instrumentalness,liveness,valence,tempo,time_signature,duration_ms,explicit,key_name,mode_name,key_mode,lyrics
2 | Fearless (Taylor's Version),FALSE,4/9/21,26,Bye Bye Baby (Taylor's Version) [From The Vault],Taylor Swift,NA,FALSE,NA,NA,4/9/21,0.624,0.624,5,-7.86,1,0.053900000000000003,0.334,0,0.0995,0.527,80.132,4,242158,FALSE,F,major,F major,
3 | Lover,FALSE,8/23/19,1,I Forgot That You Existed,Taylor Swift,NA,FALSE,NA,NA,8/23/19,0.664,0.316,5,-10.345,1,0.519,0.298,2.03e-6,0.0812,0.541,92.875,4,170640,FALSE,F,major,F major,
4 | Midnights,FALSE,10/21/22,8,Vigilante Shit,Taylor Swift,NA,FALSE,NA,NA,10/21/22,0.87,0.279,4,-11.134,0,0.364,0.16,1.74e-5,0.121,0.16,79.996,4,164801,TRUE,E,minor,E minor,
5 | evermore,FALSE,12/11/20,14,closure,Taylor Swift,NA,FALSE,NA,NA,12/11/20,0.689,0.704,9,-10.813,1,0.245,0.835,4.83e-6,0.134,0.92,151.884,5,180653,FALSE,A,major,A major,
6 | Lover,FALSE,8/23/19,13,False God,Taylor Swift,NA,FALSE,NA,NA,8/23/19,0.739,0.32,11,-10.862,0,0.239,0.736,1.47e-4,0.111,0.351,79.97,4,200307,FALSE,B,minor,B minor,
7 | Midnights,FALSE,10/21/22,18,Glitch,Taylor Swift,NA,TRUE,NA,NA,10/21/22,0.675,0.466,11,-9.738,0,0.201,0.361,0,0.11,0.347,140.864,4,148781,FALSE,B,minor,B minor,
8 | reputation,FALSE,11/10/17,11,Dancing With Our Hands Tied,Taylor Swift,NA,FALSE,NA,NA,11/10/17,0.624,0.691,0,-6.686,0,0.196,0.0604,1.1e-5,0.138,0.284,160.024,4,211507,FALSE,C,minor,C minor,
9 | 1989,FALSE,10/27/14,8,Bad Blood,Taylor Swift,NA,FALSE,NA,NA,10/27/14,0.646,0.794,7,-6.104,1,0.19,0.0885,6.16e-6,0.201,0.287,170.216,4,211933,FALSE,G,major,G major,
10 | Red (Taylor's Version),FALSE,11/12/21,8,We Are Never Ever Getting Back Together (Taylor's Version),Taylor Swift,NA,FALSE,NA,NA,11/12/21,0.567,0.686,7,-6.139,1,0.175,0.0317,1.86e-6,0.0732,0.716,172.014,4,193147,FALSE,G,major,G major,
11 | evermore,FALSE,12/11/20,1,willow,Taylor Swift,NA,FALSE,NA,12/11/20,12/11/20,0.392,0.574,7,-9.195,1,0.17,0.833,0.00179,0.145,0.529,81.112,4,214707,FALSE,G,major,G major,
12 | 1989,FALSE,10/27/14,6,Shake It Off,Taylor Swift,NA,FALSE,NA,8/18/14,8/18/14,0.647,0.8,7,-5.384,1,0.165,0.0647,0,0.334,0.942,160.078,4,219200,FALSE,G,major,G major,
13 | Midnights,FALSE,10/21/22,7,Question...?,Taylor Swift,NA,FALSE,NA,NA,10/21/22,0.751,0.502,7,-8.777,1,0.162,0.226,0,0.297,0.112,108.989,4,210557,TRUE,G,major,G major,
14 | reputation,FALSE,11/10/17,3,I Did Something Bad,Taylor Swift,NA,FALSE,NA,NA,11/10/17,0.696,0.602,0,-6.156,0,0.159,0.0679,2.11e-5,0.0696,0.305,82.989,4,238253,FALSE,C,minor,C minor,
15 | Lover,FALSE,8/23/19,2,Cruel Summer,Taylor Swift,NA,FALSE,NA,NA,8/23/19,0.552,0.702,9,-5.707,1,0.157,0.117,2.06e-5,0.105,0.564,169.994,4,178427,FALSE,A,major,A major,
16 | reputation,FALSE,11/10/17,1,...Ready For It?,Taylor Swift,NA,FALSE,NA,10/24/17,10/24/17,0.613,0.764,2,-6.509,1,0.136,0.0527,0,0.197,0.417,160.015,4,208187,FALSE,D,major,D major,
17 | reputation,FALSE,11/10/17,8,Gorgeous,Taylor Swift,NA,FALSE,10/20/17,NA,10/20/17,0.8,0.535,7,-6.684,1,0.135,0.0713,9.48e-6,0.213,0.451,92.027,4,209680,FALSE,G,major,G major,
18 | reputation,FALSE,11/10/17,9,Getaway Car,Taylor Swift,NA,FALSE,NA,9/27/18,11/10/17,0.562,0.689,2,-6.745,1,0.127,0.00465,2.23e-6,0.0888,0.351,172.054,4,233627,FALSE,D,major,D major,
19 | reputation,FALSE,11/10/17,6,Look What You Made Me Do,Taylor Swift,NA,FALSE,NA,8/24/17,8/24/17,0.766,0.709,9,-6.471,0,0.123,0.204,1.41e-5,0.126,0.506,128.07,4,211853,FALSE,A,minor,A minor,
20 | Midnights,FALSE,10/21/22,19,"Would've, Could've, Should've",Taylor Swift,NA,TRUE,NA,NA,10/21/22,0.483,0.84,7,-6.508,1,0.119,0.43,5.75e-4,0.146,0.55,157.979,4,260362,FALSE,G,major,G major,
21 | Midnights,FALSE,10/21/22,13,Mastermind,Taylor Swift,NA,FALSE,NA,NA,10/21/22,0.661,0.352,4,-14.077,1,0.117,0.571,0.00147,0.0884,0.13,125.934,4,191039,FALSE,E,major,E major,
22 | reputation,FALSE,11/10/17,13,This Is Why We Can't Have Nice Things,Taylor Swift,NA,FALSE,NA,NA,11/10/17,0.567,0.789,5,-5.986,1,0.115,0.0156,0,0.382,0.438,163.96,4,207133,FALSE,F,major,F major,
23 | Red (Taylor's Version),FALSE,11/12/21,24,Babe (Taylor's Version) [From The Vault],Taylor Swift,NA,FALSE,NA,NA,11/12/21,0.584,0.743,4,-7.075,1,0.0931,0.0538,2.83e-6,0.121,0.746,167.844,4,224240,FALSE,E,major,E major,
24 | Lover,FALSE,8/23/19,3,Lover,Taylor Swift,NA,FALSE,NA,8/16/19,8/16/19,0.359,0.543,7,-7.582,1,0.0919,0.492,1.58e-5,0.118,0.453,68.534,4,221307,FALSE,G,major,G major,
25 | Red,FALSE,10/22/12,8,We Are Never Ever Getting Back Together,Taylor Swift,NA,FALSE,NA,8/13/12,8/13/12,0.628,0.676,7,-5.911,1,0.0916,0.00957,2.65e-5,0.102,0.75,85.984,4,191880,FALSE,G,major,G major,
26 | 1989,FALSE,10/27/14,9,Wildest Dreams,Taylor Swift,NA,FALSE,NA,8/31/15,10/27/14,0.55,0.688,8,-7.416,1,0.0897,0.0692,0.00144,0.106,0.465,139.997,4,220440,FALSE,G#,major,G# major,
27 | Midnights,FALSE,10/21/22,17,High Infidelity,Taylor Swift,NA,TRUE,NA,NA,10/21/22,0.646,0.529,10,-10.179,1,0.0864,0.723,0,0.0885,0.755,87.862,4,231475,FALSE,A#,major,A# major,
28 | Speak Now,FALSE,10/25/10,10,Better Than Revenge,Taylor Swift,NA,FALSE,NA,NA,10/25/10,0.516,0.917,11,-3.186,0,0.0827,0.0139,6.37e-6,0.36,0.635,145.821,4,217160,FALSE,B,minor,B minor,
29 | Lover,FALSE,8/23/19,9,Cornelia Street,Taylor Swift,NA,FALSE,NA,NA,8/23/19,0.824,0.624,0,-9.728,1,0.0827,0.781,1.89e-4,0.1,0.248,102.012,4,287267,FALSE,C,major,C major,
30 | folklore,FALSE,7/24/20,15,peace,Taylor Swift,NA,FALSE,NA,NA,7/24/20,0.644,0.284,5,-12.879,1,0.0821,0.916,1.5e-5,0.0909,0.328,150.072,4,234000,TRUE,F,major,F major,
31 | Midnights,FALSE,10/21/22,16,Paris,Taylor Swift,NA,TRUE,NA,NA,10/21/22,0.7,0.509,7,-10.547,1,0.0789,0.112,3.27e-5,0.137,0.345,110.947,4,196259,FALSE,G,major,G major,
32 | Red,FALSE,10/22/12,11,Holy Ground,Taylor Swift,NA,FALSE,NA,NA,10/22/12,0.627,0.816,4,-6.698,1,0.0774,0.0162,0.00208,0.0966,0.648,157.043,4,201853,FALSE,E,major,E major,
33 | Lover,FALSE,8/23/19,7,Miss Americana & The Heartbreak Prince,Taylor Swift,NA,FALSE,NA,NA,8/23/19,0.662,0.747,11,-6.926,0,0.0736,0.028,0.00615,0.138,0.487,150.088,4,234147,FALSE,B,minor,B minor,
34 | reputation,FALSE,11/10/17,7,So It Goes...,Taylor Swift,NA,FALSE,NA,NA,11/10/17,0.574,0.61,2,-7.283,1,0.0732,0.122,1.1e-6,0.13,0.374,74.957,4,227907,FALSE,D,major,D major,
35 | reputation,FALSE,11/10/17,14,Call It What You Want,Taylor Swift,NA,FALSE,11/3/17,NA,11/3/17,0.598,0.504,9,-9.874,1,0.0731,0.186,2.21e-4,0.34,0.252,163.954,4,203507,FALSE,A,major,A major,
36 | 1989,FALSE,10/27/14,16,New Romantics,Taylor Swift,NA,TRUE,3/3/15,2/23/16,10/27/14,0.633,0.889,5,-5.87,1,0.0715,0.00463,4.58e-4,0.0658,0.584,121.956,4,230467,FALSE,F,major,F major,
37 | 1989,FALSE,10/27/14,12,I Know Places,Taylor Swift,NA,FALSE,NA,NA,10/27/14,0.602,0.755,0,-4.991,1,0.071,0.245,0,0.178,0.495,159.965,4,195707,FALSE,C,major,C major,
38 | Midnights,FALSE,10/21/22,6,Midnight Rain,Taylor Swift,NA,FALSE,NA,NA,10/21/22,0.636,0.377,0,-11.721,1,0.0708,0.71,6.51e-5,0.115,0.23,139.966,4,174783,FALSE,C,major,C major,
39 | Midnights,FALSE,10/21/22,20,Dear Reader,Taylor Swift,NA,TRUE,NA,NA,10/21/22,0.562,0.388,0,-12.088,1,0.0705,0.481,0.00145,0.117,0.159,107.747,4,225194,FALSE,C,major,C major,
40 | Midnights,FALSE,10/21/22,1,Lavender Haze,Taylor Swift,NA,FALSE,NA,11/29/22,10/21/22,0.735,0.444,10,-10.519,1,0.0684,0.204,0.0012,0.17,0.0984,97.038,4,202396,TRUE,A#,major,A# major,
41 | reputation,FALSE,11/10/17,5,Delicate,Taylor Swift,NA,FALSE,NA,3/12/18,11/10/17,0.75,0.404,9,-10.178,0,0.0682,0.216,3.57e-4,0.0911,0.0499,95.045,4,232253,FALSE,A,minor,A minor,
42 | Midnights,FALSE,10/21/22,9,Bejeweled,Taylor Swift,NA,FALSE,10/25/22,NA,10/21/22,0.7,0.55,7,-9.13,1,0.0653,0.0661,1.02e-4,0.091,0.412,164.003,4,194166,FALSE,G,major,G major,
43 | Taylor Swift,FALSE,10/24/06,12,I'm Only Me When I'm With You,Taylor Swift,NA,TRUE,NA,NA,3/18/08,0.563,0.934,8,-3.629,1,0.0646,0.00452,8.07e-4,0.103,0.518,143.964,4,213053,FALSE,G#,major,G# major,
44 | Lover,FALSE,8/23/19,12,Soon You'll Get Better,Taylor Swift,The Chicks,FALSE,NA,NA,8/23/19,0.433,0.182,0,-12.566,1,0.0641,0.907,0,0.123,0.421,207.476,4,201587,FALSE,C,major,C major,
45 | Red (Taylor's Version),FALSE,11/12/21,11,Holy Ground (Taylor's Version),Taylor Swift,NA,FALSE,NA,NA,11/12/21,0.622,0.809,9,-5.623,1,0.0638,0.0288,0.00218,0.109,0.511,156.894,4,202960,FALSE,A,major,A major,
46 | Lover,FALSE,8/23/19,10,Death By A Thousand Cuts,Taylor Swift,NA,FALSE,NA,NA,8/23/19,0.712,0.732,4,-6.754,1,0.0629,0.454,0,0.319,0.313,94.071,4,198533,FALSE,E,major,E major,
47 | Fearless (Taylor's Version),FALSE,4/9/21,8,Tell Me Why (Taylor's Version),Taylor Swift,NA,FALSE,NA,NA,4/9/21,0.578,0.909,7,-3.669,1,0.0628,0.0222,0,0.333,0.541,100.023,4,200576,FALSE,G,major,G major,
48 | 1989,FALSE,10/27/14,15,You Are In Love,Taylor Swift,NA,TRUE,NA,NA,10/27/14,0.474,0.48,2,-8.894,1,0.0622,0.707,1.08e-4,0.0903,0.319,170.109,4,267107,FALSE,D,major,D major,
49 | evermore,FALSE,12/11/20,9,coney island,Taylor Swift,The National,FALSE,NA,1/18/21,12/11/20,0.537,0.537,8,-11.266,1,0.0617,0.819,9.04e-4,0.142,0.292,107.895,4,275320,FALSE,G#,major,G# major,
50 | Midnights,FALSE,10/21/22,5,"You're On Your Own, Kid",Taylor Swift,NA,FALSE,NA,NA,10/21/22,0.694,0.38,2,-10.307,1,0.0614,0.416,8.47e-6,0.126,0.376,120.044,4,194207,FALSE,D,major,D major,
51 | Midnights,FALSE,10/21/22,11,Karma,Taylor Swift,NA,FALSE,NA,NA,10/21/22,0.649,0.622,8,-7.054,1,0.0582,0.074,0,0.594,0.106,90.024,4,204852,TRUE,G#,major,G# major,
52 | Fearless (Taylor's Version),FALSE,4/9/21,19,The Other Side Of The Door (Taylor's Version),Taylor Swift,NA,FALSE,NA,NA,4/9/21,0.432,0.873,4,-4.367,1,0.0577,0.0279,0,0.167,0.629,163.936,4,238444,FALSE,E,major,E major,
53 | Lover,FALSE,8/23/19,16,ME!,Taylor Swift,"Brendon Urie, Panic! At The Disco",FALSE,NA,4/26/19,4/26/19,0.61,0.83,0,-4.105,1,0.0571,0.033,0,0.118,0.728,182.162,4,193000,FALSE,C,major,C major,
54 | Speak Now,FALSE,10/25/10,12,Haunted,Taylor Swift,NA,FALSE,NA,NA,10/25/10,0.436,0.95,5,-2.627,1,0.057,0.0826,1.08e-6,0.149,0.352,162.088,4,242080,FALSE,F,major,F major,
55 | Lover,FALSE,8/23/19,6,I Think He Knows,Taylor Swift,NA,FALSE,NA,NA,8/23/19,0.897,0.366,0,-8.029,1,0.0569,0.00889,3.53e-4,0.0715,0.416,100.003,4,173387,FALSE,C,major,C major,
56 | Midnights,FALSE,10/21/22,15,Bigger Than The Whole Sky,Taylor Swift,NA,TRUE,NA,NA,10/21/22,0.422,0.235,6,-12.379,1,0.0568,0.826,0.00648,0.115,0.068,165.71,4,218503,FALSE,F#,major,F# major,
57 | Red (Taylor's Version),FALSE,11/12/21,20,State Of Grace (Acoustic Version) [Taylor's Version],Taylor Swift,NA,FALSE,NA,NA,11/12/21,0.445,0.131,7,-13.778,1,0.0564,0.663,1.57e-6,0.108,0.101,208.918,3,321640,FALSE,G,major,G major,
58 | Lover,FALSE,8/23/19,18,Daylight,Taylor Swift,NA,FALSE,NA,NA,8/23/19,0.557,0.496,0,-9.602,1,0.0563,0.808,1.73e-4,0.0772,0.265,149.983,4,293453,FALSE,C,major,C major,
59 | reputation,FALSE,11/10/17,2,End Game,Taylor Swift,"Ed Sheeran, Future",FALSE,NA,11/14/17,11/10/17,0.649,0.589,2,-6.237,1,0.0558,0.00845,0,0.108,0.151,159.073,4,244827,FALSE,D,major,D major,
60 | Lover,FALSE,8/23/19,14,You Need To Calm Down,Taylor Swift,NA,FALSE,NA,6/14/19,6/14/19,0.771,0.671,2,-5.617,1,0.0553,0.00929,0,0.0637,0.714,85.026,4,171360,FALSE,D,major,D major,
61 | Fearless,FALSE,11/11/08,11,Forever & Always,Taylor Swift,NA,FALSE,NA,NA,11/11/08,0.605,0.829,10,-4.993,1,0.0549,0.0907,0,0.106,0.514,128.012,4,225333,FALSE,A#,major,A# major,
62 | 1989,FALSE,10/27/14,14,Wonderland,Taylor Swift,NA,TRUE,2/17/15,NA,10/27/14,0.422,0.692,3,-5.447,1,0.0549,0.0493,2.57e-5,0.177,0.197,184.014,4,245560,FALSE,D#,major,D# major,
63 | 1989,FALSE,10/27/14,2,Blank Space,Taylor Swift,NA,FALSE,NA,11/10/14,10/27/14,0.76,0.703,5,-5.412,1,0.054,0.103,0,0.0913,0.57,95.997,4,231827,FALSE,F,major,F major,
64 | Lover,FALSE,8/23/19,4,The Man,Taylor Swift,NA,FALSE,NA,1/27/20,8/23/19,0.777,0.658,0,-5.191,1,0.054,0.0767,0,0.0901,0.633,110.048,4,190360,FALSE,C,major,C major,
65 | 1989,FALSE,10/27/14,7,I Wish You Would,Taylor Swift,NA,FALSE,NA,NA,10/27/14,0.653,0.893,0,-5.966,1,0.0538,0.0158,4.95e-5,0.102,0.513,118.035,4,207440,FALSE,C,major,C major,
66 | Taylor Swift,FALSE,10/24/06,15,Teardrops On My Guitar (Pop Version),Taylor Swift,NA,TRUE,NA,NA,3/18/08,0.459,0.753,10,-3.827,1,0.0537,0.0402,0,0.0863,0.483,199.997,4,179067,FALSE,A#,major,A# major,
67 | Red (Taylor's Version),FALSE,11/12/21,25,Message In A Bottle (Taylor's Version) [From The Vault],Taylor Swift,NA,FALSE,NA,NA,11/12/21,0.622,0.791,7,-6.106,1,0.0535,0.00188,3.72e-6,0.083,0.494,115.915,4,225960,FALSE,G,major,G major,
68 | reputation,FALSE,11/10/17,12,Dress,Taylor Swift,NA,FALSE,NA,NA,11/10/17,0.719,0.469,0,-8.792,1,0.0533,0.0329,0,0.169,0.0851,120.085,4,230373,FALSE,C,major,C major,
69 | Fearless (Taylor's Version),FALSE,4/9/21,22,Mr. Perfectly Fine (Taylor's Version) [From The Vault],Taylor Swift,NA,FALSE,4/7/21,NA,4/7/21,0.66,0.817,11,-6.269,0,0.0521,0.162,0,0.0667,0.714,135.942,4,277592,FALSE,B,minor,B minor,
70 | folklore,FALSE,7/24/20,11,invisible string,Taylor Swift,NA,FALSE,NA,NA,7/24/20,0.651,0.459,4,-11.128,0,0.0507,0.859,6.63e-5,0.105,0.454,83.455,4,252880,FALSE,E,minor,E minor,
71 | Midnights,FALSE,10/21/22,12,Sweet Nothing,Taylor Swift,NA,FALSE,NA,NA,10/21/22,0.334,0.161,0,-14.875,1,0.0506,0.967,4.71e-5,0.115,0.396,176.747,4,188497,FALSE,C,major,C major,
72 | 1989,FALSE,10/27/14,10,How You Get The Girl,Taylor Swift,NA,FALSE,NA,NA,10/27/14,0.765,0.656,5,-6.112,1,0.0504,0.00364,0.00868,0.0918,0.545,119.997,4,247533,FALSE,F,major,F major,
73 | Fearless (Taylor's Version),FALSE,4/9/21,25,Don't You (Taylor's Version) [From The Vault],Taylor Swift,NA,FALSE,NA,NA,4/9/21,0.563,0.473,3,-11.548,1,0.0503,0.514,1.2e-5,0.109,0.405,101.934,4,208609,FALSE,D#,major,D# major,
74 | Red (Taylor's Version),FALSE,11/12/21,1,State Of Grace (Taylor's Version),Taylor Swift,NA,FALSE,NA,NA,11/12/21,0.594,0.713,9,-5.314,1,0.0503,3.28e-4,0,0.114,0.328,129.958,4,295413,FALSE,A,major,A major,
75 | Red (Taylor's Version),FALSE,11/12/21,13,The Lucky One (Taylor's Version),Taylor Swift,NA,FALSE,NA,NA,11/12/21,0.686,0.571,4,-7.138,1,0.05,0.066,0,0.0608,0.538,117.889,4,240467,FALSE,E,major,E major,
76 | Lover,FALSE,8/23/19,11,London Boy,Taylor Swift,NA,FALSE,NA,NA,8/23/19,0.695,0.71,1,-6.639,1,0.05,0.0246,1.04e-4,0.133,0.557,157.925,4,190240,FALSE,C#,major,C# major,
77 | Lover,FALSE,8/23/19,8,Paper Rings,Taylor Swift,NA,FALSE,NA,NA,8/23/19,0.811,0.719,9,-6.553,1,0.0497,0.0129,1.36e-5,0.0742,0.865,103.979,4,222400,FALSE,A,major,A major,
78 | Red (Taylor's Version),FALSE,11/12/21,26,I Bet You Think About Me (Taylor's Version) [From The Vault],Taylor Swift,Chris Stapleton,FALSE,NA,NA,11/12/21,0.391,0.715,0,-4.516,1,0.0495,0.167,0,0.183,0.473,149.654,3,285107,TRUE,C,major,C major,
79 | folklore,FALSE,7/24/20,12,mad woman,Taylor Swift,NA,FALSE,NA,NA,7/24/20,0.593,0.7,3,-9.016,1,0.0492,0.67,6.77e-6,0.116,0.451,141.898,4,237267,TRUE,D#,major,D# major,
80 | evermore,FALSE,12/11/20,5,tolerate it,Taylor Swift,NA,FALSE,NA,NA,12/11/20,0.316,0.361,9,-10.381,1,0.0488,0.878,2.67e-5,0.0797,0.221,74.952,5,245440,TRUE,A,major,A major,
81 | Midnights,FALSE,10/21/22,10,Labyrinth,Taylor Swift,NA,FALSE,NA,NA,10/21/22,0.487,0.313,0,-15.434,1,0.0487,0.791,0.348,0.124,0.141,110.039,4,247962,FALSE,C,major,C major,
82 | Fearless (Taylor's Version),FALSE,4/9/21,5,White Horse (Taylor's Version),Taylor Swift,NA,FALSE,NA,NA,4/9/21,0.423,0.517,0,-7.377,1,0.0486,0.369,0,0.177,0.366,185.262,4,234516,FALSE,C,major,C major,
83 | Fearless (Taylor's Version),FALSE,4/9/21,10,The Way I Loved You (Taylor's Version),Taylor Swift,NA,FALSE,NA,NA,4/9/21,0.402,0.732,5,-4.665,1,0.0484,0.0033,0,0.108,0.472,161.032,4,243137,FALSE,F,major,F major,
84 | evermore,FALSE,12/11/20,13,marjorie,Taylor Swift,NA,FALSE,NA,NA,12/11/20,0.535,0.561,5,-11.609,1,0.0484,0.876,1.36e-4,0.115,0.287,96.103,4,257773,FALSE,F,major,F major,
85 | folklore,FALSE,7/24/20,1,the 1,Taylor Swift,NA,FALSE,10/9/20,NA,7/24/20,0.78,0.361,0,-6.942,1,0.048,0.75,7.14e-6,0.108,0.163,139.902,4,210240,TRUE,C,major,C major,
86 | Speak Now,FALSE,10/25/10,6,Mean,Taylor Swift,NA,FALSE,10/18/10,3/13/11,10/18/10,0.568,0.761,4,-3.987,1,0.0459,0.452,0,0.217,0.789,163.974,4,237733,FALSE,E,major,E major,
87 | Midnights,FALSE,10/21/22,3,Anti-Hero,Taylor Swift,NA,FALSE,NA,10/24/22,10/21/22,0.638,0.634,4,-6.582,1,0.0457,0.133,1.23e-6,0.152,0.519,96.953,4,200690,FALSE,E,major,E major,
88 | Fearless (Taylor's Version),FALSE,4/9/21,11,Forever & Always (Taylor's Version),Taylor Swift,NA,FALSE,NA,NA,4/9/21,0.598,0.821,10,-4.433,1,0.0447,0.0231,0,0.143,0.673,128.03,4,225328,FALSE,A#,major,A# major,
89 | Red,FALSE,10/22/12,2,Red,Taylor Swift,NA,FALSE,10/2/12,6/21/13,10/2/12,0.602,0.896,1,-4.267,0,0.0437,0.0773,8.87e-5,0.091,0.641,124.978,4,220827,FALSE,C#,minor,C# minor,
90 | Midnights,FALSE,10/21/22,4,Snow On The Beach,Taylor Swift,Lana Del Rey,FALSE,NA,NA,10/21/22,0.659,0.323,9,-13.425,1,0.0436,0.735,0.00321,0.116,0.154,110.007,4,256124,TRUE,A,major,A major,
91 | folklore,FALSE,7/24/20,2,cardigan,Taylor Swift,NA,FALSE,NA,7/27/20,7/24/20,0.612,0.58,0,-8.572,0,0.0419,0.525,4.14e-4,0.26,0.534,130.045,4,239560,FALSE,C,minor,C minor,
92 | evermore,FALSE,12/11/20,12,long story short,Taylor Swift,NA,FALSE,NA,NA,12/11/20,0.546,0.73,0,-7.704,1,0.0417,0.66,0.179,0.0972,0.573,157.895,4,215920,FALSE,C,major,C major,
93 | Fearless (Taylor's Version),FALSE,4/9/21,20,Today Was A Fairytale (Taylor's Version),Taylor Swift,NA,FALSE,NA,NA,4/9/21,0.482,0.779,7,-5.025,1,0.0409,0.0135,0,0.296,0.328,158.162,4,241822,FALSE,G,major,G major,
94 | evermore,FALSE,12/11/20,3,gold rush,Taylor Swift,NA,FALSE,NA,NA,12/11/20,0.512,0.462,9,-10.491,1,0.0408,0.83,0.166,0.121,0.353,112.05,4,185320,TRUE,A,major,A major,
95 | Red,FALSE,10/22/12,15,Starlight,Taylor Swift,NA,FALSE,NA,NA,10/22/12,0.649,0.777,9,-5.804,1,0.0406,0.0213,3.35e-4,0.221,0.587,126.018,4,217827,FALSE,A,major,A major,
96 | folklore,FALSE,7/24/20,3,the last great american dynasty,Taylor Swift,NA,FALSE,NA,NA,7/24/20,0.688,0.653,7,-8.558,1,0.0403,0.481,0.00414,0.106,0.701,147.991,4,231000,TRUE,G,major,G major,
97 | 1989,FALSE,10/27/14,3,Style,Taylor Swift,NA,FALSE,NA,2/9/15,10/27/14,0.588,0.791,7,-5.595,1,0.0402,0.00245,0.00258,0.118,0.487,94.933,4,231000,FALSE,G,major,G major,
98 | Lover,FALSE,8/23/19,5,The Archer,Taylor Swift,NA,FALSE,7/23/19,NA,7/23/19,0.292,0.574,0,-9.375,1,0.0401,0.12,0.00569,0.0663,0.166,124.344,4,211240,FALSE,C,major,C major,
99 | Lover,FALSE,8/23/19,17,It's Nice To Have A Friend,Taylor Swift,NA,FALSE,NA,NA,8/23/19,0.737,0.175,10,-9.912,1,0.0401,0.971,3.37e-4,0.171,0.545,70.008,4,150440,FALSE,A#,major,A# major,
100 | Speak Now,FALSE,10/25/10,7,The Story Of Us,Taylor Swift,NA,FALSE,NA,4/19/11,10/25/10,0.543,0.818,4,-3.611,1,0.0398,0.00478,3.68e-4,0.0357,0.683,139.898,4,265667,FALSE,E,major,E major,
101 | evermore,FALSE,12/11/20,17,it's time to go,Taylor Swift,NA,TRUE,NA,NA,12/11/20,0.592,0.41,0,-12.426,1,0.0397,0.801,1.39e-5,0.09,0.416,151.923,4,254640,FALSE,C,major,C major,
102 | 1989,FALSE,10/27/14,4,Out Of The Woods,Taylor Swift,NA,FALSE,10/14/14,1/19/16,10/14/14,0.553,0.841,7,-6.937,1,0.0396,7.43e-4,1.19e-5,0.341,0.338,92.008,4,235800,FALSE,G,major,G major,
103 | folklore,FALSE,7/24/20,16,hoax,Taylor Swift,NA,FALSE,NA,NA,7/24/20,0.676,0.178,8,-15.01,1,0.0394,0.964,7.9e-6,0.135,0.404,118.84,4,220040,FALSE,G#,major,G# major,
104 | Fearless,FALSE,11/11/08,8,Tell Me Why,Taylor Swift,NA,FALSE,NA,NA,11/11/08,0.601,0.852,7,-3.096,1,0.0386,0.0606,3.41e-6,0.37,0.513,100.015,4,200560,FALSE,G,major,G major,
105 | reputation,FALSE,11/10/17,4,Don't Blame Me,Taylor Swift,NA,FALSE,NA,NA,11/10/17,0.615,0.534,9,-6.719,0,0.0386,0.106,1.76e-5,0.0607,0.193,135.917,4,236413,FALSE,A,minor,A minor,
106 | Fearless,FALSE,11/11/08,6,You Belong With Me,Taylor Swift,NA,FALSE,11/4/08,4/18/09,11/4/08,0.687,0.771,6,-4.424,1,0.0384,0.164,2.46e-5,0.112,0.445,129.964,4,231147,FALSE,F#,major,F# major,
107 | Red (Taylor's Version),FALSE,11/12/21,22,Better Man (Taylor's Version) [From The Vault],Taylor Swift,NA,FALSE,NA,NA,11/12/21,0.473,0.579,4,-5.824,1,0.0384,0.214,0,0.0877,0.255,73.942,4,297013,FALSE,E,major,E major,
108 | folklore,FALSE,7/24/20,17,the lakes,Taylor Swift,NA,TRUE,7/24/21,NA,7/24/20,0.313,0.258,2,-11.532,1,0.0383,0.842,1.3e-6,0.0948,0.268,179.947,4,211813,FALSE,D,major,D major,
109 | reputation,FALSE,11/10/17,10,King Of My Heart,Taylor Swift,NA,FALSE,NA,NA,11/10/17,0.675,0.703,0,-7.902,1,0.0382,0.00882,2.21e-6,0.0391,0.314,110.01,4,214320,FALSE,C,major,C major,
110 | Midnights,FALSE,10/21/22,2,Maroon,Taylor Swift,NA,FALSE,NA,NA,10/21/22,0.658,0.378,7,-8.3,1,0.0379,0.0593,0,0.0976,0.0382,108.034,4,218271,TRUE,G,major,G major,
111 | Red,FALSE,10/22/12,6,22,Taylor Swift,NA,FALSE,NA,3/12/13,10/22/12,0.658,0.729,7,-6.561,1,0.0378,0.00215,0.0013,0.0752,0.668,104.007,4,230133,FALSE,G,major,G major,
112 | evermore,FALSE,12/11/20,2,champagne problems,Taylor Swift,NA,FALSE,NA,NA,12/11/20,0.462,0.24,0,-12.077,1,0.0377,0.92,0,0.113,0.32,171.319,4,244000,TRUE,C,major,C major,
113 | evermore,FALSE,12/11/20,7,happiness,Taylor Swift,NA,FALSE,NA,NA,12/11/20,0.559,0.334,11,-10.733,1,0.0376,0.87,0,0.114,0.211,122.079,4,315147,TRUE,B,major,B major,
114 | Red (Taylor's Version),FALSE,11/12/21,2,Red (Taylor's Version),Taylor Swift,NA,FALSE,NA,NA,11/12/21,0.516,0.777,1,-4.908,0,0.0375,0.00108,1.62e-6,0.0761,0.408,125.047,4,223093,FALSE,C#,minor,C# minor,
115 | Fearless (Taylor's Version),FALSE,4/9/21,21,You All Over Me (Taylor's Version) [From The Vault],Taylor Swift,Maren Morris,FALSE,3/26/21,NA,3/26/21,0.599,0.494,2,-7.61,1,0.0372,0.816,0,0.101,0.44,142.893,4,220839,FALSE,D,major,D major,
116 | The Taylor Swift Holiday Collection,TRUE,10/14/07,1,Last Christmas,Taylor Swift,NA,FALSE,NA,NA,10/14/07,0.702,0.77,5,-5.319,1,0.0372,0.31,0,0.149,0.685,116.009,4,208253,FALSE,F,major,F major,
117 | Red,FALSE,10/22/12,13,The Lucky One,Taylor Swift,NA,FALSE,NA,NA,10/22/12,0.706,0.499,4,-7.675,1,0.0369,0.0371,0,0.132,0.545,117.937,4,240133,FALSE,E,major,E major,
118 | Taylor Swift,FALSE,10/24/06,14,A Perfectly Good Heart,Taylor Swift,NA,TRUE,NA,NA,3/18/08,0.483,0.751,4,-5.726,1,0.0365,0.00349,0,0.128,0.268,156.092,4,220147,FALSE,E,major,E major,
119 | Fearless (Taylor's Version),FALSE,4/9/21,24,That's When (Taylor's Version) [From The Vault],Taylor Swift,Keith Urban,FALSE,NA,NA,4/9/21,0.588,0.608,5,-7.062,1,0.0365,0.225,0,0.092,0.508,90.201,4,189496,FALSE,F,major,F major,
120 | Red,FALSE,10/22/12,4,I Knew You Were Trouble,Taylor Swift,NA,FALSE,NA,NA,10/22/12,0.622,0.469,3,-6.798,0,0.0363,0.00454,2.25e-6,0.0398,0.679,77.019,4,217973,FALSE,D#,minor,D# minor,
121 | Red (Taylor's Version),FALSE,11/12/21,14,Everything Has Changed (Taylor's Version),Taylor Swift,Ed Sheeran,FALSE,NA,NA,11/12/21,0.498,0.61,6,-5.098,1,0.0363,0.271,0,0.223,0.474,79.918,4,245427,FALSE,F#,major,F# major,
122 | folklore,FALSE,7/24/20,8,august,Taylor Swift,NA,FALSE,NA,NA,7/24/20,0.387,0.634,5,-9.222,1,0.0363,0.553,6.93e-5,0.0931,0.42,89.567,4,261920,FALSE,F,major,F major,
123 | Fearless,FALSE,11/11/08,13,Change,Taylor Swift,NA,FALSE,8/8/08,NA,8/8/08,0.548,0.756,5,-4.175,1,0.036,0.00471,0,0.117,0.224,96.038,4,281053,FALSE,F,major,F major,
124 | Fearless (Taylor's Version),FALSE,4/9/21,14,Jump Then Fall (Taylor's Version),Taylor Swift,NA,FALSE,NA,NA,4/9/21,0.572,0.69,2,-6.201,1,0.0358,0.0474,0,0.0897,0.624,160.111,4,237580,FALSE,D,major,D major,
125 | Red,FALSE,10/22/12,21,Red (Original Demo Recording),Taylor Swift,NA,TRUE,NA,NA,10/22/12,0.678,0.902,1,-4.659,0,0.0358,0.113,2.35e-4,0.0722,0.637,124.998,4,226280,FALSE,C#,minor,C# minor,
126 | Red (Taylor's Version),FALSE,11/12/21,15,Starlight (Taylor's Version),Taylor Swift,NA,FALSE,NA,NA,11/12/21,0.628,0.685,4,-5.864,1,0.0358,0.00324,0,0.18,0.605,126.014,4,220413,FALSE,E,major,E major,
127 | folklore,FALSE,7/24/20,10,illicit affairs,Taylor Swift,NA,FALSE,NA,NA,7/24/20,0.529,0.304,10,-10.567,1,0.0356,0.862,0,0.108,0.44,119.58,4,190893,FALSE,A#,major,A# major,
128 | The Taylor Swift Holiday Collection,TRUE,10/14/07,5,Christmas Must Be Something More,Taylor Swift,NA,FALSE,NA,NA,10/14/07,0.689,0.793,7,-4.838,1,0.0356,0.298,0,0.107,0.506,122.879,4,232147,FALSE,G,major,G major,
129 | Red,FALSE,10/22/12,20,Treacherous (Original Demo Recording),Taylor Swift,NA,TRUE,NA,NA,10/22/12,0.828,0.64,2,-8.248,1,0.0355,0.175,6.14e-5,0.0996,0.519,109.993,4,239720,FALSE,D,major,D major,
130 | reputation,FALSE,11/10/17,15,New Year's Day,Taylor Swift,NA,FALSE,NA,11/27/17,11/10/17,0.661,0.151,0,-12.864,1,0.0354,0.921,0,0.13,0.23,94.922,4,235467,FALSE,C,major,C major,
131 | evermore,FALSE,12/11/20,10,ivy,Taylor Swift,NA,FALSE,NA,NA,12/11/20,0.515,0.545,2,-9.277,1,0.0353,0.855,1.97e-5,0.0921,0.535,88.856,4,260440,TRUE,D,major,D major,
132 | Midnights,FALSE,10/21/22,14,The Great War,Taylor Swift,NA,TRUE,NA,NA,10/21/22,0.573,0.741,5,-8.987,1,0.0353,0.219,2.23e-5,0.0842,0.554,96.016,4,240356,FALSE,F,major,F major,
133 | folklore,FALSE,7/24/20,6,mirrorball,Taylor Swift,NA,FALSE,NA,NA,7/24/20,0.549,0.417,7,-10.064,1,0.035,0.686,1.9e-6,0.0604,0.347,110.137,4,208973,FALSE,G,major,G major,
134 | Fearless,FALSE,11/11/08,16,Forever & Always (Piano Version),Taylor Swift,NA,TRUE,NA,NA,10/26/09,0.592,0.16,9,-10.785,1,0.0347,0.852,0,0.116,0.294,118.929,4,267507,FALSE,A,major,A major,
135 | Fearless,FALSE,11/11/08,19,The Other Side Of The Door,Taylor Swift,NA,TRUE,NA,NA,10/26/09,0.476,0.816,4,-4.279,1,0.0347,0.169,0,0.238,0.469,163.847,4,237600,FALSE,E,major,E major,
136 | Speak Now,FALSE,10/25/10,14,Long Live,Taylor Swift,NA,FALSE,NA,NA,10/25/10,0.418,0.68,7,-4.3,1,0.0347,0.036,7.59e-5,0.114,0.142,204.12,4,317453,FALSE,G,major,G major,
137 | evermore,FALSE,12/11/20,11,cowboy like me,Taylor Swift,NA,FALSE,NA,NA,12/11/20,0.604,0.517,0,-9.014,1,0.0347,0.768,1.55e-4,0.123,0.511,127.967,4,275040,TRUE,C,major,C major,
138 | Fearless (Taylor's Version),FALSE,4/9/21,6,You Belong With Me (Taylor's Version),Taylor Swift,NA,FALSE,NA,NA,4/9/21,0.632,0.773,6,-4.856,1,0.0346,0.0623,0,0.0885,0.474,130.033,4,231124,FALSE,F#,major,F# major,
139 | 1989,FALSE,10/27/14,13,Clean,Taylor Swift,NA,FALSE,NA,NA,10/27/14,0.815,0.377,4,-7.754,1,0.0346,0.232,0,0.1,0.211,103.97,4,271000,FALSE,E,major,E major,
140 | Lover,FALSE,8/23/19,15,Afterglow,Taylor Swift,NA,FALSE,NA,NA,8/23/19,0.756,0.449,9,-8.746,1,0.0344,0.13,0,0.114,0.399,111.011,4,223293,FALSE,A,major,A major,
141 | Red (Taylor's Version),FALSE,11/12/21,4,I Knew You Were Trouble (Taylor's Version),Taylor Swift,NA,FALSE,NA,NA,11/12/21,0.584,0.557,6,-6.371,1,0.0342,0.0129,0,0.0576,0.767,154.008,4,219760,FALSE,F#,major,F# major,
142 | Fearless (Taylor's Version),FALSE,4/9/21,13,Change (Taylor's Version),Taylor Swift,NA,FALSE,NA,NA,4/9/21,0.499,0.815,5,-4.063,1,0.0341,1.91e-4,0,0.181,0.344,95.999,4,279360,FALSE,F,major,F major,
143 | Fearless,FALSE,11/11/08,15,Untouchable,Taylor Swift,NA,TRUE,NA,NA,10/26/09,0.419,0.454,5,-6.809,1,0.0335,0.0583,3.78e-6,0.0974,0.288,200.017,4,311040,FALSE,F,major,F major,
144 | Red,FALSE,10/22/12,3,Treacherous,Taylor Swift,NA,FALSE,NA,NA,10/22/12,0.705,0.621,2,-8.086,1,0.0334,0.101,4.3e-6,0.147,0.395,109.993,4,240773,FALSE,D,major,D major,
145 | Fearless,FALSE,11/11/08,10,The Way I Loved You,Taylor Swift,NA,FALSE,NA,NA,11/11/08,0.436,0.712,5,-3.689,1,0.0329,0.0322,0,0.111,0.379,160.886,4,244240,FALSE,F,major,F major,
146 | Fearless (Taylor's Version),FALSE,4/9/21,15,Untouchable (Taylor's Version),Taylor Swift,NA,FALSE,NA,NA,4/9/21,0.393,0.531,5,-6.112,1,0.0329,0.0181,0,0.169,0.224,200.391,4,312107,FALSE,F,major,F major,
147 | Red,FALSE,10/22/12,1,State Of Grace,Taylor Swift,NA,FALSE,10/16/12,NA,10/16/12,0.588,0.825,4,-5.882,1,0.0328,1.97e-4,0.00138,0.0885,0.397,129.968,4,295187,FALSE,E,major,E major,
148 | folklore,FALSE,7/24/20,5,my tears ricochet,Taylor Swift,NA,FALSE,NA,NA,7/24/20,0.456,0.265,0,-10.663,1,0.0328,0.814,0,0.0679,0.112,129.947,4,255893,FALSE,C,major,C major,
149 | Red (Taylor's Version),FALSE,11/12/21,19,Girl At Home (Taylor's Version),Taylor Swift,NA,FALSE,NA,NA,11/12/21,0.691,0.736,5,-6.974,1,0.0326,0.00955,1.88e-5,0.101,0.612,125.089,4,220520,FALSE,F,major,F major,
150 | Taylor Swift,FALSE,10/24/06,4,A Place In This World,Taylor Swift,NA,FALSE,NA,NA,10/24/06,0.576,0.777,9,-2.881,1,0.0324,0.051,0,0.32,0.428,115.028,4,199200,FALSE,A,major,A major,
151 | Speak Now,FALSE,10/25/10,17,Superman,Taylor Swift,NA,TRUE,11/8/10,NA,10/25/10,0.582,0.765,7,-3.648,1,0.0324,0.0268,1.95e-6,0.103,0.559,131.982,4,275960,FALSE,G,major,G major,
152 | Taylor Swift,FALSE,10/24/06,2,Picture To Burn,Taylor Swift,NA,FALSE,NA,2/3/08,10/24/06,0.658,0.877,7,-2.098,1,0.0323,0.173,0,0.0962,0.821,105.586,4,173067,FALSE,G,major,G major,
153 | 1989,FALSE,10/27/14,1,Welcome To New York,Taylor Swift,NA,FALSE,10/20/14,NA,10/20/14,0.789,0.634,7,-4.762,1,0.0323,0.0348,1.64e-6,0.302,0.658,116.992,4,212600,FALSE,G,major,G major,
154 | 1989,FALSE,10/27/14,5,All You Had To Do Was Stay,Taylor Swift,NA,FALSE,NA,NA,10/27/14,0.605,0.725,5,-5.729,1,0.0323,0.00201,2.02e-5,0.101,0.539,96.97,4,193293,FALSE,F,major,F major,
155 | folklore,FALSE,7/24/20,9,this is me trying,Taylor Swift,NA,FALSE,NA,NA,7/24/20,0.511,0.478,9,-9.837,1,0.0322,0.312,2.3e-5,0.147,0.408,135.882,4,195107,FALSE,A,major,A major,
156 | 1989,FALSE,10/27/14,11,This Love,Taylor Swift,NA,FALSE,NA,NA,10/27/14,0.481,0.435,4,-8.795,1,0.0321,0.678,0,0.0928,0.107,143.95,4,250093,FALSE,E,major,E major,
157 | Speak Now,FALSE,10/25/10,13,Last Kiss,Taylor Swift,NA,FALSE,NA,NA,10/25/10,0.371,0.341,10,-9.475,1,0.0319,0.57,3.09e-5,0.101,0.196,89.038,1,367133,FALSE,A#,major,A# major,
158 | The Taylor Swift Holiday Collection,TRUE,10/14/07,4,Silent Night,Taylor Swift,NA,FALSE,NA,NA,10/14/07,0.423,0.311,7,-7.581,1,0.0319,0.621,0,0.0987,0.134,119.546,3,209893,FALSE,G,major,G major,
159 | Fearless,FALSE,11/11/08,4,Hey Stephen,Taylor Swift,NA,FALSE,NA,NA,11/11/08,0.843,0.553,6,-7.348,1,0.0317,0.168,2.66e-6,0.107,0.825,115.997,4,254320,FALSE,F#,major,F# major,
160 | Red (Taylor's Version),FALSE,11/12/21,5,All Too Well (Taylor's Version),Taylor Swift,NA,FALSE,NA,NA,11/12/21,0.44,0.528,0,-7.809,1,0.0317,0.0171,0.00203,0.234,0.132,185.972,4,329160,FALSE,C,major,C major,
161 | Red,FALSE,10/22/12,17,The Moment I Knew,Taylor Swift,NA,TRUE,NA,NA,10/22/12,0.62,0.506,7,-7.327,1,0.0312,0.187,1.47e-5,0.102,0.275,126.015,4,285560,FALSE,G,major,G major,
162 | evermore,FALSE,12/11/20,4,'tis the damn season,Taylor Swift,NA,FALSE,NA,NA,12/11/20,0.575,0.434,5,-8.193,1,0.0312,0.735,6.59e-5,0.105,0.348,145.916,4,229840,FALSE,F,major,F major,
163 | Fearless (Taylor's Version),FALSE,4/9/21,3,Love Story (Taylor's Version),Taylor Swift,NA,FALSE,NA,2/12/21,2/12/21,0.627,0.792,2,-4.311,1,0.031,0.13,3.97e-6,0.0845,0.415,119.054,4,235767,FALSE,D,major,D major,
164 | Red (Taylor's Version),FALSE,11/12/21,17,The Moment I Knew (Taylor's Version),Taylor Swift,NA,FALSE,NA,NA,11/12/21,0.636,0.402,7,-7.855,1,0.031,0.0494,0,0.107,0.208,125.952,4,285280,FALSE,G,major,G major,
165 | Red (Taylor's Version),FALSE,11/12/21,21,Ronan (Taylor's Version),Taylor Swift,NA,FALSE,NA,NA,11/12/21,0.623,0.279,0,-10.802,1,0.031,0.661,0,0.193,0.38,116.04,4,264253,FALSE,C,major,C major,
166 | Red (Taylor's Version),FALSE,11/12/21,27,Forever Winter (Taylor's Version) [From The Vault],Taylor Swift,NA,FALSE,NA,NA,11/12/21,0.611,0.552,7,-5.828,1,0.031,0.256,0,0.134,0.41,116.012,4,263533,FALSE,G,major,G major,
167 | Fearless,FALSE,11/11/08,3,Love Story,Taylor Swift,NA,FALSE,NA,9/12/08,9/12/08,0.618,0.736,2,-3.937,1,0.0308,0.157,0,0.073,0.307,118.982,4,235280,FALSE,D,major,D major,
168 | Fearless (Taylor's Version),FALSE,4/9/21,16,Forever & Always (Piano Version) [Taylor's Version],Taylor Swift,NA,FALSE,NA,NA,4/9/21,0.546,0.273,9,-9.208,1,0.0308,0.819,0,0.137,0.271,118.753,4,267833,FALSE,A,major,A major,
169 | Speak Now,FALSE,10/25/10,2,Sparks Fly,Taylor Swift,NA,FALSE,NA,7/18/11,10/25/10,0.605,0.787,5,-3.002,1,0.0308,0.0396,1.42e-6,0.163,0.374,114.987,4,260933,FALSE,F,major,F major,
170 | evermore,FALSE,12/11/20,15,evermore,Taylor Swift,Bon Iver,FALSE,NA,NA,12/11/20,0.39,0.27,1,-10.673,1,0.0308,0.937,0.00227,0.111,0.32,125.177,5,304107,FALSE,C#,major,C# major,
171 | Speak Now,FALSE,10/25/10,8,Never Grow Up,Taylor Swift,NA,FALSE,NA,NA,10/25/10,0.714,0.305,2,-8.837,1,0.0305,0.819,0,0.174,0.554,124.91,4,290467,FALSE,D,major,D major,
172 | Red,FALSE,10/22/12,22,State Of Grace (Acoustic Version),Taylor Swift,NA,TRUE,NA,NA,10/22/12,0.626,0.118,7,-15.91,1,0.0305,0.664,1.69e-4,0.127,0.0682,105.597,4,323080,FALSE,G,major,G major,
173 | Speak Now,FALSE,10/25/10,4,Speak Now,Taylor Swift,NA,FALSE,10/4/10,NA,10/4/10,0.709,0.599,7,-3.734,1,0.0304,0.095,0,0.0973,0.735,118.975,4,240760,FALSE,G,major,G major,
174 | Taylor Swift,FALSE,10/24/06,11,Our Song,Taylor Swift,NA,FALSE,NA,9/9/07,10/24/06,0.668,0.672,2,-4.931,1,0.0303,0.111,0,0.329,0.539,89.011,4,201107,FALSE,D,major,D major,
175 | Speak Now,FALSE,10/25/10,3,Back To December,Taylor Swift,NA,FALSE,10/11/10,11/15/10,10/11/10,0.529,0.67,2,-4.663,1,0.0303,0.117,0,0.334,0.286,141.893,4,293027,FALSE,D,major,D major,
176 | Red (Taylor's Version),FALSE,11/12/21,30,All Too Well (10 Minute Version) [Taylor's Version] [From The Vault],Taylor Swift,NA,FALSE,11/15/21,NA,11/12/21,0.631,0.518,0,-8.771,1,0.0303,0.274,0,0.088,0.205,93.023,4,613027,TRUE,C,major,C major,
177 | Red (Taylor's Version),FALSE,11/12/21,18,Come Back...Be Here (Taylor's Version),Taylor Swift,NA,FALSE,NA,NA,11/12/21,0.46,0.632,4,-6.031,1,0.0302,0.0158,0,0.0822,0.399,79.846,4,223333,FALSE,E,major,E major,
178 | Fearless (Taylor's Version),FALSE,4/9/21,4,Hey Stephen (Taylor's Version),Taylor Swift,NA,FALSE,NA,NA,4/9/21,0.788,0.571,6,-6.135,1,0.0296,0.106,0,0.0934,0.797,115.99,4,254255,FALSE,F#,major,F# major,
179 | Speak Now,FALSE,10/25/10,1,Mine,Taylor Swift,NA,FALSE,NA,8/4/10,8/4/10,0.624,0.757,7,-2.94,1,0.0296,0.00265,1.87e-6,0.189,0.658,121.07,4,230707,FALSE,G,major,G major,
180 | Taylor Swift,FALSE,10/24/06,7,Tied Together With A Smile,Taylor Swift,NA,FALSE,NA,NA,10/24/06,0.479,0.578,2,-4.963,1,0.0294,0.525,0,0.0841,0.192,146.165,4,248107,FALSE,D,major,D major,
181 | Taylor Swift,FALSE,10/24/06,6,The Outside,Taylor Swift,NA,FALSE,NA,NA,10/24/06,0.589,0.805,5,-4.055,1,0.0293,0.00491,0,0.24,0.591,112.982,4,207107,FALSE,F,major,F major,
182 | Red (Taylor's Version),FALSE,11/12/21,28,Run (Taylor's Version) [From The Vault],Taylor Swift,Ed Sheeran,FALSE,NA,NA,11/12/21,0.61,0.488,3,-6.918,1,0.0293,0.817,0,0.312,0.443,125.039,4,240227,FALSE,D#,major,D# major,
183 | Taylor Swift,FALSE,10/24/06,10,Mary's Song (Oh My My My),Taylor Swift,NA,FALSE,NA,NA,10/24/06,0.403,0.627,2,-5.28,1,0.0292,0.0177,0,0.182,0.374,74.9,4,213080,FALSE,D,major,D major,
184 | Fearless,FALSE,11/11/08,12,The Best Day,Taylor Swift,NA,FALSE,NA,NA,11/11/08,0.664,0.447,1,-8.535,1,0.0292,0.611,1e-6,0.139,0.364,126.079,4,245347,FALSE,C#,major,C# major,
185 | Red,FALSE,10/22/12,12,Sad Beautiful Tragic,Taylor Swift,NA,FALSE,NA,NA,10/22/12,0.624,0.34,7,-12.411,1,0.029,0.632,0.0337,0.121,0.261,129.987,3,283680,FALSE,G,major,G major,
186 | Taylor Swift,FALSE,10/24/06,9,Should've Said No,Taylor Swift,NA,FALSE,NA,5/18/08,10/24/06,0.476,0.777,4,-3.771,0,0.0289,0.0103,0,0.196,0.472,167.964,4,242200,FALSE,E,minor,E minor,
187 | Speak Now,FALSE,10/25/10,9,Enchanted,Taylor Swift,NA,FALSE,NA,NA,10/25/10,0.455,0.623,8,-3.878,1,0.0288,0.0739,4.24e-4,0.146,0.208,163.893,4,352187,FALSE,G#,major,G# major,
188 | Red (Taylor's Version),FALSE,11/12/21,3,Treacherous (Taylor's Version),Taylor Swift,NA,FALSE,NA,NA,11/12/21,0.645,0.593,2,-6.506,1,0.0288,0.0344,1.27e-4,0.13,0.299,109.984,4,242720,FALSE,D,major,D major,
189 | Fearless (Taylor's Version),FALSE,4/9/21,7,Breathe (Taylor's Version),Taylor Swift,Colbie Caillat,FALSE,NA,NA,4/9/21,0.506,0.626,1,-6.066,1,0.0287,0.156,0,0.228,0.321,148.035,4,263377,FALSE,C#,major,C# major,
190 | Red,FALSE,10/22/12,14,Everything Has Changed,Taylor Swift,Ed Sheeran,FALSE,NA,7/16/13,10/22/12,0.624,0.458,6,-6.755,1,0.0287,0.36,3.89e-6,0.303,0.436,80.017,4,243933,FALSE,F#,major,F# major,
191 | The Taylor Swift Holiday Collection,TRUE,10/14/07,6,White Christmas,Taylor Swift,NA,FALSE,NA,NA,10/14/07,0.562,0.392,9,-9.014,1,0.0287,0.587,0,0.224,0.514,145.792,4,154000,FALSE,A,major,A major,
192 | folklore,FALSE,7/24/20,13,epiphany,Taylor Swift,NA,FALSE,NA,NA,7/24/20,0.317,0.254,1,-13.679,1,0.0286,0.727,3.26e-4,0.0867,0.103,93.933,4,289747,FALSE,C#,major,C# major,
193 | Fearless (Taylor's Version),FALSE,4/9/21,9,You're Not Sorry (Taylor's Version),Taylor Swift,NA,FALSE,NA,NA,4/9/21,0.506,0.444,6,-4.99,1,0.0285,0.0538,0,0.123,0.241,133.873,4,261843,FALSE,F#,major,F# major,
194 | The Taylor Swift Holiday Collection,TRUE,10/14/07,2,Christmases When You Were Mine,Taylor Swift,NA,FALSE,NA,NA,10/14/07,0.625,0.206,7,-12.568,1,0.0285,0.646,0,0.0915,0.392,79.466,4,184253,FALSE,G,major,G major,
195 | Speak Now,FALSE,10/25/10,15,Ours,Taylor Swift,NA,TRUE,NA,11/22/11,10/25/10,0.608,0.569,0,-7.227,1,0.0284,0.537,0,0.097,0.238,159.845,4,237920,FALSE,C,major,C major,
196 | Red,FALSE,10/22/12,19,Girl At Home,Taylor Swift,NA,TRUE,NA,NA,10/22/12,0.733,0.59,5,-5.86,1,0.0284,0.272,0,0.136,0.633,125.048,4,220320,FALSE,F,major,F major,
197 | Fearless (Taylor's Version),FALSE,4/9/21,18,SuperStar (Taylor's Version),Taylor Swift,NA,FALSE,NA,NA,4/9/21,0.454,0.555,2,-6.69,1,0.0283,0.157,0,0.343,0.171,171.862,4,263865,FALSE,D,major,D major,
198 | Fearless (Taylor's Version),FALSE,4/9/21,1,Fearless (Taylor's Version),Taylor Swift,NA,FALSE,NA,NA,4/9/21,0.569,0.741,5,-4.963,1,0.0282,0.0122,0,0.333,0.43,100.118,4,241592,FALSE,F,major,F major,
199 | Fearless (Taylor's Version),FALSE,4/9/21,12,The Best Day (Taylor's Version),Taylor Swift,NA,FALSE,NA,NA,4/9/21,0.642,0.576,1,-8.524,1,0.0282,0.488,0,0.159,0.469,125,4,245290,FALSE,C#,major,C# major,
200 | evermore,FALSE,12/11/20,16,right where you left me,Taylor Swift,NA,TRUE,NA,NA,12/11/20,0.581,0.619,10,-6.524,1,0.0282,0.769,1.06e-4,0.117,0.645,137.915,4,245027,FALSE,A#,major,A# major,
201 | Red (Taylor's Version),FALSE,11/12/21,6,22 (Taylor's Version),Taylor Swift,NA,FALSE,NA,NA,11/12/21,0.642,0.695,7,-5.62,1,0.0281,4.43e-4,1.02e-5,0.0753,0.642,103.984,4,230960,FALSE,G,major,G major,
202 | Red (Taylor's Version),FALSE,11/12/21,29,The Very First Night (Taylor's Version) [From The Vault],Taylor Swift,NA,FALSE,NA,NA,11/12/21,0.678,0.733,7,-5.025,1,0.0281,0.00115,0,0.104,0.581,121.009,4,200280,FALSE,G,major,G major,
203 | Speak Now,FALSE,10/25/10,5,Dear John,Taylor Swift,NA,FALSE,NA,NA,10/25/10,0.589,0.47,4,-5.32,1,0.028,0.166,1.45e-6,0.112,0.102,119.386,3,403920,FALSE,E,major,E major,
204 | Red (Taylor's Version),FALSE,11/12/21,10,The Last Time (Taylor's Version),Taylor Swift,Gary Lightbody,FALSE,NA,NA,11/12/21,0.502,0.534,2,-5.954,1,0.0278,0.0399,0,0.0977,0.155,94.05,4,299080,FALSE,D,major,D major,
205 | folklore,FALSE,7/24/20,7,seven,Taylor Swift,NA,FALSE,NA,NA,7/24/20,0.602,0.494,4,-10.813,1,0.0277,0.888,2.59e-5,0.0902,0.374,94.955,4,208907,FALSE,E,major,E major,
206 | Red,FALSE,10/22/12,10,The Last Time,Taylor Swift,Gary Lightbody,FALSE,NA,11/4/13,10/22/12,0.505,0.443,2,-7.039,1,0.0276,0.035,3.15e-6,0.0921,0.123,94.069,4,298293,FALSE,D,major,D major,
207 | Fearless,FALSE,11/11/08,9,You're Not Sorry,Taylor Swift,NA,FALSE,10/28/08,NA,10/28/08,0.459,0.459,6,-4.126,1,0.0275,0.13,2.3e-6,0.113,0.281,134.018,4,261800,FALSE,F#,major,F# major,
208 | Red (Taylor's Version),FALSE,11/12/21,12,Sad Beautiful Tragic (Taylor's Version),Taylor Swift,NA,FALSE,NA,NA,11/12/21,0.601,0.406,7,-11.827,1,0.0275,0.622,9.19e-5,0.133,0.232,130.059,3,284773,FALSE,G,major,G major,
209 | Red (Taylor's Version),FALSE,11/12/21,23,Nothing New (Taylor's Version) [From The Vault],Taylor Swift,Phoebe Bridgers,FALSE,NA,NA,11/12/21,0.606,0.377,0,-9.455,1,0.0275,0.817,0,0.154,0.446,101.96,4,258813,FALSE,C,major,C major,
210 | folklore,FALSE,7/24/20,4,exile,Taylor Swift,Bon Iver,FALSE,NA,8/3/20,7/24/20,0.31,0.374,6,-8.464,1,0.0275,0.761,2.46e-5,0.11,0.16,75.938,4,285640,FALSE,F#,major,F# major,
211 | Red (Taylor's Version),FALSE,11/12/21,16,Begin Again (Taylor's Version),Taylor Swift,NA,FALSE,NA,NA,11/12/21,0.519,0.527,7,-7.673,1,0.0274,0.075,0,0.132,0.267,78.915,4,238867,FALSE,G,major,G major,
212 | Fearless,FALSE,11/11/08,1,Fearless,Taylor Swift,NA,FALSE,10/14/08,1/3/10,10/14/08,0.598,0.714,5,-4.397,1,0.0273,0.0364,1.04e-6,0.31,0.467,99.979,4,242000,FALSE,F,major,F major,
213 | Fearless,FALSE,11/11/08,7,Breathe,Taylor Swift,Colbie Caillat,FALSE,10/21/08,NA,10/21/08,0.498,0.486,1,-7.384,1,0.0273,0.373,0,0.113,0.17,147.952,4,263987,FALSE,C#,major,C# major,
214 | Fearless (Taylor's Version),FALSE,4/9/21,2,Fifteen (Taylor's Version),Taylor Swift,NA,FALSE,NA,NA,4/9/21,0.559,0.646,7,-4.61,1,0.0272,0.116,0,0.32,0.289,95.466,4,294419,FALSE,G,major,G major,
215 | Fearless,FALSE,11/11/08,14,Jump Then Fall,Taylor Swift,NA,TRUE,NA,NA,10/26/09,0.618,0.687,2,-5.737,1,0.0271,0.111,0,0.0693,0.82,79.991,4,237067,FALSE,D,major,D major,
216 | The Taylor Swift Holiday Collection,TRUE,10/14/07,3,Santa Baby,Taylor Swift,NA,FALSE,NA,NA,10/14/07,0.759,0.509,5,-6.593,1,0.0271,0.159,0,0.153,0.75,107.948,4,158600,FALSE,F,major,F major,
217 | Red,FALSE,10/22/12,7,I Almost Do,Taylor Swift,NA,FALSE,NA,NA,10/22/12,0.567,0.481,4,-7.357,1,0.027,0.0173,4.13e-5,0.182,0.233,145.865,4,242573,FALSE,E,major,E major,
218 | Fearless (Taylor's Version),FALSE,4/9/21,17,Come In With The Rain (Taylor's Version),Taylor Swift,NA,FALSE,NA,NA,4/9/21,0.476,0.564,2,-5.677,1,0.0269,0.0406,0,0.102,0.167,143.929,4,237339,FALSE,D,major,D major,
219 | Taylor Swift,FALSE,10/24/06,5,Cold As You,Taylor Swift,NA,FALSE,NA,NA,10/24/06,0.418,0.482,5,-5.769,1,0.0266,0.217,0,0.123,0.261,175.558,4,239013,FALSE,F,major,F major,
220 | Speak Now,FALSE,10/25/10,16,If This Was A Movie,Taylor Swift,NA,TRUE,11/8/10,NA,10/25/10,0.511,0.719,4,-3.457,1,0.0265,0.166,4.54e-6,0.34,0.289,147.852,4,234547,FALSE,E,major,E major,
221 | Red (Taylor's Version),FALSE,11/12/21,7,I Almost Do (Taylor's Version),Taylor Swift,NA,FALSE,NA,NA,11/12/21,0.511,0.559,4,-6.587,1,0.0264,0.0167,0,0.113,0.248,145.88,4,244587,FALSE,E,major,E major,
222 | evermore,FALSE,12/11/20,6,"no body, no crime",Taylor Swift,HAIM,FALSE,NA,1/11/21,12/11/20,0.546,0.613,7,-7.589,0,0.0264,0.418,0,0.103,0.535,79.015,4,215627,FALSE,G,minor,G minor,
223 | evermore,FALSE,12/11/20,8,dorothea,Taylor Swift,NA,FALSE,NA,NA,12/11/20,0.605,0.488,4,-8.322,1,0.0264,0.696,0,0.129,0.354,119.966,4,225880,FALSE,E,major,E major,
224 | Fearless,FALSE,11/11/08,2,Fifteen,Taylor Swift,NA,FALSE,NA,8/30/09,11/11/08,0.559,0.636,7,-4.4,1,0.0263,0.0728,0,0.144,0.214,95.485,4,294347,FALSE,G,major,G major,
225 | Fearless (Taylor's Version),FALSE,4/9/21,23,We Were Happy (Taylor's Version) [From The Vault],Taylor Swift,NA,FALSE,NA,NA,4/9/21,0.609,0.373,0,-8.819,1,0.0263,0.849,0,0.0779,0.13,106.007,4,244237,FALSE,C,major,C major,
226 | Red,FALSE,10/22/12,16,Begin Again,Taylor Swift,NA,FALSE,9/25/12,10/1/12,9/25/12,0.53,0.526,7,-8.349,1,0.0263,0.199,3.52e-6,0.232,0.323,79.025,4,237613,FALSE,G,major,G major,
227 | Speak Now,FALSE,10/25/10,11,Innocent,Taylor Swift,NA,FALSE,NA,NA,10/25/10,0.552,0.608,1,-5.29,0,0.0259,0.186,0,0.125,0.17,134.05,4,302253,FALSE,C#,minor,C# minor,
228 | Fearless,FALSE,11/11/08,17,Come In With The Rain,Taylor Swift,NA,TRUE,NA,NA,10/26/09,0.527,0.475,2,-6.205,1,0.0258,0.0868,4.34e-6,0.248,0.224,143.962,4,238107,FALSE,D,major,D major,
229 | Fearless,FALSE,11/11/08,5,White Horse,Taylor Swift,NA,FALSE,NA,12/7/08,11/11/08,0.585,0.346,0,-8.014,1,0.0256,0.302,0,0.102,0.195,92.564,4,234440,FALSE,C,major,C major,
230 | folklore,FALSE,7/24/20,14,betty,Taylor Swift,NA,FALSE,NA,8/17/20,7/24/20,0.595,0.376,0,-8.767,1,0.0256,0.589,0,0.0989,0.476,95.946,4,294520,TRUE,C,major,C major,
231 | Red,FALSE,10/22/12,18,Come Back...Be Here,Taylor Swift,NA,TRUE,NA,NA,10/22/12,0.483,0.548,4,-6.291,1,0.0254,0.00471,2.01e-6,0.0997,0.217,79.926,4,222707,FALSE,E,major,E major,
232 | Taylor Swift,FALSE,10/24/06,1,Tim McGraw,Taylor Swift,NA,FALSE,NA,6/19/06,6/19/06,0.58,0.491,0,-6.462,1,0.0251,0.575,0,0.121,0.425,76.009,4,232107,FALSE,C,major,C major,
233 | Red (Taylor's Version),FALSE,11/12/21,9,Stay Stay Stay (Taylor's Version),Taylor Swift,NA,FALSE,NA,NA,11/12/21,0.693,0.681,0,-7.039,1,0.025,0.0848,0,0.0768,0.663,100.02,4,205787,FALSE,C,major,C major,
234 | Taylor Swift,FALSE,10/24/06,8,Stay Beautiful,Taylor Swift,NA,FALSE,NA,NA,10/24/06,0.594,0.629,8,-4.919,1,0.0246,0.0868,0,0.137,0.504,131.597,4,236053,FALSE,G#,major,G# major,
235 | Red,FALSE,10/22/12,9,Stay Stay Stay,Taylor Swift,NA,FALSE,NA,NA,10/22/12,0.729,0.748,0,-6.67,1,0.0245,0.307,1.29e-6,0.09,0.928,99.981,4,204827,FALSE,C,major,C major,
236 | Taylor Swift,FALSE,10/24/06,13,Invisible,Taylor Swift,NA,TRUE,NA,NA,3/18/08,0.612,0.394,7,-5.723,1,0.0243,0.637,0,0.147,0.233,96.001,4,203227,FALSE,G,major,G major,
237 | Red,FALSE,10/22/12,5,All Too Well,Taylor Swift,NA,FALSE,NA,NA,10/22/12,0.602,0.609,0,-7.803,1,0.0243,0.0382,2e-4,0.127,0.337,93.06,4,327893,FALSE,C,major,C major,
238 | Fearless,FALSE,11/11/08,18,SuperStar,Taylor Swift,NA,TRUE,NA,NA,10/26/09,0.61,0.531,2,-6.123,1,0.0239,0.367,0,0.351,0.228,85.979,4,261453,FALSE,D,major,D major,
239 | Taylor Swift,FALSE,10/24/06,3,Teardrops On My Guitar,Taylor Swift,NA,FALSE,NA,2/19/07,10/24/06,0.621,0.417,10,-6.941,1,0.0231,0.288,0,0.119,0.289,99.953,4,203040,FALSE,A#,major,A# major,
--------------------------------------------------------------------------------
/dashboard/theme.scss:
--------------------------------------------------------------------------------
1 | /*-- scss:defaults --*/
2 | @import url('https://fonts.googleapis.com/css?family=IBM+Plex+Sans&display=swap');
3 | @import url('https://fonts.googleapis.com/css?family=Yomogi&display=swap');
4 |
5 |
6 | $title-font:"Yomogi";
7 | $main-color:#401487;
8 | $font-color:white;
9 | $light-color:#F4F5FB;
10 | $main-font:"IBM Plex Sans";
11 |
12 |
13 | .sidebar form {
14 | flex-direction:column;
15 | }
16 |
17 | .bslib-card .card-body+.card-body {
18 | height: 70%!important;
19 | }
20 |
21 | .navbar-logo {
22 | border-radius:100%!important;
23 | }
24 |
25 | .navbar-title {
26 | font-family:Yomogi;
27 | text-transform:uppercase;
28 | }
29 |
30 | th:first-child,
31 | td:first-child {
32 | display: none!important;
33 | }
34 |
35 |
36 |
37 | h1 {
38 | font-family: Yomogi;
39 | }
40 |
41 | .quarto-dashboard.dashboard-sidebar>.page-layout-custom>.bslib-sidebar-layout>.sidebar {
42 | padding-left: 1em;
43 | padding-right: 1em;
44 | background: $light-color;
45 | }
46 |
47 | input[type='range'] {
48 | accent-color:$main-color!important;
49 | }
50 |
51 | body {
52 | font-family:$main-font!important;
53 | }
54 |
55 | .navbar {
56 | background-color:$main-color!important;
57 | }
58 |
59 |
60 | .quarto-dashboard .card.valuebox.bslib-card.bg-light {
61 | background-color: $light-color!important;
62 | color: $main-color!important;
63 | }
64 |
65 | .observablehq--number {
66 | font-family:$main-font!important;
67 | color:$main-color!important;
68 | font-size:50px;
69 | }
70 |
71 | .card-header {
72 | background:$light-color;
73 | color:#272727;
74 | }
75 |
76 | .quarto-dashboard .bslib-value-box .value-box-showcase .bi {
77 | font-size:3em;
78 | }
79 |
80 | .bslib-value-box .value-box-area {
81 | padding:0px!important;
82 | }
83 |
84 | .html-fill-container>.html-fill-item {
85 | overflow:hidden;
86 | }
87 |
--------------------------------------------------------------------------------
/dashboard/tswift.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tashapiro/intro-observable/2c44e8f252d49de3a5271a096c458331ae908f01/dashboard/tswift.jpeg
--------------------------------------------------------------------------------
/presentation/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tashapiro/intro-observable/2c44e8f252d49de3a5271a096c458331ae908f01/presentation/.DS_Store
--------------------------------------------------------------------------------
/presentation/images/headshot.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tashapiro/intro-observable/2c44e8f252d49de3a5271a096c458331ae908f01/presentation/images/headshot.jpeg
--------------------------------------------------------------------------------
/presentation/images/intro-observable.qmd:
--------------------------------------------------------------------------------
1 | ---
2 | format:
3 | revealjs:
4 | footer: "Tanya Shapiro | R-Ladies Philly"
5 | slide-number: true
6 | theme: "theme.scss"
7 | embed-resources: true
8 | self-contained: TRUE
9 | ---
10 |
11 | ## {#TitleSlide data-menu-title="TitleSlide" background-color="white" background-image="images/title-slide.png" background-position="center center" background-size="cover"}
12 |
13 | ```{=html}
14 |
21 | ```
22 | ## Hi, I'm Tanya!
23 |
24 | ```{=html}
25 |
26 |

27 |
28 |
29 | - Independent data consultant, specializing in data analysis and visualization design
30 | - Previously worked in insurance industry
31 | - Fell in love with R via #TidyTuesday
32 | - Data geek based in St. Pete, FL
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 | ```
44 | ## Data Viz Learning Journey
45 |
46 | Started off with building visuals in R. Then I wanted to get into more interactive visuals...
47 |
48 | ::: columns
49 | ::: {.column style="width:40%;"}
50 | 
51 | :::
52 |
53 | ::: {.column style="width:60%;"}
54 | - What is node.js?
55 | - What is D3? And svg?
56 | - What IDE should I use?
57 | - Omg where do I start
58 | :::
59 | :::
60 |
61 | ## A Less Intimidating Way to Start...
62 |
63 | Observable Notebooks = my new playground for data viz experiments
64 |
65 | ::: columns
66 | ::: {.column style="width:40%;"}
67 | 
68 | :::
69 |
70 | ::: {.column style="width:60%;"}
71 | - No fuss with installation
72 | - Accessible community, lots of resources
73 | - Observable Plot felt familiar
74 | - Integrate easily with my Quarto site
75 | :::
76 | :::
77 |
78 | ## What is Observable Plot?
79 |
80 | ::: columns
81 | ::: {.column style="width:50%;font-size:35px;"}
82 | - Free and open-source JS library for data viz
83 |
84 | - Under the hood - D3!
85 |
86 | - Inspired by the grammar of graphics
87 |
88 | - Create visuals with layered marks
89 |
90 | - Combine with inputs to create interactive plots
91 | :::
92 |
93 | ::: {.column style="width:50%"}
94 | {fig-align="center"}
95 | :::
96 | :::
97 |
98 | ## Example of Observable Plot
99 |
100 | #### Big Tech Stock Prices
101 |
102 | ```{ojs}
103 |
104 | stocks = FileAttachment("data/big_tech_stock_prices.csv").csv({ typed: true })
105 |
106 | tickers = ["AAPL","ADBE","IBM","META", "GOOGL"]
107 |
108 | viewof selectStock = Inputs.select(tickers, {label:"Stock", value: "GOOGL"})
109 |
110 |
111 | filtered_stocks = stocks.filter(d => d.stock_symbol === selectStock)
112 |
113 | Plot.plot({
114 | width:1000,
115 | x: {label:''},
116 | y: {label:'Close ($)', grid:true},
117 | marks: [
118 | Plot.areaY(filtered_stocks, {x: "date", y: "close", fillOpacity: 0.3, fill:'#4197FF'}),
119 | Plot.lineY(filtered_stocks, {x: "date", y: "close", stroke:'#4197FF'}),
120 | Plot.ruleY([0])
121 | ]
122 | })
123 |
124 | ```
125 |
126 | ## Goals for Today
127 |
128 | ```{=html}
129 |
130 |
Learn about Observable Notebooks
131 |
Work with Observable Plot
132 |
Add in dynamic inputs
133 |
Combine Observable with Quarto
134 |
135 | ```
136 | ## Let's Get Plotting!
137 |
138 | Reroute to this [Observable Notebook](https://observablehq.com/d/f002f055d6fa2f8d).
139 |
140 | ::: {.div style="width:100%;display:flex;justify-content:center;"}
141 | [{fig-align="center" width="800"}](https://observablehq.com/d/f002f055d6fa2f8d)
142 | :::
143 |
--------------------------------------------------------------------------------
/presentation/images/intro.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tashapiro/intro-observable/2c44e8f252d49de3a5271a096c458331ae908f01/presentation/images/intro.webp
--------------------------------------------------------------------------------
/presentation/images/observable.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tashapiro/intro-observable/2c44e8f252d49de3a5271a096c458331ae908f01/presentation/images/observable.png
--------------------------------------------------------------------------------
/presentation/images/observable.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tashapiro/intro-observable/2c44e8f252d49de3a5271a096c458331ae908f01/presentation/images/observable.webp
--------------------------------------------------------------------------------
/presentation/images/philly.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tashapiro/intro-observable/2c44e8f252d49de3a5271a096c458331ae908f01/presentation/images/philly.jpeg
--------------------------------------------------------------------------------
/presentation/images/title-slide.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tashapiro/intro-observable/2c44e8f252d49de3a5271a096c458331ae908f01/presentation/images/title-slide.png
--------------------------------------------------------------------------------
/presentation/intro-observable.qmd:
--------------------------------------------------------------------------------
1 | ---
2 | format:
3 | revealjs:
4 | footer: "Tanya Shapiro | R-Ladies Philly"
5 | slide-number: true
6 | theme: "theme.scss"
7 | embed-resources: true
8 | self-contained: TRUE
9 | ---
10 |
11 | ## {#TitleSlide data-menu-title="TitleSlide" background-color="white" background-image="images/title-slide.png" background-position="center center" background-size="cover"}
12 |
13 | ```{=html}
14 |
21 | ```
22 | ## Hi, I'm Tanya!
23 |
24 | ```{=html}
25 |
26 |

27 |
28 |
29 | - Independent data consultant, specializing in data analysis and visualization design
30 | - Previously worked in insurance industry
31 | - Fell in love with R via #TidyTuesday
32 | - Data geek based in St. Pete, FL
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 | ```
44 | ## Data Viz Learning Journey
45 |
46 | Started off with building visuals in R. Then I wanted to get into more interactive visuals...
47 |
48 | ::: columns
49 | ::: {.column style="width:40%;"}
50 | 
51 | :::
52 |
53 | ::: {.column style="width:60%;"}
54 | - What is node.js?
55 | - What is D3? And svg?
56 | - What IDE should I use?
57 | - Omg where do I start
58 | :::
59 | :::
60 |
61 | ## A Less Intimidating Way to Start...
62 |
63 | Observable Notebooks = my new playground for data viz experiments
64 |
65 | ::: columns
66 | ::: {.column style="width:40%;"}
67 | 
68 | :::
69 |
70 | ::: {.column style="width:60%;"}
71 | - No fuss with installation
72 | - Accessible community, lots of resources
73 | - Observable Plot felt familiar
74 | - Integrate easily with my Quarto site
75 | :::
76 | :::
77 |
78 | ## What is Observable Plot?
79 |
80 | ::: columns
81 | ::: {.column style="width:50%;font-size:35px;"}
82 | - Free and open-source JS library for data viz
83 |
84 | - Under the hood - D3!
85 |
86 | - Inspired by the grammar of graphics
87 |
88 | - Create visuals with layered marks
89 |
90 | - Combine with inputs to create interactive plots
91 | :::
92 |
93 | ::: {.column style="width:50%"}
94 | {fig-align="center"}
95 | :::
96 | :::
97 |
98 | ## Example of Observable Plot
99 |
100 | #### Big Tech Stock Prices
101 |
102 | ```{ojs}
103 |
104 | stocks = FileAttachment("data/big_tech_stock_prices.csv").csv({ typed: true })
105 |
106 | tickers = ["AAPL","ADBE","IBM","META", "GOOGL"]
107 |
108 | viewof selectStock = Inputs.select(tickers, {label:"Stock", value: "GOOGL"})
109 |
110 |
111 | filtered_stocks = stocks.filter(d => d.stock_symbol === selectStock)
112 |
113 | Plot.plot({
114 | width:1000,
115 | x: {label:''},
116 | y: {label:'Close ($)', grid:true},
117 | marks: [
118 | Plot.areaY(filtered_stocks, {x: "date", y: "close", fillOpacity: 0.3, fill:'#4197FF'}),
119 | Plot.lineY(filtered_stocks, {x: "date", y: "close", stroke:'#4197FF'}),
120 | Plot.ruleY([0])
121 | ]
122 | })
123 |
124 | ```
125 |
126 | ## Goals for Today
127 |
128 | ```{=html}
129 |
130 |
Learn about Observable Notebooks
131 |
Work with Observable Plot
132 |
Add in dynamic inputs
133 |
Combine Observable with Quarto
134 |
135 | ```
136 | ## Let's Get Plotting!
137 |
138 | Reroute to this [Observable Notebook](https://observablehq.com/d/f002f055d6fa2f8d).
139 |
140 | ::: {.div style="width:100%;display:flex;justify-content:center;"}
141 | [{fig-align="center" width="800"}](https://observablehq.com/d/f002f055d6fa2f8d)
142 | :::
143 |
--------------------------------------------------------------------------------
/presentation/theme.scss:
--------------------------------------------------------------------------------
1 | /*-- scss:defaults --*/
2 |
3 | @import url("https://use.fontawesome.com/releases/v6.4.2/css/all.css");
4 | @import url('https://fonts.googleapis.com/css?family=Roboto&display=swap');
5 |
6 |
7 | $font-family-sans-serif: "Roboto";
8 | $primary-color: #54278f;
9 | $code-color:#49BCFF;
10 |
11 | .blocks {
12 | display: flex;
13 | flex-wrap: wrap;
14 | justify-content: center;
15 | }
16 |
17 | .block i {
18 | margin-right: 20px;
19 | margin-left: 10px;
20 | }
21 |
22 | figcaption {
23 | font-size: 0.6em!important;
24 | color: grey!important;
25 | }
26 |
27 | .block {
28 | border-radius:10px;
29 | height:60px;
30 | flex: 1 1 calc(50% - 20px); /* changed */
31 | background-color: $primary-color;
32 | color: white;
33 | margin: 1%;
34 | padding: 2%;
35 | opacity: 1;
36 | transition: opacity 0.5s;
37 | display: flex; /* added */
38 | justify-content: left; /* added */
39 | align-items: center; /* added */
40 | text-align: left; /* added */
41 | }
42 |
43 | .block code {
44 | color: whitesmoke !important;
45 | }
46 |
47 | .about-me {
48 | margin-top:50px;
49 | vertical-align:middle;}
50 |
51 |
52 | .social-icons {
53 | display: flex;
54 | justify-content: center;
55 | margin-top: 20px;
56 | font-size:1.2em;
57 | width:50%;
58 | margin-left:200px;
59 | }
60 |
61 | .social-icons a {
62 | display: inline-flex;
63 | margin-left:15px;
64 | margin-right:15px;
65 | }
66 |
67 |
68 |
69 | .about-me img {
70 | width: 220px!important;
71 | height:220px;
72 | clip-path: inset(0 0 0 0 round 50%);
73 | }
74 |
75 |
--------------------------------------------------------------------------------