├── docs ├── .gitignore ├── social-card.png ├── figures │ ├── city-360.jpg │ ├── devtools.png │ ├── pkgdown.png │ ├── roxygen2.png │ ├── usethis.png │ ├── word-doc.png │ ├── worlds │ │ ├── BI.png │ │ ├── CDSC.png │ │ ├── DQS.png │ │ ├── IT1.png │ │ ├── IT2.png │ │ ├── IT3.png │ │ ├── rocket.png │ │ └── space-suit.png │ ├── rpkgs-cover.png │ ├── big-data-point.jpg │ ├── carina-nebula.jpg │ ├── function-help.png │ ├── city-360-square.jpg │ ├── magnifying-glass.cur │ ├── swan-nebula-m17.jpg │ ├── carina-nebula-soft.jpg │ ├── github-issue-answer.jpg │ ├── github-issue-create.jpg │ ├── moffittABC-pkgdown.png │ ├── apress-select-sqlite.jpg │ ├── star-trek-blueprint-1.jpg │ ├── star-trek-blueprint-2.jpg │ ├── big-data-sensor-display.jpg │ └── icons │ │ ├── notes-medical-solid.svg │ │ ├── tasks-solid.svg │ │ └── laptop-code-solid.svg ├── head.html ├── libs │ ├── header-attrs │ │ └── header-attrs.js │ ├── xaringanExtra-extra-styles │ │ └── xaringanExtra-extra-styles.css │ ├── tile-view │ │ ├── tile-view.css │ │ └── tile-view.js │ ├── shareon │ │ ├── shareon.min.js │ │ └── shareon.min.css │ ├── xaringanExtra-shareagain │ │ ├── shareagain.css │ │ └── shareagain.js │ └── clipboard │ │ └── clipboard.min.js ├── library.R ├── setup.R ├── css │ ├── talk-base.css │ ├── part-two.min.css.map │ ├── build-your-universe.min.css.map │ ├── part-two.min.css │ ├── moffitt-xaringan.css │ ├── build-your-universe.min.css │ ├── part-two.css │ └── build-your-universe.css ├── js │ └── part-two.js ├── index.Rmd ├── index.html └── part-two.Rmd ├── .gitignore └── README.md /docs/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | !index.html 3 | !part-two.html 4 | !head.html 5 | 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .Rproj.user 2 | .Rhistory 3 | .RData 4 | .Ruserdata 5 | .DS_Store 6 | .history -------------------------------------------------------------------------------- /docs/social-card.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgerke/build-your-own-universe/HEAD/docs/social-card.png -------------------------------------------------------------------------------- /docs/figures/city-360.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgerke/build-your-own-universe/HEAD/docs/figures/city-360.jpg -------------------------------------------------------------------------------- /docs/figures/devtools.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgerke/build-your-own-universe/HEAD/docs/figures/devtools.png -------------------------------------------------------------------------------- /docs/figures/pkgdown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgerke/build-your-own-universe/HEAD/docs/figures/pkgdown.png -------------------------------------------------------------------------------- /docs/figures/roxygen2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgerke/build-your-own-universe/HEAD/docs/figures/roxygen2.png -------------------------------------------------------------------------------- /docs/figures/usethis.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgerke/build-your-own-universe/HEAD/docs/figures/usethis.png -------------------------------------------------------------------------------- /docs/figures/word-doc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgerke/build-your-own-universe/HEAD/docs/figures/word-doc.png -------------------------------------------------------------------------------- /docs/figures/worlds/BI.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgerke/build-your-own-universe/HEAD/docs/figures/worlds/BI.png -------------------------------------------------------------------------------- /docs/figures/rpkgs-cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgerke/build-your-own-universe/HEAD/docs/figures/rpkgs-cover.png -------------------------------------------------------------------------------- /docs/figures/worlds/CDSC.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgerke/build-your-own-universe/HEAD/docs/figures/worlds/CDSC.png -------------------------------------------------------------------------------- /docs/figures/worlds/DQS.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgerke/build-your-own-universe/HEAD/docs/figures/worlds/DQS.png -------------------------------------------------------------------------------- /docs/figures/worlds/IT1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgerke/build-your-own-universe/HEAD/docs/figures/worlds/IT1.png -------------------------------------------------------------------------------- /docs/figures/worlds/IT2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgerke/build-your-own-universe/HEAD/docs/figures/worlds/IT2.png -------------------------------------------------------------------------------- /docs/figures/worlds/IT3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgerke/build-your-own-universe/HEAD/docs/figures/worlds/IT3.png -------------------------------------------------------------------------------- /docs/figures/big-data-point.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgerke/build-your-own-universe/HEAD/docs/figures/big-data-point.jpg -------------------------------------------------------------------------------- /docs/figures/carina-nebula.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgerke/build-your-own-universe/HEAD/docs/figures/carina-nebula.jpg -------------------------------------------------------------------------------- /docs/figures/function-help.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgerke/build-your-own-universe/HEAD/docs/figures/function-help.png -------------------------------------------------------------------------------- /docs/figures/worlds/rocket.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgerke/build-your-own-universe/HEAD/docs/figures/worlds/rocket.png -------------------------------------------------------------------------------- /docs/figures/city-360-square.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgerke/build-your-own-universe/HEAD/docs/figures/city-360-square.jpg -------------------------------------------------------------------------------- /docs/figures/magnifying-glass.cur: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgerke/build-your-own-universe/HEAD/docs/figures/magnifying-glass.cur -------------------------------------------------------------------------------- /docs/figures/swan-nebula-m17.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgerke/build-your-own-universe/HEAD/docs/figures/swan-nebula-m17.jpg -------------------------------------------------------------------------------- /docs/figures/carina-nebula-soft.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgerke/build-your-own-universe/HEAD/docs/figures/carina-nebula-soft.jpg -------------------------------------------------------------------------------- /docs/figures/github-issue-answer.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgerke/build-your-own-universe/HEAD/docs/figures/github-issue-answer.jpg -------------------------------------------------------------------------------- /docs/figures/github-issue-create.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgerke/build-your-own-universe/HEAD/docs/figures/github-issue-create.jpg -------------------------------------------------------------------------------- /docs/figures/moffittABC-pkgdown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgerke/build-your-own-universe/HEAD/docs/figures/moffittABC-pkgdown.png -------------------------------------------------------------------------------- /docs/figures/worlds/space-suit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgerke/build-your-own-universe/HEAD/docs/figures/worlds/space-suit.png -------------------------------------------------------------------------------- /docs/head.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /docs/figures/apress-select-sqlite.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgerke/build-your-own-universe/HEAD/docs/figures/apress-select-sqlite.jpg -------------------------------------------------------------------------------- /docs/figures/star-trek-blueprint-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgerke/build-your-own-universe/HEAD/docs/figures/star-trek-blueprint-1.jpg -------------------------------------------------------------------------------- /docs/figures/star-trek-blueprint-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgerke/build-your-own-universe/HEAD/docs/figures/star-trek-blueprint-2.jpg -------------------------------------------------------------------------------- /docs/figures/big-data-sensor-display.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgerke/build-your-own-universe/HEAD/docs/figures/big-data-sensor-display.jpg -------------------------------------------------------------------------------- /docs/libs/header-attrs/header-attrs.js: -------------------------------------------------------------------------------- 1 | // Pandoc 2.9 adds attributes on both header and div. We remove the former (to 2 | // be compatible with the behavior of Pandoc < 2.8). 3 | document.addEventListener('DOMContentLoaded', function(e) { 4 | var hs = document.querySelectorAll("div.section[class*='level'] > :first-child"); 5 | var i, h, a; 6 | for (i = 0; i < hs.length; i++) { 7 | h = hs[i]; 8 | if (!/^h[1-6]$/i.test(h.tagName)) continue; // it should be a header h1-h6 9 | a = h.attributes; 10 | while (a.length > 0) h.removeAttribute(a[0].name); 11 | } 12 | }); 13 | -------------------------------------------------------------------------------- /docs/library.R: -------------------------------------------------------------------------------- 1 | if (!requireNamespace("xaringanExtra", quietly = TRUE)) { 2 | stop("`xaringanExtra` is required: devtools::install_github('gadenbuie/xaringanExtra')") 3 | } 4 | 5 | if (!requireNamespace("metathis", quietly = TRUE)) { 6 | stop("`metathis` is required: devtools::install_github('gadenbuie/metathis')") 7 | } 8 | 9 | if (!requireNamespace("moffittdocs", quietly = TRUE)) { 10 | stop("`moffittdocs` is required: devtools::install_github('Moffitt-Health-Informatics/moffittdocs')") 11 | } 12 | 13 | if (!requireNamespace("icon", quietly = TRUE)) { 14 | stop("`icon` is required: install.packages('icon')") 15 | } 16 | 17 | -------------------------------------------------------------------------------- /docs/figures/icons/notes-medical-solid.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/libs/xaringanExtra-extra-styles/xaringanExtra-extra-styles.css: -------------------------------------------------------------------------------- 1 | /* Line Hover Indicator */ 2 | .remark-code-line:hover { 3 | font-weight: bold; 4 | opacity: 1 !important; 5 | } 6 | 7 | .remark-code-line:hover:before { 8 | content: "\25B6"; 9 | color: #6d7e8a; 10 | position: absolute; 11 | transform: translateX(-1.2em); 12 | animation: hover 0.66s alternate 8 cubic-bezier(0.445, 0.05, 0.55, 0.95); 13 | } 14 | 15 | @keyframes hover { 16 | 0% { 17 | transform: translateX(-1.2em); 18 | } 19 | 100% { 20 | transform: translateX(-0.8em); 21 | } 22 | } 23 | /* Mute highlighted line */ 24 | .remark-code-has-line-highlighted .remark-code-line:not(.remark-code-line-highlighted) { 25 | opacity: 0.4; 26 | } 27 | 28 | /* Override highlight color */ 29 | .remark-code-line-highlighted { 30 | background-color: inherit; 31 | } 32 | -------------------------------------------------------------------------------- /docs/figures/icons/tasks-solid.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/figures/icons/laptop-code-solid.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/libs/tile-view/tile-view.css: -------------------------------------------------------------------------------- 1 | .remark__tile-view * { 2 | box-sizing: border-box; 3 | } 4 | 5 | .remark__tile-view { 6 | background: lightgray; 7 | position: relative; 8 | width: 100%; 9 | height: 100%; 10 | padding: 3em; 11 | font-size: 18px; 12 | box-sizing: border-box; 13 | overflow: scroll; 14 | } 15 | 16 | .remark__tile-view__header { 17 | text-align: center; 18 | } 19 | 20 | .remark__tile-view__tiles { 21 | display: grid; 22 | /* Set column width in JS */ 23 | /* grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); */ 24 | justify-items: center; 25 | } 26 | 27 | .remark__tile-view__tile { 28 | position: relative; 29 | margin: 0.5em; 30 | padding: 0.5em; 31 | } 32 | 33 | .remark__tile-view__slide-container { 34 | margin: 0 auto; 35 | } 36 | 37 | .remark__tile-view__tile--current { 38 | background: #ffd863; 39 | border: 5px solid #ffd863; 40 | margin: calc(0.5em - 5px); 41 | border-radius: 0; 42 | } 43 | 44 | .remark__tile-view__tile--seen { 45 | opacity: 0.5; 46 | } 47 | 48 | .remark__tile-view__tile:hover { 49 | /* background: #993d70; */ 50 | background: #44bc96; 51 | opacity: 1; 52 | } 53 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Build your own universe 2 | 3 | ![](https://github.com/tgerke/build-your-own-universe/raw/master/docs/social-card.png) 4 | 5 | ### Scale high-quality research data provisioning with R packages 6 | 7 | [slides]: https://build-your-own-universe.netlify.app/ 8 | [r-med]: https://events.linuxfoundation.org/r-medicine/ 9 | [recording]: https://youtu.be/lm0QYr1C2Sw?list=PL4IzsxWztPdljYo7uE5G_R2PtYw3fUReo 10 | 11 | Travis Gerke & Garrick Aden-Buie 12 | 13 | This is a 20-minute talk most recently given at [R/Medicine 2020][r-med]. 14 | 15 | 📺 [View slides][slides] (best viewed in Firefox!) 16 | 17 | 🎥 [Watch recording][recording] 18 | 19 | ✍️ Abstract: 20 | 21 | Institutional honest brokers consolidate patient, clinical, and lab data from a variety of data sources in order to provide investigators with research-ready data sets. High-quality research data provisioning requires skilled navigation of heterogeneous software systems and a detailed understanding of data structure standards within each source. In this talk we discuss how we, as honest brokers at a large cancer center, have created a universe of internal R packages that simplify data access, store and present metadata, standardize best practices, support reproducibility and repeatability, apply branding styles to reports and visualizations, and facilitate communication with the research data end user. Our package ecosystem simplifies the workflow of honest brokers to scale curation and delivery of high-quality research data. 22 | -------------------------------------------------------------------------------- /docs/setup.R: -------------------------------------------------------------------------------- 1 | options(htmltools.dir.version = FALSE) 2 | knitr::opts_chunk$set( 3 | fig.width= 9, 4 | fig.height= 3.5, 5 | fig.retina= 3, 6 | out.width = "100%", 7 | cache = FALSE, 8 | echo = TRUE, 9 | message = FALSE, 10 | warning = FALSE 11 | ) 12 | 13 | library(metathis) 14 | 15 | htmltools::tagList( 16 | moffittdocs::use_moffitt_tachyons(), 17 | xaringanExtra::use_animate_css(TRUE, xaringan = FALSE), 18 | xaringanExtra::use_tile_view(), 19 | xaringanExtra::use_extra_styles(hover_code_line = TRUE, mute_unhighlighted_code = TRUE), 20 | xaringanExtra::use_share_again(), 21 | meta() %>% 22 | meta_general( 23 | description = "Build your own universe: Scale high-quality research data provisioning with R packages", 24 | generator = "xaringan and remark.js" 25 | ) %>% 26 | meta_name("github-repo" = "tgerke/build-your-own-universe") %>% 27 | meta_social( 28 | title = "Build your own universe: Scale high-quality research data provisioning with R packages", 29 | url = "https://build-your-own-universe.netlify.app", 30 | image = "https://build-your-own-universe.netlify.app/social-card.png", 31 | image_alt = "Title slide of Build your own universe: Scale high-quality research data provisioning with R packages, presented at R/Medicine 2020 by Travis Gerke and Garrick Aden-Buie", 32 | og_type = "website", 33 | og_author = "Travis Gerke", 34 | twitter_card_type = "summary_large_image", 35 | twitter_creator = "@grrrck", 36 | twitter_site = "@travisgerke" 37 | ) %>% 38 | include_meta() 39 | ) 40 | -------------------------------------------------------------------------------- /docs/css/talk-base.css: -------------------------------------------------------------------------------- 1 | #slide-title { 2 | background-color: #f9faff; 3 | background-size: 100%; 4 | background-position: 0 -530px; 5 | background-repeat: repeat-y 6 | } 7 | .talk-title { 8 | margin-bottom: 110px 9 | } 10 | .talk-meta { 11 | left: 1em; 12 | width: 100% 13 | } 14 | .talk-author { 15 | font-size: 1em 16 | } 17 | .talk-date { 18 | font-size: .8em 19 | } 20 | .blueprint1 { 21 | background-image: url(../figures/star-trek-blueprint-1.jpg); 22 | background-size: cover 23 | } 24 | .blueprint2 { 25 | background-image: url(../figures/star-trek-blueprint-2.jpg); 26 | background-size: cover 27 | } 28 | hr { 29 | box-sizing: content-box; 30 | height: 3px; 31 | overflow: visible; 32 | border: none; 33 | background: var(--moffitt-gray) 34 | } 35 | .remark-container { 36 | background: #fff 37 | } 38 | .remark-slide-scaler { 39 | box-shadow: none; 40 | -webkit-box-shadow: none; 41 | outline: 2px solid #eee 42 | } 43 | .city-360-rotate { 44 | background-image: url(../figures/city-360-square.jpg); 45 | background-size: contain; 46 | width: 700px; 47 | height: 700px; 48 | border-radius: 50%; 49 | position: absolute; 50 | animation-name: rotate; 51 | animation-iteration-count: infinite; 52 | animation-timing-function: linear; 53 | animation-duration: 120s 54 | } 55 | .city-360-top { 56 | top: -60%; 57 | left: 250px 58 | } 59 | .city-360-bottom { 60 | top: 60%; 61 | left: 250px 62 | } 63 | .city-360-bg { 64 | background-color: #f9faff; 65 | } 66 | @keyframes rotate { 67 | from { 68 | transform: rotate(0) 69 | } 70 | to { 71 | transform: rotate(360deg) 72 | } 73 | } 74 | .highlight-last-item > ul > li, 75 | .highlight-last-item > ol > li { 76 | opacity: 0.5; 77 | } 78 | .highlight-last-item > ul > li:last-of-type, 79 | .highlight-last-item > ol > li:last-of-type { 80 | opacity: 1; 81 | } -------------------------------------------------------------------------------- /docs/libs/shareon/shareon.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * shareon v1.4.1 by Nikita Karamov 3 | * https://shareon.js.org 4 | */ 5 | 6 | var shareon=function(){"use strict";var t={facebook:t=>"https://www.facebook.com/sharer/sharer.php?u="+t.url,linkedin:t=>`https://www.linkedin.com/shareArticle?mini=true&url=${t.url}&title=${t.title}`,messenger:t=>`https://www.facebook.com/dialog/send?app_id=3619024578167617&link=${t.url}&redirect_uri=${t.url}`,odnoklassniki:t=>`https://connect.ok.ru/offer?url=${t.url}&title=${t.title}${t.media?"&imageUrl="+t.media:""}`,pinterest:t=>`https://pinterest.com/pin/create/button/?url=${t.url}&description=${t.title}${t.media?"&media="+t.media:""}`,pocket:t=>"https://getpocket.com/edit.php?url="+t.url,reddit:t=>`https://www.reddit.com/submit?title=${t.title}&url=${t.url}`,telegram:t=>`https://telegram.me/share/url?url=${t.url}${t.text?"&text="+t.text:""}`,twitter:t=>`https://twitter.com/intent/tweet?url=${t.url}&text=${t.title}${t.via?"&via="+t.via:""}`,viber:t=>`viber://forward?text=${t.title}%0D%0A${t.url}${t.text?"%0D%0A%0D%0A"+t.text:""}`,vkontakte:t=>`https://vk.com/share.php?url=${t.url}&title=${t.title}${t.media?"&image="+t.media:""}`,whatsapp:t=>`whatsapp://send?text=${t.title}%0D%0A${t.url}${t.text?"%0D%0A%0D%0A"+t.text:""}`};const e=()=>{const e=document.getElementsByClassName("shareon");for(let r=0;r{window.open(o,"_blank","noopener,noreferrer").opener=null});break}}}}}};return window.onload=()=>{e()},e}(); 7 | -------------------------------------------------------------------------------- /docs/css/part-two.min.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["part-two.css"],"names":[],"mappings":"AAAA,8BACE,iBAAkB,QAEpB,8BACE,WAAY,IAAI,MAAM,KACtB,YAAa,IAAI,MAAM,KACvB,aAAc,IAAI,MAAM,KAE1B,4DACE,WAAY,KACZ,cAAe,KAEjB,2CACE,cAAe,IAAI,MAAM,KAE3B,kCACE,WAAY,IAAI,MAAM,KAExB,0BACE,WAAY,KAEd,+BACA,UACE,SAAU,SACV,MAAO,MACP,WAAY,OACZ,YAAa,MACb,OAAQ,KAAK,KACb,iBAAkB,KAClB,OAAQ,IAAI,MAAM,KAClB,cAAe,KACf,UAAW,OAEb,WACE,MAAO,MACP,cAAe,MACf,QAAS,KAEX,gBACA,iBACE,QAAS,IACT,SAAU,SACV,MAAO,EACP,OAAQ,EAEV,iBACE,KAAM,KACN,OAAQ,MACR,OAAQ,KAAK,MACb,aAAc,KAAK,YAAY,YAAY,KAE7C,gBACE,KAAM,KACN,OAAQ,MACR,OAAQ,KAAK,MACb,aAAc,KAAK,YAAY,YAAY,KAE7C,iBACA,kBACE,MAAO,KACP,OAAQ,MACR,MAAO,KACP,OAAQ,KACR,iBAAkB,KAClB,OAAQ,IAAI,MAAM,KAClB,sBAAuB,KACvB,mBAAoB,KACpB,cAAe,KAEjB,iBACE,MAAO,KACP,OAAQ,KACR,MAAO,IACP,OAAQ,MACR,aAAc,KACd,sBAAuB,KACvB,mBAAoB,KACpB,cAAe,KAEjB,kEACA,0BACE,uBAAwB,SACxB,eAAgB,SAChB,2BAA4B,GAC5B,mBAAoB,GACpB,4BAA6B,KAC7B,oBAAqB,KAEvB,4BACE,GACE,QAAS,EACT,iBAAkB,IAAI,IACtB,UAAW,WACX,eAAgB,WAChB,OAAQ,WAEV,KACE,QAAS,EACT,iBAAkB,IAAI,IACtB,UAAW,WACX,eAAgB,QAChB,OAAQ,SAGZ,oBACE,GACE,QAAS,EACT,iBAAkB,IAAI,IACtB,UAAW,WACX,eAAgB,WAChB,OAAQ,WAEV,KACE,QAAS,EACT,iBAAkB,IAAI,IACtB,UAAW,WACX,eAAgB,QAChB,OAAQ,SAGZ,iCACA,8BACE,QAAS,EACT,aAAc,MAAM,MAAM,OAC1B,YAAa,OACb,SAAU,OACV,4BAA6B,SAC7B,oBAAqB,SAEvB,2CACE,MAAO,MACP,kBAAmB,KAAK,GAAG,cAC3B,UAAW,KAAK,GAAG,cACnB,4BAA6B,SAC7B,oBAAqB,SAEvB,2CACE,MAAO,MACP,kBAAmB,KAAK,GAAG,cAC3B,UAAW,KAAK,GAAG,cACnB,4BAA6B,SAC7B,oBAAqB,SACrB,wBAAyB,GACzB,gBAAiB,GAEnB,2CACE,MAAO,MACP,kBAAmB,KAAK,IAAI,aAC5B,UAAW,KAAK,IAAI,aACpB,4BAA6B,SAC7B,oBAAqB,SACrB,wBAAyB,GACzB,gBAAiB,GAEnB,2CACE,MAAO,MACP,kBAAmB,KAAK,IAAI,aAC5B,UAAW,KAAK,IAAI,aACpB,4BAA6B,SAC7B,oBAAqB,SACrB,wBAAyB,KACzB,gBAAiB,KAEnB,gBACE,GACE,MAAO,EAET,GACE,QAAS,EAEX,MACE,aAAc,MAAM,MAAM,OAE5B,KACE,QAAS,EACT,OAAQ,MAGZ,wBACE,GACE,MAAO,EAET,GACE,QAAS,EAEX,MACE,aAAc,MAAM,MAAM,OAE5B,KACE,QAAS,EACT,OAAQ,MAGZ,iBACE,IACE,aAAc,aAGlB,yBACE,IACE,aAAc,YAGlB,iCACA,wBACE,MAAO,QAET,aACE,UAAW,YAAY,GAAG,+BAC1B,iBAAkB,OAEpB,uBACE,GACE,UAAW,SAEb,KACE,QAAS,EACT,UAAW,WAGf,+BACE,GACE,UAAW,SAEb,KACE,QAAS,EACT,UAAW,WAGf,oCACA,8BACE,UAAW,MACX,qBAAsB,EACtB,kBAAmB,EACnB,aAAc,EACd,mBAAoB,KACpB,gBAAiB,KACjB,WAAY,KAEd,qBACE,WAAY,EACZ,aAAc,IACd,uBAAwB,IACxB,wBAAyB,IAE3B,uBACE,uBAAwB,IACxB,wBAAyB,IACzB,YAAa,KAEf,oBACE,MAAO,KACP,OAAQ,KACR,aAAc,IAEhB,qBACE,SAAU,SACV,KAAM,KACN,IAAK,KAEP,eACC,SAAU,SACV,MAAO,IACP,UAAW,IACV,IAAK,KAEP,qBACC,iBAAkB,KAClB,OAAQ,KAET,qBACE,OAAQ,oCAAoC,CAAE,QAEhD,wBACA,UACE,iBAAkB,6BAClB,gBAAiB,MACjB,SAAU,OACV,OAAQ,KACR,YAAa,IAEf,cACE,WAAY,IACZ,SAAU,OACV,SAAU,SACV,MAAO,IAET,uBACE,WAAY,KACZ,YAAa,WACb,UAAW,KACX,WAAY,OAEd,+CACE,YAAa,IAGf,sDACE,QAAS,KACT,MAAO,QACP,SAAU,SACV,UAAW,KACX,UAAW,KAEb,oBACE,WAAY,EAAE,EACd,MAAO,KACP,OAAQ,KACR,SAAU,SACV,IAAK,KACL,KAAM,MACN,OAAQ,KACR,OAAQ,QAEV,kBACE,WAAY,EAAE,EACd,MAAO,KACP,OAAQ,KACR,SAAU,SACV,IAAK,KACL,KAAM,MACN,OAAQ,KACR,OAAQ,QAEV,qBACE,WAAY,EAAE,EACd,WAAY,OACZ,MAAO,MACP,OAAQ,KACR,QAAS,EACT,SAAU,SACV,IAAK,KACL,KAAM,MACN,OAAQ,KACR,OAAQ,QACR,UAAW,KAEb,4EACA,6BACE,uBAAwB,aACxB,eAAgB,aAElB,gCACE,GACE,QAAS,EACT,iBAAkB,KAAK,KACvB,UAAW,mBAAmB,WAAW,WAE3C,IACE,QAAS,EACT,iBAAkB,KAAK,KACvB,UAAW,mBAAmB,WAAW,eAE3C,IACE,QAAS,EACT,iBAAkB,EAAE,KACpB,UAAW,mBAAmB,WAAW,WAE3C,IACE,QAAS,EACT,iBAAkB,EAAE,KACpB,UAAW,mBAAmB,eAAe,eAE/C,KACE,QAAS,EACT,iBAAkB,KAAK,KACvB,UAAW,mBAAmB,eAAe,YAGjD,wBACE,GACE,QAAS,EACT,iBAAkB,KAAK,KACvB,UAAW,mBAAmB,WAAW,WAE3C,IACE,QAAS,EACT,iBAAkB,KAAK,KACvB,UAAW,mBAAmB,WAAW,eAE3C,IACE,QAAS,EACT,iBAAkB,EAAE,KACpB,UAAW,mBAAmB,WAAW,WAE3C,IACE,QAAS,EACT,iBAAkB,EAAE,KACpB,UAAW,mBAAmB,eAAe,eAE/C,KACE,QAAS,EACT,iBAAkB,KAAK,KACvB,UAAW,mBAAmB,eAAe,YAGjD,qCACA,UACE,iBAAkB,uCAClB,gBAAiB,EAAE,KACnB,kBAAmB,UACnB,WAAY,gBAAgB,KAAK,OAAO,CAAC,MAAM,IAAI,QAErD,gBACE,QAAS,EACT,MAAO,KACP,gBAAiB,KAAK,KAExB,IACE,sBAAuB,QACvB,0BAA2B,KAE7B,6BACA,cACE,SAAU,SACV,IAAK,MAEP,aACE,QAAS,IAEX,sBACE,MAAO,cACP,UAAW,MACX,MAAO,MACP,OAAQ,EACR,SAAU,SACV,WAAY,OAEd,gBACE,IAAK,IACL,MAAO,IAET,gBACE,IAAK,IACL,MAAO,IAET,WACE,WAAY,EACZ,SAAU,OAEZ,0BACE,WAAY,MAEd,2BACE,UAAW,UAAU,KAAK,OAC1B,oBAAqB,SAEvB,qBACE,GACE,WAAY,EAEd,KACE,WAAY,OAGhB,gBACE,IAAK,IACL,MAAO,IAET,cACE,SAAU,SACV,OAAQ,QAEV,oBACE,QAAS,EAEX,4BACE,WAAY,QAAQ,MAAM,OAE5B,4BACE,SAAU,SACV,WAAY,QAAQ,MAAM,OAC1B,MAAO,OACP,KAAM,EAER,wCACE,QAAS,EAEX,kCACE,QAAS,EAEX,gBACE,IAAK,IACL,MAAO,EAET,gBACE,OAAQ,IACR,MAAO"} -------------------------------------------------------------------------------- /docs/css/build-your-universe.min.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["build-your-universe.css"],"names":[],"mappings":"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"} -------------------------------------------------------------------------------- /docs/js/part-two.js: -------------------------------------------------------------------------------- 1 | /* global slideshow,LeaderLine */ 2 | 3 | (function () { 4 | const ready = function (fn) { 5 | /* MIT License Copyright (c) 2016 Nuclei */ 6 | /* https://github.com/nuclei/readyjs */ 7 | const completed = () => { 8 | document.removeEventListener('DOMContentLoaded', completed) 9 | window.removeEventListener('load', completed) 10 | fn() 11 | } 12 | if (document.readyState !== 'loading') { 13 | setTimeout(fn) 14 | } else { 15 | document.addEventListener('DOMContentLoaded', completed) 16 | window.addEventListener('load', completed) 17 | } 18 | } 19 | 20 | function emailAttachment () { 21 | const btn = document.getElementById('email-attachment') 22 | btn.addEventListener('click', function (ev) { 23 | btn.classList.toggle('zoomForward') 24 | setTimeout(() => slideshow.gotoNextSlide(), 1000) 25 | setTimeout(() => btn.classList.remove('zoomForward'), 1500) 26 | }) 27 | } 28 | 29 | function sqlMagnifier () { 30 | const sqlMagnifier = document.getElementById('sql-magnifier') 31 | const sqlCode = document.querySelector('.code-two-column pre code.sql') 32 | sqlCode.addEventListener('mouseover', function(ev) { 33 | if (ev.target.classList.contains('remark-code-line')) { 34 | sqlMagnifier.innerHTML = ev.target.outerHTML 35 | } 36 | }) 37 | sqlCode.addEventListener('mouseleave', function() { 38 | sqlMagnifier.innerHTML = '' 39 | }) 40 | } 41 | 42 | function wordButtons () { 43 | const btnDown = document.getElementById('sql-font-size-down') 44 | const btnUp = document.getElementById('sql-font-size-up') 45 | const btnFont = document.getElementById('sql-font-comic-sans') 46 | const code = document.querySelector('.word-doc pre code') 47 | let size = 100 48 | code.style.fontSize = size + '%' 49 | btnDown.addEventListener('click', function () { 50 | if (size <= 10) return 51 | size = size - 10 52 | code.style.fontSize = size + '%' 53 | }) 54 | btnUp.addEventListener('click', function () { 55 | if (size >= 100) return 56 | size = size + 10 57 | code.style.fontSize = size + '%' 58 | }) 59 | btnFont.addEventListener('click', function (ev) { 60 | if (btnFont.innerText === '') { 61 | btnFont.innerText = 'Comic Sans' 62 | code.style.fontFamily = '"Comic Sans MS", cursive, sans-serif' 63 | } else { 64 | btnFont.innerText = '' 65 | code.style.fontFamily = 'sans-serif' 66 | } 67 | }) 68 | } 69 | 70 | function initStyleClickers () { 71 | const styleClickers = document.querySelectorAll('.remark-slides-area .onClickStyle') 72 | for (const styleClicker of styleClickers) { 73 | styleClicker.extraClasses = [...styleClicker.classList].filter(c => c !== 'onClickStyle') 74 | styleClicker.classList = 'onClickStyle' 75 | styleClicker.style.cursor = 'pointer' 76 | styleClicker.addEventListener('click', function (ev) { 77 | console.log(styleClicker) 78 | if (styleClicker.classList.length > 1) { 79 | styleClicker.classList = 'onClickStyle' 80 | } else { 81 | styleClicker.classList = ['onClickStyle', ...styleClicker.extraClasses].join(' ') 82 | } 83 | }) 84 | } 85 | } 86 | 87 | function connectElements (i, startSocket = 'top', endSocket = 'auto') { 88 | const startElements = document 89 | .querySelectorAll(`.remark-slides-area .leader-start-${i}`) 90 | 91 | startElements.forEach(function (start) { 92 | const end = start.closest('.remark-slide-content').querySelector(`.leader-end-${i}`) 93 | start.line = new LeaderLine(start, end, { 94 | hide: !start.closest('.remark-slide-container').classList.contains('remark-visible') 95 | }) 96 | start.line.setOptions({ startSocket, endSocket }) 97 | const lineEl = document.querySelector('body > svg.leader-line') 98 | const slide = start.closest('.remark-slides-area') 99 | slide.appendChild(lineEl) 100 | return start.line 101 | }) 102 | } 103 | 104 | function showOrDrawLineElement (el) { 105 | const lineClass = [...el.classList].filter(c => c.match(/^leader-start/))[0] 106 | const lineElements = el.closest('.remark-slides-area').querySelectorAll('.' + lineClass) 107 | el.line.position() 108 | if (lineElements[0] === el) { 109 | el.line.show('draw') 110 | } else { 111 | el.line.show('none') 112 | } 113 | } 114 | 115 | function initializeLeaderLines () { 116 | connectElements(1, 'bottom') 117 | connectElements(2, 'bottom') 118 | connectElements(3, 'bottom', 'top') 119 | connectElements(4, 'bottom') 120 | connectElements(5, 'left', 'bottom') 121 | 122 | slideshow.on('hideSlide', function (slide) { 123 | const idx = slide.getSlideIndex() 124 | const slides = document.querySelectorAll('.remark-slides-area .remark-slide-container') 125 | const thisSlide = slides[idx] 126 | const lineLeaders = thisSlide.querySelectorAll('[class^="leader-start"]') 127 | if (lineLeaders.length) { 128 | lineLeaders.forEach(el => el.line.hide('none')) 129 | } 130 | }) 131 | 132 | slideshow.on('afterShowSlide', function (slide) { 133 | const idx = slide.getSlideIndex() 134 | const slides = document.querySelectorAll('.remark-slides-area .remark-slide-container') 135 | const thisSlide = slides[idx] 136 | const lineLeaders = thisSlide.querySelectorAll('[class^="leader-start"]') 137 | if (lineLeaders.length) { 138 | lineLeaders.forEach(function (el) { 139 | showOrDrawLineElement(el) 140 | }) 141 | } 142 | }) 143 | 144 | window.addEventListener('resize', function () { 145 | document 146 | .querySelectorAll('.remark-slides-area [class^="leader-start"]') 147 | .forEach(el => el.line.position()) 148 | }) 149 | } 150 | 151 | ready(emailAttachment) 152 | ready(sqlMagnifier) 153 | ready(wordButtons) 154 | ready(initStyleClickers) 155 | ready(initializeLeaderLines) 156 | })() 157 | -------------------------------------------------------------------------------- /docs/libs/tile-view/tile-view.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Tile View for remark.js Slides 3 | * 4 | * Garrick Aden-Buie 5 | * 6 | * Inspired and converted to Vanilla JS from 7 | * https://github.com/StephenHesperus/remark-hook/ 8 | * 9 | * Include after remarkjs slides are initialized. 10 | * 11 | */ 12 | 13 | /* global slideshow */ 14 | (function () { 15 | const ready = function (fn) { 16 | /* MIT License Copyright (c) 2016 Nuclei */ 17 | /* https://github.com/nuclei/readyjs */ 18 | const completed = () => { 19 | document.removeEventListener('DOMContentLoaded', completed) 20 | window.removeEventListener('load', completed) 21 | fn() 22 | } 23 | if (document.readyState !== 'loading') { 24 | setTimeout(fn) 25 | } else { 26 | document.addEventListener('DOMContentLoaded', completed) 27 | window.addEventListener('load', completed) 28 | } 29 | } 30 | 31 | ready(function () { 32 | const launchKey = 79 // keycode for O, used to enable tile view 33 | 34 | // Slides container 35 | const remarkSlideShow = document.querySelector('div.remark-slides-area') 36 | 37 | let tileView = document.querySelector('div.remark__tile-view') 38 | if (!tileView) { 39 | tileView = document.createElement('div') 40 | tileView.className = 'remark__tile-view' 41 | } 42 | 43 | const toggleElement = el => { 44 | el.style.display = el.style.display === 'none' ? '' : 'none' 45 | } 46 | 47 | const toggleTileView = function () { 48 | toggleElement(tileView) 49 | toggleElement(remarkSlideShow) 50 | 51 | if (tileView.style.display === 'none') { 52 | // tileView is now hidden, go to current slide 53 | slideshow.gotoSlide(tileVars.currentSlideIdx + 1) 54 | 55 | // remove scroll/mousewheel event blocking 56 | tileView.removeEventListener('mousewheel', blockEvent) 57 | tileView.removeEventListener('DOMMouseScroll', blockEvent) 58 | console.log('removing blockScaling') 59 | document.removeEventListener('keydown', blockScaling) 60 | } else { 61 | // store current slide index prior to launching tile-view 62 | tileVars.currentSlideIdx = slideshow.getCurrentSlideIndex() 63 | 64 | // set class on seen and current slide and scroll into view 65 | const tiles = tileView.querySelectorAll('.remark__tile-view__tile'); 66 | [...tiles].forEach((tile, idx) => { 67 | tile.classList.toggle( 68 | 'remark__tile-view__tile--seen', 69 | idx < tileVars.currentSlideIdx 70 | ) 71 | tile.classList.toggle( 72 | 'remark__tile-view__tile--current', 73 | idx === tileVars.currentSlideIdx 74 | ) 75 | }) 76 | tiles[tileVars.currentSlideIdx].scrollIntoView({ 77 | behavior: 'smooth', 78 | block: 'center' 79 | }) 80 | 81 | // block remarkjs from handling scroll events 82 | tileView.addEventListener('mousewheel', blockEvent) 83 | tileView.addEventListener('DOMMouseScroll', blockEvent) 84 | console.log('adding blockScaling') 85 | document.addEventListener('keydown', blockScaling) 86 | } 87 | } 88 | 89 | const createTileView = ({ minSize = 250, title = document.title } = {}) => { 90 | // Tile view header 91 | const h1 = document.createElement('h1') 92 | h1.className = 'remark__tile-view__header' 93 | h1.innerHTML = title 94 | 95 | tileView.appendChild(h1) 96 | const tiles = document.createElement('div') 97 | tiles.className = 'remark__tile-view__tiles' 98 | tileView.appendChild(tiles) 99 | 100 | // Clone slideshow 101 | const slidesArea = remarkSlideShow.cloneNode(true) 102 | 103 | // Calculate slide scale and tile container size 104 | const slideScaler = slidesArea.querySelector('.remark-slide-scaler') 105 | const slideWidth = parseFloat(slideScaler.style.width.replace('px', '')) 106 | const slideHeight = parseFloat( 107 | slideScaler.style.height.replace('px', '') 108 | ) 109 | const scale = minSize / Math.min(slideWidth, slideHeight) 110 | let tileWidth = Math.round(slideWidth * scale) 111 | let tileHeight = Math.round(slideHeight * scale) 112 | 113 | // convert tileWidth/Height to em relative to base 18px (set in CSS) 114 | tileWidth = tileWidth / 18 115 | tileHeight = tileHeight / 18 116 | 117 | tiles.style.gridTemplateColumns = `repeat(auto-fill, minmax(${tileWidth}em, 1fr))` 118 | 119 | const slides = slidesArea.querySelectorAll('.remark-slide-container') 120 | 121 | slides.forEach((slide, slideIndex) => { 122 | let tile = document.createElement('template') 123 | tile.innerHTML = `
124 |
125 |
` 126 | tile = tile.content.firstChild 127 | 128 | const tileContainer = tile.querySelector( 129 | '.remark__tile-view__slide-container' 130 | ) 131 | tileContainer.style.width = `${tileWidth}em` 132 | tileContainer.style.height = `${tileHeight}em` 133 | 134 | const thisSlideScaler = slide.querySelector('.remark-slide-scaler') 135 | thisSlideScaler.style.top = '0px' 136 | thisSlideScaler.style.left = '0px' 137 | thisSlideScaler.style.transform = `scale(${scale})` 138 | thisSlideScaler.parentElement.classList.add('remark-visible') 139 | 140 | slide.addEventListener('click', () => { 141 | tileVars.currentSlideIdx = slideIndex 142 | toggleTileView() 143 | }) 144 | 145 | tileContainer.appendChild(slide) 146 | tiles.appendChild(tile) 147 | }) 148 | 149 | document.body.appendChild(tileView) 150 | } 151 | 152 | const tileVars = {} 153 | const blockEvent = ev => ev.stopPropagation() 154 | const blockScaling = function (ev) { 155 | if (ev.controlKey || ev.metaKey) { 156 | if (ev.key === '=' || ev.key === '-') { 157 | ev.preventDefault() 158 | console.log('window scaling is not allowed inside the tile overview') 159 | } 160 | } 161 | } 162 | 163 | document.addEventListener('keydown', ev => { 164 | if (ev.keyCode === launchKey) { 165 | toggleTileView() 166 | } 167 | }) 168 | 169 | const addTileViewHelpText = () => { 170 | const helpTable = document.querySelector( 171 | '.remark-help-content table.light-keys' 172 | ) 173 | if (!helpTable) { 174 | console.error( 175 | 'Could not find remark help table, has remark been initialized?' 176 | ) 177 | return 178 | } 179 | const newRow = document.createElement('tr') 180 | newRow.innerHTML += 'o' 181 | newRow.innerHTML += 'Tile View: Overview of Slides' 182 | helpTable.append(newRow) 183 | } 184 | 185 | createTileView({ minSize: 200 }) 186 | toggleElement(tileView) 187 | addTileViewHelpText() 188 | }) 189 | })() 190 | -------------------------------------------------------------------------------- /docs/css/part-two.min.css: -------------------------------------------------------------------------------- 1 | .remark-code-line-highlighted{background-color:#f0f0f0}.remark-code-line-highlighted{border-top:2px solid #ddd;border-left:2px solid #ddd;border-right:2px solid #ddd}.remark-code-line-highlighted+.remark-code-line-highlighted{border-top:none;border-bottom:none}.remark-code-line-highlighted:last-of-type{border-bottom:2px solid #ddd}.remark-code-line-highlighted+div{border-top:2px solid #aaa}pre code.remark-code.hljs{background:#fff}/*! ---- thought bubble ---- */.bubble p{position:relative;width:300px;text-align:center;line-height:1.4em;margin:40px auto;background-color:#fff;border:8px solid #333;border-radius:30px;font-size:1.25em}.thought p{width:500px;border-radius:200px;padding:30px}.bubble p:after,.bubble p:before{content:' ';position:absolute;width:0;height:0}.speech p:before{left:30px;bottom:-50px;border:25px solid;border-color:#333 transparent transparent #333}.speech p:after{left:38px;bottom:-30px;border:15px solid;border-color:#fff transparent transparent #fff}.thought p:after,.thought p:before{right:10px;bottom:-30px;width:40px;height:40px;background-color:#fff;border:8px solid #666;-webkit-border-radius:28px;-moz-border-radius:28px;border-radius:28px}.thought p:after{width:20px;height:20px;right:5px;bottom:-40px;border-color:#999;-webkit-border-radius:18px;-moz-border-radius:18px;border-radius:18px}/*! https://github.com/miniMAC/magic/blob/master/dist/magic.css */.remark-visible .vanishIn{-webkit-animation-name:vanishIn;animation-name:vanishIn;-webkit-animation-duration:2s;animation-duration:2s;-webkit-animation-fill-mode:both;animation-fill-mode:both}@-webkit-keyframes vanishIn{0%{opacity:0;transform-origin:50% 50%;transform:scale(2,2);-webkit-filter:blur(90px);filter:blur(90px)}100%{opacity:1;transform-origin:50% 50%;transform:scale(1,1);-webkit-filter:blur(0);filter:blur(0)}}@keyframes vanishIn{0%{opacity:0;transform-origin:50% 50%;transform:scale(2,2);-webkit-filter:blur(90px);filter:blur(90px)}100%{opacity:1;transform-origin:50% 50%;transform:scale(1,1);-webkit-filter:blur(0);filter:blur(0)}}/*! ---- typing animation ---- */.remark-visible .css-typing p{opacity:0;border-right:.15em solid orange;white-space:nowrap;overflow:hidden;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards}.remark-visible .css-typing p:nth-child(1){width:870px;-webkit-animation:type 3s steps(70,end);animation:type 3s steps(70,end);-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards}.remark-visible .css-typing p:nth-child(2){width:390px;-webkit-animation:type 2s steps(31,end);animation:type 2s steps(31,end);-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards;-webkit-animation-delay:4s;animation-delay:4s}.remark-visible .css-typing p:nth-child(3){width:110px;-webkit-animation:type .5s steps(7,end);animation:type .5s steps(7,end);-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards;-webkit-animation-delay:7s;animation-delay:7s}.remark-visible .css-typing p:nth-child(4){width:100px;-webkit-animation:type .5s steps(7,end);animation:type .5s steps(7,end);-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards;-webkit-animation-delay:7.5s;animation-delay:7.5s}@keyframes type{0%{width:0}1%{opacity:1}99.9%{border-right:.15em solid orange}100%{opacity:1;border:none}}@-webkit-keyframes type{0%{width:0}1%{opacity:1}99.9%{border-right:.15em solid orange}100%{opacity:1;border:none}}@keyframes blink{50%{border-color:transparent}}@-webkit-keyframes blink{50%{border-color:tranparent}}/*! ---- email attachment ---- */#email-attachment:hover{color:#eb1455}.zoomForward{animation:zoomForward 1s cubic-bezier(.45,-.12,.51,.71);transform-origin:center}@keyframes zoomForward{0%{transform:scale(1)}100%{opacity:0;transform:scale(25)}}@-webkit-keyframes zoomForward{0%{transform:scale(1)}100%{opacity:0;transform:scale(25)}}/*! ---- sql code two column ---- */.code-two-column .remark-code{font-size:.33em;-webkit-column-count:2;-moz-column-count:2;column-count:2;-webkit-column-gap:20px;-moz-column-gap:20px;column-gap:20px}.code-two-column pre{margin-top:0;padding-left:1em;border-top-left-radius:8px;border-top-right-radius:8px}.code-window-title-bar{border-top-left-radius:8px;border-top-right-radius:8px;padding-top:10px}.code-window-circle{width:17px;height:18px;margin-right:6px}.code-window-circles{position:absolute;left:12px;top:11px}#sql-magnifier{position:absolute;width:90%;font-size:1em;top:20px}#sql-magnifier .hljs{background-color:#fff;height:60px}.code-two-column pre{cursor:url(../figures/magnifying-glass.cur),zoom-in}/*! ---- ms word ---- */.word-doc{background-image:url(../figures/word-doc.png);background-size:cover;overflow:hidden;height:100%;padding-top:5em}.word-doc pre{max-height:66%;overflow:hidden;position:absolute;width:90%}.word-doc pre code.sql{background:#fff;font-family:sans-serif;max-width:100%;overflow-x:hidden}.word-doc pre code.sql .remark-code-line:hover{font-weight:400}.word-doc pre code.sql .remark-code-line:hover:before{content:none;color:#6d7e8a;position:absolute;transform:none;animation:none}#sql-font-size-down{background:0 0;width:35px;height:30px;position:absolute;top:77px;left:404px;border:none;cursor:pointer}#sql-font-size-up{background:0 0;width:35px;height:30px;position:absolute;top:77px;left:365px;border:none;cursor:pointer}#sql-font-comic-sans{background:0 0;text-align:center;width:125px;height:30px;padding:0;position:absolute;top:77px;left:138px;border:none;cursor:pointer;font-size:21px}/*! magic.css https://github.com/miniMAC/magic/blob/master/dist/magic.css */.remark-fading .boingOutDown{-webkit-animation-name:boingOutDown;animation-name:boingOutDown}@-webkit-keyframes boingOutDown{0%{opacity:1;transform-origin:100% 100%;transform:perspective(800px) rotateX(0) rotateY(0)}20%{opacity:1;transform-origin:100% 100%;transform:perspective(800px) rotateX(0) rotateY(10deg)}30%{opacity:1;transform-origin:0 100%;transform:perspective(800px) rotateX(0) rotateY(0)}40%{opacity:1;transform-origin:0 100%;transform:perspective(800px) rotateX(10deg) rotateY(10deg)}100%{opacity:0;transform-origin:100% 100%;transform:perspective(800px) rotateX(90deg) rotateY(0)}}@keyframes boingOutDown{0%{opacity:1;transform-origin:100% 100%;transform:perspective(800px) rotateX(0) rotateY(0)}20%{opacity:1;transform-origin:100% 100%;transform:perspective(800px) rotateX(0) rotateY(10deg)}30%{opacity:1;transform-origin:0 100%;transform:perspective(800px) rotateX(0) rotateY(0)}40%{opacity:1;transform-origin:0 100%;transform:perspective(800px) rotateX(10deg) rotateY(10deg)}100%{opacity:0;transform-origin:100% 100%;transform:perspective(800px) rotateX(90deg) rotateY(0)}}/*! ---- css-focus-sliding-bg ---- */.focus-ul{background-image:linear-gradient(90deg,#eb1455,#eb1455);background-size:0 100%;background-repeat:no-repeat;transition:background-size .25s ease-in,color .5s ease-in}.focus-ul:focus{outline:0;color:#fff;background-size:100% 100%}del{text-decoration-color:#eb1455;text-decoration-thickness:10px}/*! ---- Code Example ---- */.code-example{position:absolute;top:100px}.leader-line{z-index:100}[class^=leader-start]{color:var(--orange);font-size:1.1em;width:450px;margin:0;position:absolute;text-align:center}.leader-start-1{top:10%;right:30%}.leader-start-2{top:15%;right:20%}.expand-in{max-height:0;overflow:hidden}.remark-fading .expand-in{max-height:600px}.remark-visible .expand-in{animation:expand-in 1.5s linear;animation-fill-mode:forwards}@keyframes expand-in{0%{max-height:0}100%{max-height:600px}}.leader-start-3{top:20%;right:10%}.reveal-focus{position:relative;cursor:pointer}.reveal-focus:focus{outline:0}.reveal-focus .reveal-shown{transition:opacity 150ms linear}.reveal-focus .reveal-other{position:absolute;transition:opacity 150ms linear;width:1000px;left:0}.reveal-focus:not(:focus) .reveal-other{opacity:0}.reveal-focus:focus .reveal-shown{opacity:0}.leader-start-4{top:45%;right:0}.leader-start-5{bottom:15%;right:5%} 2 | /*# sourceMappingURL=part-two.min.css.map */ -------------------------------------------------------------------------------- /docs/libs/xaringanExtra-shareagain/shareagain.css: -------------------------------------------------------------------------------- 1 | /* Animations by animate.css */ 2 | 3 | @-webkit-keyframes slideOutDown { 4 | from { 5 | -webkit-transform: translate3d(0, 0, 0); 6 | transform: translate3d(0, 0, 0); 7 | } 8 | 9 | to { 10 | visibility: hidden; 11 | -webkit-transform: translate3d(0, 100%, 0); 12 | transform: translate3d(0, 100%, 0); 13 | } 14 | } 15 | 16 | @keyframes slideOutDown { 17 | from { 18 | -webkit-transform: translate3d(0, 0, 0); 19 | transform: translate3d(0, 0, 0); 20 | } 21 | 22 | to { 23 | visibility: hidden; 24 | -webkit-transform: translate3d(0, 100%, 0); 25 | transform: translate3d(0, 100%, 0); 26 | } 27 | } 28 | 29 | @-webkit-keyframes slideInUp { 30 | from { 31 | -webkit-transform: translate3d(0, 100%, 0); 32 | transform: translate3d(0, 100%, 0); 33 | visibility: visible; 34 | } 35 | 36 | to { 37 | -webkit-transform: translate3d(0, 0, 0); 38 | transform: translate3d(0, 0, 0); 39 | } 40 | } 41 | 42 | @keyframes slideInUp { 43 | from { 44 | -webkit-transform: translate3d(0, 100%, 0); 45 | transform: translate3d(0, 100%, 0); 46 | visibility: visible; 47 | } 48 | 49 | to { 50 | -webkit-transform: translate3d(0, 0, 0); 51 | transform: translate3d(0, 0, 0); 52 | } 53 | } 54 | 55 | @-webkit-keyframes slideOutRight { 56 | from { 57 | -webkit-transform: translate3d(0, 0, 0); 58 | transform: translate3d(0, 0, 0); 59 | } 60 | 61 | to { 62 | visibility: hidden; 63 | -webkit-transform: translate3d(100%, 0, 0); 64 | transform: translate3d(100%, 0, 0); 65 | } 66 | } 67 | 68 | @keyframes slideOutRight { 69 | from { 70 | -webkit-transform: translate3d(0, 0, 0); 71 | transform: translate3d(0, 0, 0); 72 | } 73 | 74 | to { 75 | visibility: hidden; 76 | -webkit-transform: translate3d(100%, 0, 0); 77 | transform: translate3d(100%, 0, 0); 78 | } 79 | } 80 | 81 | @-webkit-keyframes slideInRight { 82 | from { 83 | -webkit-transform: translate3d(100%, 0, 0); 84 | transform: translate3d(100%, 0, 0); 85 | visibility: visible; 86 | } 87 | 88 | to { 89 | -webkit-transform: translate3d(0, 0, 0); 90 | transform: translate3d(0, 0, 0); 91 | } 92 | } 93 | 94 | @keyframes slideInRight { 95 | from { 96 | -webkit-transform: translate3d(100%, 0, 0); 97 | transform: translate3d(100%, 0, 0); 98 | visibility: visible; 99 | } 100 | 101 | to { 102 | -webkit-transform: translate3d(0, 0, 0); 103 | transform: translate3d(0, 0, 0); 104 | } 105 | } 106 | 107 | .slideOutDown { 108 | -webkit-animation-name: slideOutDown; 109 | animation-name: slideOutDown; 110 | -webkit-animation-duration: 1s; 111 | animation-duration: 1s; 112 | -webkit-animation-fill-mode: both; 113 | animation-fill-mode: both; 114 | } 115 | 116 | .slideInUp { 117 | -webkit-animation-name: slideInUp; 118 | animation-name: slideInUp; 119 | -webkit-animation-duration: 1s; 120 | animation-duration: 1s; 121 | -webkit-animation-fill-mode: both; 122 | animation-fill-mode: both; 123 | } 124 | 125 | .slideInRight { 126 | -webkit-animation-name: slideInRight; 127 | animation-name: slideInRight; 128 | -webkit-animation-duration: 1s; 129 | animation-duration: 1s; 130 | -webkit-animation-fill-mode: both; 131 | animation-fill-mode: both; 132 | } 133 | 134 | .slideOutRight { 135 | -webkit-animation-name: slideOutRight; 136 | animation-name: slideOutRight; 137 | -webkit-animation-duration: 1s; 138 | animation-duration: 1s; 139 | -webkit-animation-fill-mode: both; 140 | animation-fill-mode: both; 141 | } 142 | 143 | /* shareagain css rules */ 144 | .shareagain-bar { 145 | --shareagain-background: #00000088; 146 | --shareagain-foreground: #FFFFFF; 147 | --shareagain-twitter: inline-block; 148 | --shareagain-facebook: inline-block; 149 | --shareagain-linkedin: inline-block; 150 | --shareagain-pinterest: inline-block; 151 | --shareagain-pocket: inline-block; 152 | --shareagain-reddit: inline-block; 153 | } 154 | 155 | .shareagain-bar { 156 | position: absolute; 157 | bottom: 0; 158 | left: 0; 159 | right: 0; 160 | height: 66px; 161 | z-index: 1000; 162 | } 163 | 164 | .shareagain-bar ul { 165 | background: var(--shareagain-background); 166 | color: var(--shareagain-foreground); 167 | display: grid; 168 | grid-template-columns: auto 1fr auto; 169 | grid-gap: 5px; 170 | justify-items: center; 171 | align-items: center; 172 | font-family: sans-serif; 173 | list-style: none; 174 | padding-left: 0; 175 | margin: 0; 176 | height: 100%; 177 | width: 100%; 178 | } 179 | 180 | .shareagain-buttons { 181 | height: 100%; 182 | } 183 | 184 | .shareagain-button { 185 | height: 100%; 186 | width: 66px; 187 | border: none; 188 | padding: 15px; 189 | color: white; 190 | background: transparent; 191 | } 192 | 193 | .shareagain-button:hover { 194 | background: rgba(0,0,0,.25); 195 | } 196 | 197 | .shareagain-button.disabled { 198 | color: #aaa; 199 | cursor: not-allowed; 200 | } 201 | 202 | .shareagain-button svg { 203 | height: 100%; 204 | width: 100%; 205 | } 206 | 207 | .shareagain-button svg path { 208 | stroke-width: 1px; 209 | } 210 | 211 | .shareagain-title { 212 | font-size: 24px; 213 | text-align: center; 214 | max-height: 100%; 215 | overflow: hidden; 216 | } 217 | 218 | .shareagain-bar .shareon { 219 | font-size: 0 !important; 220 | position: absolute; 221 | top: -45px; 222 | width: 100vw; 223 | right: 0; 224 | text-align: right; 225 | -webkit-transform: translate3d(100%, 0, 0); 226 | transform: translate3d(100%, 0, 0); 227 | } 228 | 229 | .shareagain-bar .shareon > .link { 230 | background-color: #cacaca; 231 | } 232 | .shareagain-bar .shareon > .link::before { 233 | background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke='currentColor' class='clipboard-copy w-6 h-6'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M8 5H6a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2v-1M8 5a2 2 0 002 2h2a2 2 0 002-2M8 5a2 2 0 012-2h2a2 2 0 012 2m0 0h2a2 2 0 012 2v3m2 4H10m0 0l3-3m-3 3l3 3'%3E%3C/path%3E%3C/svg%3E"); 234 | } 235 | 236 | .shareagain-bar .shareon > .link.success::before { 237 | background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke='currentColor' class='clipboard-check w-6 h-6'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4'%3E%3C/path%3E%3C/svg%3E"); 238 | } 239 | 240 | .shareagain-bar .shareon .twitter { display: var(--shareagain-twitter); } 241 | .shareagain-bar .shareon .facebook { display: var(--shareagain-facebook); } 242 | .shareagain-bar .shareon .linkedin { display: var(--shareagain-linkedin); } 243 | .shareagain-bar .shareon .pinterest { display: var(--shareagain-pinterest); } 244 | .shareagain-bar .shareon .pocket { display: var(--shareagain-pocket); } 245 | .shareagain-bar .shareon .reddit { display: var(--shareagain-reddit); } 246 | 247 | @media screen and (max-width: 600px) { 248 | .shareagain-bar { 249 | font-size: 10px; 250 | height: 50px; 251 | } 252 | .shareagain-buttons .shareagain-button { 253 | width: 50px; 254 | } 255 | .shareagain-title { 256 | font-size: 16px; 257 | } 258 | } 259 | @media screen and (max-width: 400px) { 260 | .shareagain-bar { 261 | height: 30px; 262 | } 263 | .shareagain-bar ul { 264 | grid-template-columns: auto auto; 265 | justify-content: space-between; 266 | } 267 | .shareagain-bar .shareagain-title { 268 | display: none; 269 | } 270 | .shareagain-buttons .shareagain-button { 271 | width: 30px; 272 | padding: 5px; 273 | } 274 | } 275 | 276 | @media screen and (max-width: 400px) { 277 | .shareagain-bar { 278 | height: 30px; 279 | } 280 | .shareagain-bar ul { 281 | grid-template-columns: auto auto; 282 | justify-content: space-between; 283 | } 284 | .shareagain-bar .shareagain-title { 285 | display: none; 286 | } 287 | .shareagain-buttons .shareagain-button { 288 | width: 30px; 289 | padding: 5px; 290 | } 291 | .shareagain-buttons .shareon > * { 292 | width: 15px; 293 | height: 20px; 294 | } 295 | .shareagain-buttons .shareon > ::before { 296 | transform: scale(0.66); 297 | top: 7px; 298 | } 299 | .shareagain-buttons .shareon .link { 300 | width: 23px; 301 | } 302 | .shareagain-buttons .shareon > .link::before { 303 | top: 5px; 304 | left: 6px; 305 | } 306 | } 307 | -------------------------------------------------------------------------------- /docs/css/moffitt-xaringan.css: -------------------------------------------------------------------------------- 1 | /* ------------------------------------------------------- 2 | * 3 | * !! This file was generated by xaringanthemer !! 4 | * 5 | * Changes made to this file directly will be overwritten 6 | * if you used xaringanthemer in your xaringan slides Rmd 7 | * 8 | * Issues or likes? 9 | * - https://github.com/gadenbuie/xaringanthemer 10 | * - https://www.garrickadenbuie.com 11 | * 12 | * Need help? Try: 13 | * - vignette(package = "xaringanthemer") 14 | * - ?xaringanthemer::style_xaringan 15 | * - xaringan wiki: https://github.com/yihui/xaringan/wiki 16 | * - remarkjs wiki: https://github.com/gnab/remark/wiki 17 | * 18 | * Version: 0.3.0 19 | * 20 | * ------------------------------------------------------- */ 21 | @import url(https://fonts.googleapis.com/css?family=Roboto:400,300,700&display=swap); 22 | @import url(https://fonts.googleapis.com/css?family=Roboto:300&display=swap); 23 | @import url(https://fonts.googleapis.com/css?family=IBM+Plex+Mono:300,400&display=swap); 24 | @import url(https://fonts.googleapis.com/css?family=IBM+Plex+Serif:600,600i,400,400i&display=swap); 25 | 26 | :root { 27 | /* Fonts */ 28 | --text-font-family: Roboto; 29 | --text-font-is-google: 1; 30 | --text-font-family-fallback: -apple-system, BlinkMacSystemFont, avenir next, avenir, helvetica neue, helvetica, Ubuntu, roboto, noto, segoe ui, arial; 31 | --text-font-base: sans-serif; 32 | --header-font-family: Roboto; 33 | --header-font-is-google: 1; 34 | --code-font-family: 'IBM Plex Mono'; 35 | --code-font-is-google: 1; 36 | --base-font-size: 28px; 37 | --text-font-size: 1rem; 38 | --code-font-size: 0.9rem; 39 | --code-inline-font-size: 1em; 40 | --header-h1-font-size: 2.5rem; 41 | --header-h2-font-size: 2rem; 42 | --header-h3-font-size: 1.75rem; 43 | 44 | /* Colors */ 45 | --text-color: #000000; 46 | --header-color: #062f6e; 47 | --background-color: #FFFFFF; 48 | --link-color: var(--red); 49 | --text-bold-color: #062f6E; 50 | --code-highlight-color: rgba(255,255,0,0.5); 51 | --inverse-text-color: white; 52 | --inverse-background-color: #062f6E; 53 | --inverse-header-color: white; 54 | --title-slide-background-color: #062f6e; 55 | --title-slide-text-color: #FFFFFF; 56 | --header-background-color: #062f6e; 57 | --header-background-text-color: #FFFFFF; 58 | --primary: #062f6e; 59 | --secondary: #062f6E; 60 | --white: #FFFFFF; 61 | --black: #000000; 62 | --blue: #062f6E; 63 | --blue-medium: #39C0E0; 64 | --blue-light: #A0D9E6; 65 | --green: #82c878; 66 | --yellow: #F5BE0B; 67 | --orange: #FE6B00; 68 | --pink: #F79FC8; 69 | --red: #eb1455; 70 | --moffitt-gray: #bec3c3; 71 | } 72 | 73 | html { 74 | font-size: var(--base-font-size); 75 | } 76 | 77 | body { 78 | font-family: var(--text-font-family), var(--text-font-family-fallback), var(--text-font-base); 79 | font-weight: 400; 80 | color: var(--text-color); 81 | } 82 | h1, h2, h3 { 83 | font-family: var(--header-font-family); 84 | font-weight: 300; 85 | color: var(--header-color); 86 | } 87 | .remark-slide-content { 88 | background-color: var(--background-color); 89 | font-size: 1rem; 90 | padding: 0 70px 0 50px; 91 | width: 100%; 92 | height: 100%; 93 | } 94 | .remark-slide-content h1 { 95 | font-size: var(--header-h1-font-size); 96 | } 97 | .remark-slide-content h2 { 98 | font-size: var(--header-h2-font-size); 99 | } 100 | .remark-slide-content h3 { 101 | font-size: var(--header-h3-font-size); 102 | } 103 | .remark-code, .remark-inline-code { 104 | font-family: var(--code-font-family), Menlo, Consolas, Monaco, Liberation Mono, Lucida Console, monospace; 105 | } 106 | .remark-code { 107 | font-size: var(--code-font-size); 108 | } 109 | .remark-inline-code { 110 | font-size: var(--code-inline-font-size); 111 | color: var(--orange); 112 | } 113 | .remark-slide-number { 114 | color: #062f6e; 115 | opacity: 1; 116 | font-size: 0.75rem; 117 | } 118 | strong { color: var(--text-bold-color); } 119 | a, a > code { 120 | color: var(--link-color); 121 | text-decoration: none; 122 | } 123 | .footnote { 124 | position: absolute; 125 | bottom: 0px; 126 | padding-right: 4em; 127 | font-size: 0.9em; 128 | } 129 | .remark-code-line-highlighted { 130 | background-color: var(--code-highlight-color); 131 | } 132 | .inverse { 133 | background-color: var(--inverse-background-color); 134 | color: var(--inverse-text-color); 135 | 136 | } 137 | .inverse h1, .inverse h2, .inverse h3 { 138 | color: var(--inverse-header-color); 139 | } 140 | .title-slide, .title-slide h1, .title-slide h2, .title-slide h3 { 141 | color: var(--title-slide-text-color); 142 | } 143 | .title-slide { 144 | background-color: var(--title-slide-background-color); 145 | } 146 | .title-slide .remark-slide-number { 147 | display: none; 148 | } 149 | /* Two-column layout */ 150 | .left-column { 151 | width: 20%; 152 | height: 92%; 153 | float: left; 154 | } 155 | .left-column h2, .left-column h3 { 156 | color: #062f6e99; 157 | } 158 | .left-column h2:last-of-type, .left-column h3:last-child { 159 | color: #062f6e; 160 | } 161 | .right-column { 162 | width: 75%; 163 | float: right; 164 | padding-top: 1em; 165 | } 166 | .pull-left { 167 | float: left; 168 | width: 47%; 169 | } 170 | .pull-right { 171 | float: right; 172 | width: 47%; 173 | } 174 | .pull-right ~ * { 175 | clear: both; 176 | } 177 | img, video, iframe { 178 | max-width: 100%; 179 | } 180 | blockquote { 181 | border-left: solid 5px var(--blue-light); 182 | padding-left: 1em; 183 | } 184 | .remark-slide table { 185 | margin: auto; 186 | border-top: 1px solid #666; 187 | border-bottom: 1px solid #666; 188 | } 189 | .remark-slide table thead th { 190 | border-bottom: 1px solid #ddd; 191 | } 192 | th, td { 193 | padding: 5px; 194 | } 195 | .remark-slide thead, .remark-slide tfoot, .remark-slide tr:nth-child(even) { 196 | background: #f0f0f0; 197 | } 198 | table.dataTable tbody { 199 | background-color: var(--background-color); 200 | color: var(--text-color); 201 | } 202 | table.dataTable.display tbody tr.odd { 203 | background-color: var(--background-color); 204 | } 205 | table.dataTable.display tbody tr.even { 206 | background-color: #f0f0f0; 207 | } 208 | table.dataTable.hover tbody tr:hover, table.dataTable.display tbody tr:hover { 209 | background-color: rgba(255, 255, 255, 0.5); 210 | } 211 | .dataTables_wrapper .dataTables_length, .dataTables_wrapper .dataTables_filter, .dataTables_wrapper .dataTables_info, .dataTables_wrapper .dataTables_processing, .dataTables_wrapper .dataTables_paginate { 212 | color: var(--text-color); 213 | } 214 | .dataTables_wrapper .dataTables_paginate .paginate_button { 215 | color: var(--text-color) !important; 216 | } 217 | 218 | /* Slide Header Background for h1 elements */ 219 | .remark-slide-content.header_background > h1 { 220 | display: block; 221 | position: absolute; 222 | top: 0; 223 | left: 0; 224 | width: 100%; 225 | background: var(--header-background-color); 226 | color: var(--header-background-text-color); 227 | padding: 2rem 70px 1.5rem 50px; 228 | margin-top: 0; 229 | box-sizing: border-box; 230 | } 231 | .remark-slide-content.header_background { 232 | padding-top: 7rem; 233 | } 234 | 235 | @page { margin: 0; } 236 | @media print { 237 | .remark-slide-scaler { 238 | width: 100% !important; 239 | height: 100% !important; 240 | transform: scale(1) !important; 241 | top: 0 !important; 242 | left: 0 !important; 243 | } 244 | } 245 | 246 | .primary { 247 | color: var(--primary); 248 | } 249 | .bg-primary { 250 | background-color: var(--primary); 251 | } 252 | .secondary { 253 | color: var(--secondary); 254 | } 255 | .bg-secondary { 256 | background-color: var(--secondary); 257 | } 258 | .white { 259 | color: var(--white); 260 | } 261 | .bg-white { 262 | background-color: var(--white); 263 | } 264 | .black { 265 | color: var(--black); 266 | } 267 | .bg-black { 268 | background-color: var(--black); 269 | } 270 | .blue { 271 | color: var(--blue); 272 | } 273 | .bg-blue { 274 | background-color: var(--blue); 275 | } 276 | .blue-medium { 277 | color: var(--blue-medium); 278 | } 279 | .bg-blue-medium { 280 | background-color: var(--blue-medium); 281 | } 282 | .blue-light { 283 | color: var(--blue-light); 284 | } 285 | .bg-blue-light { 286 | background-color: var(--blue-light); 287 | } 288 | .green { 289 | color: var(--green); 290 | } 291 | .bg-green { 292 | background-color: var(--green); 293 | } 294 | .yellow { 295 | color: var(--yellow); 296 | } 297 | .bg-yellow { 298 | background-color: var(--yellow); 299 | } 300 | .orange { 301 | color: var(--orange); 302 | } 303 | .bg-orange { 304 | background-color: var(--orange); 305 | } 306 | .pink { 307 | color: var(--pink); 308 | } 309 | .bg-pink { 310 | background-color: var(--pink); 311 | } 312 | .red { 313 | color: var(--red); 314 | } 315 | .bg-red { 316 | background-color: var(--red); 317 | } 318 | .moffitt-gray { 319 | color: var(--moffitt-gray); 320 | } 321 | .bg-moffitt-gray { 322 | background-color: var(--moffitt-gray); 323 | } 324 | 325 | -------------------------------------------------------------------------------- /docs/css/build-your-universe.min.css: -------------------------------------------------------------------------------- 1 | .swan-nebula{background-image:url(../figures/swan-nebula-m17.jpg);background-size:100%;background-position:center middle}.carina-nebula{background-image:url(../figures/carina-nebula.jpg);background-size:100%;background-position:top left}.carina-nebula-soft{background-image:url(../figures/carina-nebula-soft.jpg);background-size:100%;background-position:top left}.world{position:absolute;border-radius:50%;background-size:115%;background-position:center center}.world-it{width:200px;height:200px;top:40%;left:20%;background-color:#14608d}.world-it::before{content:"IT";color:#fff;font-size:1.75em;position:absolute;top:-30px;left:-30px}.world-150{width:150px;height:150px}.world-it1{background-image:url(../figures/worlds/IT1.png);background-repeat:no-repeat}.world-it2{background-image:url(../figures/worlds/IT2.png);background-repeat:no-repeat;transform:scale(1.25)}.world-it3{background-image:url(../figures/worlds/IT3.png);background-repeat:no-repeat;transform:scale(1.33)}.rocket{background-image:url(../figures/worlds/rocket.png);background-repeat:no-repeat;position:absolute;width:155px;height:70px;background-size:contain;transform-origin:0 50px;top:670px;left:-93px;transform:rotate(185deg);z-index:100;offset-path:path('M 0 0 C 133.3333 -60.6667 84 -177 393 -117 A 1 1 0 0 0 441 -437');offset-anchor:center;offset-distance:100%}@media not all and (min-resolution:.001dpcm){@media{.rocket{top:150px;left:400px;-webkit-transform:rotate(13deg)}}}.rocket::before{content:"Health Informatics";position:absolute;color:#fff;font-size:1.25em;transform:rotate(-13deg);top:-90px;left:-40px;text-align:center}#slide-rocket .rocket::before{opacity:0;animation:fadeIn .5s linear forwards;animation-delay:3s}#slide-rocket .rocket{offset-distance:0}#slide-rocket .rocket{animation:rocket-fly-in 3s cubic-bezier(.41,.72,.61,.32) forwards}@keyframes rocket-fly-in{0%{offset-distance:0}100%{offset-distance:100%}}#slide-rocket .world-it::before{top:70px;left:-70px}.fullscreen-blur-fade-in{position:absolute;top:0;bottom:0;left:0;right:0;animation:blur-fade-in 5s linear}@keyframes blur-fade-in{0%{opacity:0;filter:blur(20px);transform:scale(1.25)}100%{opacity:1;filter:blur(0);transform:scale(1)}}.rotate-zoom-in{animation:rotate-zoom-in 1s ease-out}@keyframes rotate-zoom-in{0%{transform:scale(0) rotate(-180deg)}100%{transform:scale(1) rotate(+0deg)}}@keyframes moon-in-1{0%{opacity:0;transform:scale(0) rotate(-180deg)}100%{opacity:1;transform:scale(1) rotate(66deg)}}@keyframes moon-in-2{0%{opacity:0;transform:scale(0) rotate(-90deg)}100%{opacity:1;transform:scale(1) rotate(140deg)}}@keyframes moon-in-3{0%{opacity:0;transform:scale(0) rotate(0)}100%{opacity:1;transform:scale(1) rotate(210deg)}}@keyframes moon-in-4{0%{opacity:0;transform:scale(0) rotate(90deg)}100%{opacity:1;transform:scale(1) rotate(280deg)}}.moons{position:absolute;width:200px;height:200px;top:40%;left:20%}.moons-1{opacity:0;animation:moon-in-1 2s ease-out forwards;animation-delay:1.75s}.moons-1-static{opacity:1;transform:scale(1) rotate(66deg)}.moons-2{opacity:0;animation:moon-in-2 2s ease-out forwards;animation-delay:1.5s}.moons-2-static{opacity:1;transform:scale(1) rotate(140deg)}.moons-3{opacity:0;animation:moon-in-3 2s ease-out forwards;animation-delay:1.25s}.moons-3-static{opacity:1;transform:scale(1) rotate(210deg)}.moons-4{opacity:0;animation:moon-in-4 2s ease-out forwards;animation-delay:1s}.moons-4-static{opacity:1;transform:scale(1) rotate(280deg)}.moons-text{color:#fff;opacity:0;display:inline-block;position:absolute}.moons-1-static.moons-text-reveal .moons-text{opacity:1;transform:rotate(-66deg);top:-90px;left:-190px;width:200px;text-align:center;transition:all .25s ease-in}.moons-2-static.moons-text-reveal .moons-text{opacity:1;transform:rotate(220deg);top:-160px;left:-230px;width:200px;text-align:left;transition:all .25s ease-in}.moons-3-static.moons-text-reveal .moons-text{opacity:1;transform:rotate(150deg);top:-20px;left:-280px;width:220px;text-align:left;transition:all .25s ease-in}.moons-4-static.moons-text-reveal .moons-text{opacity:1;transform:rotate(80deg);top:-40px;left:-200px;width:220px;text-align:center;transition:all .25s ease-in}.moon-circle{position:absolute;width:50px;height:50px;border-radius:50%;top:-50px;left:-50px}.zoomToStepOne{animation:zoomToStepOne 3s linear forwards}.zoomStepOne{transform:scale(.9) translate(-50px,25px)}@keyframes zoomToStepOne{0%{transform:scale(1) translate(0,0)}20%{transform:scale(1) translate(0,0)}100%{transform:scale(.9) translate(-50px,25px)}}.zoomToStepTwo{animation:zoomToStepTwo 3s linear forwards}.zoomStepTwo{transform:scale(.8) translate(-65px,45px)}@keyframes zoomToStepTwo{0%{transform:scale(.9) translate(-50px,25px)}20%{transform:scale(.9) translate(-50px,25px)}100%{transform:scale(.8) translate(-65px,45px)}}.zoomToStepThree{animation:zoomToStepThree 3s linear forwards}.zoomStepThree{transform:scale(.7) translate(-130px,45px)}@keyframes zoomToStepThree{0%{transform:scale(.8) translate(-65px,45px)}20%{transform:scale(.8) translate(-65px,45px)}100%{transform:scale(.7) translate(-130px,45px)}}.world-label{position:absolute;font-size:1.5em}.world-label-entrance{opacity:0;animation:blur-fade-in .5s linear forwards;animation-delay:3s}.world-bi{background-color:#f0633c;background-image:url(../figures/worlds/BI.png);background-repeat:no-repeat;width:160px;height:160px;top:10%;left:60%}.world-bi-entrance{animation:world-bi-entrance 3s ease-in-out forwards}.world-label-bi{top:105px;right:205px;width:100px}#slide-world-bi .world-label-bi{opacity:0;animation:blur-fade-in .5s linear forwards;animation-delay:3s}@keyframes world-bi-entrance{0%{opacity:0;width:50px;height:50px;transform:translate(-345px,114px)}20%{opacity:1;width:50px;height:50px;transform:translate(-345px,114px)}100%{opacity:1;width:160px;height:160px;transform:translate(0,0)}}.world-cdsc{background-color:#14608d;background-image:url(../figures/worlds/CDSC.png);background-repeat:no-repeat;width:140px;height:140px;top:45%;right:275px}.world-label-cdsc{bottom:230px;width:190px;right:70px}#slide-world-cdsc .world-label-cdsc{opacity:0;animation:blur-fade-in .5s linear forwards;animation-delay:3s}#slide-world-cdsc .world-cdsc{opacity:1;animation:world-cdsc-entrance 3s cubic-bezier(.65,0,.35,1) forwards}@keyframes world-cdsc-entrance{0%{opacity:0;width:44px;height:44px;transform:translate(-431px,78px)}20%{opacity:1;width:44px;height:44px;transform:translate(-431px,78px)}100%{opacity:1;width:140px;height:140px;transform:translate(0,0)}}.world-dqs{background-color:#fdcea0;background-image:url(../figures/worlds/DQS.png);background-repeat:no-repeat;width:120px;height:120px;top:75%;left:57%}.world-label-dqs{bottom:58px;width:140px;right:245px}#slide-world-dqs .world-label-dqs{opacity:0;animation:blur-fade-in .5s linear forwards;animation-delay:3s}#slide-world-cdsc .world-label-cdsc{opacity:0;animation:blur-fade-in .5s linear forwards;animation-delay:3s}#slide-world-dqs .world-dqs{opacity:1;animation:world-dqs-entrance 3s cubic-bezier(.65,0,.35,1) forwards}@keyframes world-dqs-entrance{0%{opacity:0;width:40px;height:40px;transform:translate(-330px,8px)}20%{opacity:1;width:40px;height:40px;transform:translate(-330px,8px)}100%{opacity:1;width:120px;height:120px;transform:translate(0,0)}}:root{--world-easing-function:cubic-bezier(.65,.05,.36,1)}#slide-data-engineering .world-it1{animation:world-it1-exit 3s var(--world-easing-function) forwards}@keyframes world-it1-exit{0%{transform:rotate(0) scale(1)}50%{opacity:1;transform:rotate(-450deg) scale(1.33)}100%{opacity:0;transform:rotate(-2160deg) scale(1.25)}}#slide-data-engineering .world-it2{opacity:0;animation:world-it2-enter 3s var(--world-easing-function) forwards}@keyframes world-it2-enter{0%{opacity:0;transform:rotate(2160deg) scale(1.33)}50%{opacity:0;transform:rotate(2160deg) scale(1.33)}100%{opacity:1;transform:rotate(0) scale(1.25)}}#slide-data-engineering .world-it::before{top:70px;left:-70px;opacity:0;animation:fadeInLeft .5s linear forwards;animation-delay:3s}.world-it2::after{content:"Data Eng";color:#000;position:absolute;top:100px;left:120px;width:50px}#slide-data-engineering .world-it2::after{opacity:0;animation:fadeInUp .5s linear forwards;animation-delay:3s}#slide-data-engineering .world-it1::before{display:none}#slide-data-engineering .moons-4-static{animation:find-db-exit 1.5s linear forwards}@keyframes find-db-exit{0%{opacity:1;transform:scale(1) rotate(280deg)}100%{opacity:0;transform:scale(.15)}}#slide-app-dev .world-it2{animation:world-it2-exit 2s var(--world-easing-function) forwards}@keyframes world-it2-exit{0%{opacity:1;transform:scale(1.25) rotate(0)}100%{opacity:0;transform:scale(1.33) rotate(-720deg)}}.world-it3::after{content:"Data Eng";color:#000;position:absolute;top:100px;left:120px;width:50px}.world-it3::before{content:"App Dev";color:#000;font-size:1em;position:absolute;top:50px;left:20px;width:50px}#slide-app-dev .world-it3::after{opacity:0;animation:fadeInUp .5s linear forwards;animation-delay:2s}#slide-app-dev .world-it3::before{opacity:0;animation:fadeInUp .5s linear forwards;animation-delay:2s}#slide-app-dev .world-it2::before{display:none}#slide-app-dev .world-it3{animation:world-it3-enter 2s var(--world-easing-function) forwards}@keyframes world-it3-enter{0%{opacity:0;transform:scale(1.25) rotate(0)}100%{opacity:1;transform:scale(1.33) rotate(-720deg)}}.world-label-it{position:absolute;color:#fff;font-size:1.5em;top:369px;left:181px}#slide-app-dev .world-label-it{opacity:0;animation:fadeInLeft .5s linear forwards;animation-delay:2s}.vibrate-1{animation:vibrate-1 6s linear infinite both}@keyframes vibrate-1{0%{transform:rotate(30deg) translate(0)}20%{transform:rotate(28deg) translate(-4px,4px)}40%{transform:rotate(30deg) translate(-4px,-4px)}60%{transform:rotate(32deg) translate(4px,4px)}80%{transform:rotate(30deg) translate(4px,-4px)}100%{transform:rotate(30deg) translate(0)}}.space-person{background-image:url(../figures/worlds/space-suit.png);background-size:contain;background-repeat:no-repeat;position:absolute;width:120px;height:200px;top:50px;left:130px}.space-person::before{content:'CDO';color:#fff;font-size:1.25em;position:absolute;left:-70px;transform:rotate(-30deg);top:90px;animation:fadeIn .5s linear forwards} 2 | /*# sourceMappingURL=build-your-universe.min.css.map */ -------------------------------------------------------------------------------- /docs/libs/clipboard/clipboard.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * clipboard.js v2.0.6 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 o={},r.m=n=[function(t,e){t.exports=function(t){var e;if("SELECT"===t.nodeName)t.focus(),e=t.value;else if("INPUT"===t.nodeName||"TEXTAREA"===t.nodeName){var n=t.hasAttribute("readonly");n||t.setAttribute("readonly",""),t.select(),t.setSelectionRange(0,t.value.length),n||t.removeAttribute("readonly"),e=t.value}else{t.hasAttribute("contenteditable")&&t.focus();var o=window.getSelection(),r=document.createRange();r.selectNodeContents(t),o.removeAllRanges(),o.addRange(r),e=o.toString()}return e}},function(t,e){function n(){}n.prototype={on:function(t,e,n){var o=this.e||(this.e={});return(o[t]||(o[t]=[])).push({fn:e,ctx:n}),this},once:function(t,e,n){var o=this;function r(){o.off(t,r),e.apply(n,arguments)}return r._=e,this.on(t,r,n)},emit:function(t){for(var e=[].slice.call(arguments,1),n=((this.e||(this.e={}))[t]||[]).slice(),o=0,r=n.length;o { 17 | document.removeEventListener('DOMContentLoaded', completed) 18 | window.removeEventListener('load', completed) 19 | fn() 20 | } 21 | if (document.readyState !== 'loading') { 22 | setTimeout(fn) 23 | } else { 24 | document.addEventListener('DOMContentLoaded', completed) 25 | window.addEventListener('load', completed) 26 | } 27 | } 28 | 29 | ready(addShareAgainBar) 30 | 31 | function addShareAgainBar () { 32 | const icons = { 33 | left: '', 34 | right: '', 35 | fullScreen: '', 36 | share: '' 37 | } 38 | 39 | function getAuthor () { 40 | let author = document.head.querySelector('meta[name="author"]').content 41 | 42 | if (author.length > 0) { 43 | author = author + ' — ' 44 | } 45 | return author 46 | } 47 | 48 | function truncate (str, n, useWordBoundary = true) { 49 | if (str.length <= n) { return str } 50 | const subString = str.substr(0, n - 1) 51 | return (useWordBoundary 52 | ? subString.substr(0, subString.lastIndexOf(' ')) 53 | : subString) + '…' 54 | }; 55 | 56 | function getShortTitle () { 57 | return truncate(window.document.title, 50) 58 | } 59 | 60 | const navbar = document.createElement('nav') 61 | navbar.classList.add('shareagain-bar') 62 | 63 | navbar.innerHTML += `` 83 | 84 | const slidesContainer = document.querySelector(':not(html).remark-container') 85 | slidesContainer.appendChild(navbar) 86 | 87 | const btnSlidePrev = document.getElementById('shareagain-slide-prev') 88 | const btnSlideNext = document.getElementById('shareagain-slide-next') 89 | 90 | function toggleSlideButtons (slideIndex) { 91 | if (typeof slideIndex === 'undefined') { 92 | slideIndex = slideshow.getCurrentSlideIndex() 93 | } 94 | 95 | // Toggle next slide button 96 | if (slideIndex + 1 === slideshow.getSlideCount()) { 97 | btnSlideNext.classList.add('disabled') 98 | btnSlideNext.setAttribute('disabled', true) 99 | } else { 100 | btnSlideNext.classList.remove('disabled') 101 | btnSlideNext.removeAttribute('disabled') 102 | } 103 | 104 | // Toggle prev slide button 105 | if (slideIndex === 0) { 106 | btnSlidePrev.classList.add('disabled') 107 | btnSlidePrev.setAttribute('disabled', true) 108 | } else { 109 | btnSlidePrev.classList.remove('disabled') 110 | btnSlidePrev.removeAttribute('disabled') 111 | } 112 | } 113 | 114 | setTimeout(toggleSlideButtons, 100) 115 | 116 | // button click events 117 | btnSlidePrev.addEventListener('click', function (ev) { 118 | slideshow.gotoPreviousSlide() 119 | }) 120 | 121 | btnSlideNext.addEventListener('click', function (ev) { 122 | slideshow.gotoNextSlide() 123 | }) 124 | 125 | // button touch events (block remarkjs slide change on touch) 126 | btnSlidePrev.addEventListener('touchend', function (ev) { 127 | slideshow.gotoPreviousSlide() 128 | ev.stopPropagation() 129 | }) 130 | 131 | btnSlideNext.addEventListener('touchend', function (ev) { 132 | slideshow.gotoNextSlide() 133 | ev.stopPropagation() 134 | }); 135 | 136 | // show/hide share buttons 137 | ['click', 'touchend'].map(function (evType) { 138 | document.getElementById('shareagain-share').addEventListener(evType, function (ev) { 139 | toggleShareButtons() 140 | ev.preventDefault() 141 | ev.stopPropagation() 142 | }) 143 | }) 144 | 145 | navbar.addEventListener('touchend', function(ev) { 146 | ev.preventDefault() 147 | ev.stopPropagation() 148 | }) 149 | 150 | // copy slides link to clipboard 151 | const shareClip = new ClipboardJS('.shareon .link') 152 | shareClip.on('success', function (e) { 153 | const el = document.querySelector('.shareon .link') 154 | el.classList.add('success') 155 | setTimeout(() => el.classList.remove('success'), 2500) 156 | }) 157 | 158 | slideshow.on('afterShowSlide', function (slide) { 159 | toggleSlideButtons(slide.getSlideIndex()) 160 | }) 161 | 162 | function toggleAnimated ({ selector, show, inClass, outClass }) { 163 | const el = document.querySelector(selector) 164 | const isShown = el.classList.contains(inClass) 165 | if (typeof show === 'undefined') { 166 | show = !isShown 167 | } 168 | if (show === isShown) { 169 | return 170 | } 171 | if (show) { 172 | el.classList.remove(outClass) 173 | el.classList.add(inClass) 174 | } else { 175 | el.classList.remove(inClass) 176 | el.classList.add(outClass) 177 | } 178 | } 179 | 180 | let isCurrentlyFullScreen = false 181 | document.addEventListener('fullscreenchange', (event) => { 182 | if (document.fullscreenElement) { 183 | stopAutoHide(false) 184 | isCurrentlyFullScreen = true 185 | } else { 186 | isCurrentlyFullScreen = false 187 | startAutoHide() 188 | } 189 | }) 190 | 191 | function toggleNavBar (show) { 192 | // do nothing if currently fullscreen 193 | if (isCurrentlyFullScreen) return 194 | 195 | toggleAnimated({ selector: '.shareagain-bar ul', show, inClass: 'slideInUp', outClass: 'slideOutDown' }) 196 | if (!show) toggleShareButtons(false) 197 | } 198 | 199 | function toggleShareButtons (show) { 200 | toggleAnimated({ selector: '.shareagain-bar .shareon', show, inClass: 'slideInRight', outClass: 'slideOutRight' }) 201 | } 202 | 203 | // auto hide the share bar when focus is in the slides 204 | let mouseMoveTimer = null 205 | function hideNavDelayed (ev) { 206 | if (mouseMoveTimer) { 207 | clearTimeout(mouseMoveTimer) 208 | } 209 | toggleNavBar(true) 210 | mouseMoveTimer = setTimeout(function () { toggleNavBar(false) }, 2000) 211 | }; 212 | 213 | // toggle toggle full screen 214 | ['click', 'touchend'].map(function (evType) { 215 | document.getElementById('shareagain-fullscreen').addEventListener(evType, function (ev) { 216 | slideshow.toggleFullscreen() 217 | ev.stopPropagation() 218 | }) 219 | }) 220 | 221 | function startAutoHide () { 222 | hideNavDelayed() 223 | slidesContainer.addEventListener('mousemove', hideNavDelayed) 224 | } 225 | 226 | function stopAutoHide (showAfter = false) { 227 | slidesContainer.removeEventListener('mousemove', hideNavDelayed) 228 | clearTimeout(mouseMoveTimer) 229 | toggleNavBar(showAfter) 230 | } 231 | 232 | // auto hide turns on when the mouse comes into the slides area 233 | slidesContainer.addEventListener('mouseenter', function () { 234 | startAutoHide() 235 | }) 236 | 237 | // turn off auto hide when the mouse leaves the slides area 238 | slidesContainer.addEventListener('mouseleave', function () { 239 | stopAutoHide(true) 240 | }); 241 | 242 | // turn off auto hide if the mouse or focus is in the share bar 243 | ['focusin', 'mouseenter'].map(function (evType) { 244 | navbar.addEventListener(evType, function () { 245 | stopAutoHide(true) 246 | }) 247 | }); 248 | 249 | // and turn auto hide back on whne the mouse leaves the share bar 250 | // (if mouse exits out of the slides area, the slidesContainer should fire later) 251 | ['focusout', 'mouseleave'].map(function (evType) { 252 | navbar.addEventListener(evType, function () { 253 | startAutoHide() 254 | }) 255 | }) 256 | } 257 | })() 258 | -------------------------------------------------------------------------------- /docs/css/part-two.css: -------------------------------------------------------------------------------- 1 | .remark-code-line-highlighted { 2 | background-color: #f0f0f0; 3 | } 4 | .remark-code-line-highlighted { 5 | border-top: 2px solid #ddd; 6 | border-left: 2px solid #ddd; 7 | border-right: 2px solid #ddd; 8 | } 9 | .remark-code-line-highlighted + .remark-code-line-highlighted { 10 | border-top: none; 11 | border-bottom: none; 12 | } 13 | .remark-code-line-highlighted:last-of-type { 14 | border-bottom: 2px solid #ddd; 15 | } 16 | .remark-code-line-highlighted + div { 17 | border-top: 2px solid #aaa; 18 | } 19 | pre code.remark-code.hljs { 20 | background: white; 21 | } 22 | /*! ---- thought bubble ---- */ 23 | .bubble p { 24 | position: relative; 25 | width: 300px; 26 | text-align: center; 27 | line-height: 1.4em; 28 | margin: 40px auto; 29 | background-color: #fff; 30 | border: 8px solid #333; 31 | border-radius: 30px; 32 | font-size: 1.25em 33 | } 34 | .thought p { 35 | width: 500px; 36 | border-radius: 200px; 37 | padding: 30px 38 | } 39 | .bubble p:after, 40 | .bubble p:before { 41 | content: ' '; 42 | position: absolute; 43 | width: 0; 44 | height: 0 45 | } 46 | .speech p:before { 47 | left: 30px; 48 | bottom: -50px; 49 | border: 25px solid; 50 | border-color: #333 transparent transparent #333 51 | } 52 | .speech p:after { 53 | left: 38px; 54 | bottom: -30px; 55 | border: 15px solid; 56 | border-color: #fff transparent transparent #fff 57 | } 58 | .thought p:after, 59 | .thought p:before { 60 | right: 10px; 61 | bottom: -30px; 62 | width: 40px; 63 | height: 40px; 64 | background-color: #fff; 65 | border: 8px solid #666; 66 | -webkit-border-radius: 28px; 67 | -moz-border-radius: 28px; 68 | border-radius: 28px 69 | } 70 | .thought p:after { 71 | width: 20px; 72 | height: 20px; 73 | right: 5px; 74 | bottom: -40px; 75 | border-color: #999; 76 | -webkit-border-radius: 18px; 77 | -moz-border-radius: 18px; 78 | border-radius: 18px 79 | } 80 | /*! https://github.com/miniMAC/magic/blob/master/dist/magic.css */ 81 | .remark-visible .vanishIn { 82 | -webkit-animation-name: vanishIn; 83 | animation-name: vanishIn; 84 | -webkit-animation-duration: 2s; 85 | animation-duration: 2s; 86 | -webkit-animation-fill-mode: both; 87 | animation-fill-mode: both 88 | } 89 | @-webkit-keyframes vanishIn { 90 | 0% { 91 | opacity: 0; 92 | transform-origin: 50% 50%; 93 | transform: scale(2,2); 94 | -webkit-filter: blur(90px); 95 | filter: blur(90px) 96 | } 97 | 100% { 98 | opacity: 1; 99 | transform-origin: 50% 50%; 100 | transform: scale(1,1); 101 | -webkit-filter: blur(0); 102 | filter: blur(0) 103 | } 104 | } 105 | @keyframes vanishIn { 106 | 0% { 107 | opacity: 0; 108 | transform-origin: 50% 50%; 109 | transform: scale(2,2); 110 | -webkit-filter: blur(90px); 111 | filter: blur(90px) 112 | } 113 | 100% { 114 | opacity: 1; 115 | transform-origin: 50% 50%; 116 | transform: scale(1,1); 117 | -webkit-filter: blur(0); 118 | filter: blur(0) 119 | } 120 | } 121 | /*! ---- typing animation ---- */ 122 | .remark-visible .css-typing p { 123 | opacity: 0; 124 | border-right: .15em solid orange; 125 | white-space: nowrap; 126 | overflow: hidden; 127 | -webkit-animation-fill-mode: forwards; 128 | animation-fill-mode: forwards 129 | } 130 | .remark-visible .css-typing p:nth-child(1) { 131 | width: 870px; 132 | -webkit-animation: type 3s steps(70,end); 133 | animation: type 3s steps(70,end); 134 | -webkit-animation-fill-mode: forwards; 135 | animation-fill-mode: forwards 136 | } 137 | .remark-visible .css-typing p:nth-child(2) { 138 | width: 390px; 139 | -webkit-animation: type 2s steps(31,end); 140 | animation: type 2s steps(31,end); 141 | -webkit-animation-fill-mode: forwards; 142 | animation-fill-mode: forwards; 143 | -webkit-animation-delay: 4s; 144 | animation-delay: 4s 145 | } 146 | .remark-visible .css-typing p:nth-child(3) { 147 | width: 110px; 148 | -webkit-animation: type .5s steps(7,end); 149 | animation: type .5s steps(7,end); 150 | -webkit-animation-fill-mode: forwards; 151 | animation-fill-mode: forwards; 152 | -webkit-animation-delay: 7s; 153 | animation-delay: 7s 154 | } 155 | .remark-visible .css-typing p:nth-child(4) { 156 | width: 100px; 157 | -webkit-animation: type .5s steps(7,end); 158 | animation: type .5s steps(7,end); 159 | -webkit-animation-fill-mode: forwards; 160 | animation-fill-mode: forwards; 161 | -webkit-animation-delay: 7.5s; 162 | animation-delay: 7.5s 163 | } 164 | @keyframes type { 165 | 0% { 166 | width: 0 167 | } 168 | 1% { 169 | opacity: 1 170 | } 171 | 99.9% { 172 | border-right: .15em solid orange 173 | } 174 | 100% { 175 | opacity: 1; 176 | border: none 177 | } 178 | } 179 | @-webkit-keyframes type { 180 | 0% { 181 | width: 0 182 | } 183 | 1% { 184 | opacity: 1 185 | } 186 | 99.9% { 187 | border-right: .15em solid orange 188 | } 189 | 100% { 190 | opacity: 1; 191 | border: none 192 | } 193 | } 194 | @keyframes blink { 195 | 50% { 196 | border-color: transparent 197 | } 198 | } 199 | @-webkit-keyframes blink { 200 | 50% { 201 | border-color: tranparent 202 | } 203 | } 204 | /*! ---- email attachment ---- */ 205 | #email-attachment:hover { 206 | color: #eb1455 207 | } 208 | .zoomForward { 209 | animation: zoomForward 1s cubic-bezier(.45,-.12,.51,.71); 210 | transform-origin: center 211 | } 212 | @keyframes zoomForward { 213 | 0% { 214 | transform: scale(1) 215 | } 216 | 100% { 217 | opacity: 0; 218 | transform: scale(25) 219 | } 220 | } 221 | @-webkit-keyframes zoomForward { 222 | 0% { 223 | transform: scale(1) 224 | } 225 | 100% { 226 | opacity: 0; 227 | transform: scale(25) 228 | } 229 | } 230 | /*! ---- sql code two column ---- */ 231 | .code-two-column .remark-code { 232 | font-size: .33em; 233 | -webkit-column-count: 2; 234 | -moz-column-count: 2; 235 | column-count: 2; 236 | -webkit-column-gap: 20px; 237 | -moz-column-gap: 20px; 238 | column-gap: 20px 239 | } 240 | .code-two-column pre { 241 | margin-top: 0; 242 | padding-left: 1em; 243 | border-top-left-radius: 8px; 244 | border-top-right-radius: 8px 245 | } 246 | .code-window-title-bar { 247 | border-top-left-radius: 8px; 248 | border-top-right-radius: 8px; 249 | padding-top: 10px 250 | } 251 | .code-window-circle { 252 | width: 17px; 253 | height: 18px; 254 | margin-right: 6px 255 | } 256 | .code-window-circles { 257 | position: absolute; 258 | left: 12px; 259 | top: 11px 260 | } 261 | #sql-magnifier { 262 | position: absolute; 263 | width: 90%; 264 | font-size: 1em; 265 | top: 20px; 266 | } 267 | #sql-magnifier .hljs { 268 | background-color: white; 269 | height: 60px; 270 | } 271 | .code-two-column pre { 272 | cursor: url(../figures/magnifying-glass.cur), zoom-in; 273 | } 274 | /*! ---- ms word ---- */ 275 | .word-doc { 276 | background-image: url(../figures/word-doc.png); 277 | background-size: cover; 278 | overflow: hidden; 279 | height: 100%; 280 | padding-top: 5em 281 | } 282 | .word-doc pre { 283 | max-height: 66%; 284 | overflow: hidden; 285 | position: absolute; 286 | width: 90% 287 | } 288 | .word-doc pre code.sql { 289 | background: #fff; 290 | font-family: sans-serif; 291 | max-width: 100%; 292 | overflow-x: hidden 293 | } 294 | .word-doc pre code.sql .remark-code-line:hover { 295 | font-weight: normal; 296 | } 297 | 298 | .word-doc pre code.sql .remark-code-line:hover:before { 299 | content: none; 300 | color: #6d7e8a; 301 | position: absolute; 302 | transform: none; 303 | animation: none; 304 | } 305 | #sql-font-size-down { 306 | background: 0 0; 307 | width: 35px; 308 | height: 30px; 309 | position: absolute; 310 | top: 77px; 311 | left: 404px; 312 | border: none; 313 | cursor: pointer 314 | } 315 | #sql-font-size-up { 316 | background: 0 0; 317 | width: 35px; 318 | height: 30px; 319 | position: absolute; 320 | top: 77px; 321 | left: 365px; 322 | border: none; 323 | cursor: pointer 324 | } 325 | #sql-font-comic-sans { 326 | background: 0 0; 327 | text-align: center; 328 | width: 125px; 329 | height: 30px; 330 | padding: 0; 331 | position: absolute; 332 | top: 77px; 333 | left: 138px; 334 | border: none; 335 | cursor: pointer; 336 | font-size: 21px 337 | } 338 | /*! magic.css https://github.com/miniMAC/magic/blob/master/dist/magic.css */ 339 | .remark-fading .boingOutDown { 340 | -webkit-animation-name: boingOutDown; 341 | animation-name: boingOutDown 342 | } 343 | @-webkit-keyframes boingOutDown { 344 | 0% { 345 | opacity: 1; 346 | transform-origin: 100% 100%; 347 | transform: perspective(800px) rotateX(0) rotateY(0) 348 | } 349 | 20% { 350 | opacity: 1; 351 | transform-origin: 100% 100%; 352 | transform: perspective(800px) rotateX(0) rotateY(10deg) 353 | } 354 | 30% { 355 | opacity: 1; 356 | transform-origin: 0 100%; 357 | transform: perspective(800px) rotateX(0) rotateY(0) 358 | } 359 | 40% { 360 | opacity: 1; 361 | transform-origin: 0 100%; 362 | transform: perspective(800px) rotateX(10deg) rotateY(10deg) 363 | } 364 | 100% { 365 | opacity: 0; 366 | transform-origin: 100% 100%; 367 | transform: perspective(800px) rotateX(90deg) rotateY(0) 368 | } 369 | } 370 | @keyframes boingOutDown { 371 | 0% { 372 | opacity: 1; 373 | transform-origin: 100% 100%; 374 | transform: perspective(800px) rotateX(0) rotateY(0) 375 | } 376 | 20% { 377 | opacity: 1; 378 | transform-origin: 100% 100%; 379 | transform: perspective(800px) rotateX(0) rotateY(10deg) 380 | } 381 | 30% { 382 | opacity: 1; 383 | transform-origin: 0 100%; 384 | transform: perspective(800px) rotateX(0) rotateY(0) 385 | } 386 | 40% { 387 | opacity: 1; 388 | transform-origin: 0 100%; 389 | transform: perspective(800px) rotateX(10deg) rotateY(10deg) 390 | } 391 | 100% { 392 | opacity: 0; 393 | transform-origin: 100% 100%; 394 | transform: perspective(800px) rotateX(90deg) rotateY(0) 395 | } 396 | } 397 | /*! ---- css-focus-sliding-bg ---- */ 398 | .focus-ul { 399 | background-image: linear-gradient(90deg,#eb1455,#eb1455); 400 | background-size: 0 100%; 401 | background-repeat: no-repeat; 402 | transition: background-size .25s ease-in,color .5s ease-in 403 | } 404 | .focus-ul:focus { 405 | outline: 0; 406 | color: #fff; 407 | background-size: 100% 100% 408 | } 409 | del { 410 | text-decoration-color: #eb1455; 411 | text-decoration-thickness: 10px 412 | } 413 | /*! ---- Code Example ---- */ 414 | .code-example { 415 | position: absolute; 416 | top: 100px 417 | } 418 | .leader-line { 419 | z-index: 100 420 | } 421 | [class^=leader-start] { 422 | color: var(--orange); 423 | font-size: 1.1em; 424 | width: 450px; 425 | margin: 0; 426 | position: absolute; 427 | text-align: center 428 | } 429 | .leader-start-1 { 430 | top: 10%; 431 | right: 30% 432 | } 433 | .leader-start-2 { 434 | top: 15%; 435 | right: 20% 436 | } 437 | .expand-in { 438 | max-height: 0; 439 | overflow: hidden 440 | } 441 | .remark-fading .expand-in { 442 | max-height: 600px 443 | } 444 | .remark-visible .expand-in { 445 | animation: expand-in 1.5s linear; 446 | animation-fill-mode: forwards 447 | } 448 | @keyframes expand-in { 449 | 0% { 450 | max-height: 0 451 | } 452 | 100% { 453 | max-height: 600px 454 | } 455 | } 456 | .leader-start-3 { 457 | top: 20%; 458 | right: 10% 459 | } 460 | .reveal-focus { 461 | position: relative; 462 | cursor: pointer 463 | } 464 | .reveal-focus:focus { 465 | outline: 0 466 | } 467 | .reveal-focus .reveal-shown { 468 | transition: opacity 150ms linear 469 | } 470 | .reveal-focus .reveal-other { 471 | position: absolute; 472 | transition: opacity 150ms linear; 473 | width: 1000px; 474 | left: 0 475 | } 476 | .reveal-focus:not(:focus) .reveal-other { 477 | opacity: 0 478 | } 479 | .reveal-focus:focus .reveal-shown { 480 | opacity: 0 481 | } 482 | .leader-start-4 { 483 | top: 45%; 484 | right: 0 485 | } 486 | .leader-start-5 { 487 | bottom: 15%; 488 | right: 5% 489 | } -------------------------------------------------------------------------------- /docs/libs/shareon/shareon.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * shareon v1.4.1 by Nikita Karamov 3 | * https://shareon.js.org 4 | */ 5 | .shareon{font-size:0!important}.shareon>*{display:inline-block;position:relative;height:24px;min-width:16px;margin:3px;padding:6px 10px;background-color:#ccc;border-radius:3.33333px;border:none;box-sizing:content-box;color:#fff;line-height:1.5;transition:opacity .3s ease;vertical-align:middle}.shareon>:hover{border:none;cursor:pointer;opacity:.7}.shareon>:not(:empty){font-size:16px;text-decoration:none}.shareon>:not(:empty):before{position:relative;height:100%;width:28px;top:0;left:0;background-position:0 50%}.shareon>:before{display:inline-block;position:absolute;height:20px;width:20px;top:8px;left:8px;background-repeat:no-repeat;background-size:20px 20px;content:"";vertical-align:bottom}.shareon>.facebook{background-color:#1877f2}.shareon>.facebook:before{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg fill='%23fff' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M23.998 12c0-6.628-5.372-12-11.999-12C5.372 0 0 5.372 0 12c0 5.988 4.388 10.952 10.124 11.852v-8.384H7.078v-3.469h3.046V9.356c0-3.008 1.792-4.669 4.532-4.669 1.313 0 2.686.234 2.686.234v2.953H15.83c-1.49 0-1.955.925-1.955 1.874V12h3.328l-.532 3.469h-2.796v8.384c5.736-.9 10.124-5.864 10.124-11.853z'/%3E%3C/svg%3E")}.shareon>.linkedin{background-color:#2867b2}.shareon>.linkedin:before{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg fill='%23fff' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M23.722 23.72h-4.91v-7.692c0-1.834-.038-4.194-2.559-4.194-2.56 0-2.95 1.995-2.95 4.06v7.827H8.394V7.902h4.716v2.157h.063c.659-1.244 2.261-2.556 4.655-2.556 4.974 0 5.894 3.274 5.894 7.535zM.388 7.902h4.923v15.819H.388zM2.85 5.738A2.849 2.849 0 010 2.886a2.851 2.851 0 112.85 2.852z'/%3E%3C/svg%3E")}.shareon>.linkedin:not(:empty):before{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg fill='%23fff' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433a2.062 2.062 0 01-2.063-2.065 2.064 2.064 0 112.063 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z'/%3E%3C/svg%3E")}.shareon>.messenger{background-color:#09f}.shareon>.messenger:before{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg fill='%23fff' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M0 11.64C0 4.95 5.24 0 12 0s12 4.95 12 11.64-5.24 11.64-12 11.64c-1.21 0-2.38-.16-3.47-.46a.96.96 0 00-.64.05L5.5 23.92a.96.96 0 01-1.35-.85l-.07-2.14a.97.97 0 00-.32-.68A11.39 11.39 0 010 11.64zm8.32-2.19l-3.52 5.6c-.35.53.32 1.14.82.75l3.79-2.87c.26-.2.6-.2.87 0l2.8 2.1c.84.63 2.04.4 2.6-.48l3.52-5.6c.35-.53-.32-1.13-.82-.75l-3.79 2.87c-.25.2-.6.2-.86 0l-2.8-2.1a1.8 1.8 0 00-2.61.48z'/%3E%3C/svg%3E")}.shareon>.odnoklassniki{background-color:#ee8208}.shareon>.odnoklassniki:before{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg fill='%23fff' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M14.505 17.44a11.599 11.599 0 003.6-1.49 1.816 1.816 0 00-1.935-3.073 7.866 7.866 0 01-8.34 0 1.814 1.814 0 00-2.5.565c0 .002 0 .004-.002.005a1.812 1.812 0 00.567 2.5l.002.002c1.105.695 2.322 1.2 3.596 1.488l-3.465 3.465A1.796 1.796 0 006 23.439l.03.03c.344.354.81.53 1.274.53.465 0 .93-.176 1.275-.53L12 20.065l3.404 3.406a1.815 1.815 0 002.566-2.565l-3.465-3.466zM12 12.388a6.202 6.202 0 006.195-6.193C18.195 2.78 15.415 0 12 0S5.805 2.78 5.805 6.197A6.2 6.2 0 0012 12.389zm0-8.757a2.566 2.566 0 010 5.13 2.569 2.569 0 01-2.565-2.564A2.57 2.57 0 0112 3.63z'/%3E%3C/svg%3E")}.shareon>.pinterest{background-color:#ee0023}.shareon>.pinterest:before{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg fill='%23fff' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12.017 0C5.396 0 .029 5.367.029 11.987c0 5.079 3.158 9.417 7.618 11.162-.105-.949-.199-2.403.041-3.439.219-.937 1.406-5.957 1.406-5.957s-.359-.72-.359-1.781c0-1.663.967-2.911 2.168-2.911 1.024 0 1.518.769 1.518 1.688 0 1.029-.653 2.567-.992 3.992-.285 1.193.6 2.165 1.775 2.165 2.128 0 3.768-2.245 3.768-5.487 0-2.861-2.063-4.869-5.008-4.869-3.41 0-5.409 2.562-5.409 5.199 0 1.033.394 2.143.889 2.741.099.12.112.225.085.345-.09.375-.293 1.199-.334 1.363-.053.225-.172.271-.401.165-1.495-.69-2.433-2.878-2.433-4.646 0-3.776 2.748-7.252 7.92-7.252 4.158 0 7.392 2.967 7.392 6.923 0 4.135-2.607 7.462-6.233 7.462-1.214 0-2.354-.629-2.758-1.379l-.749 2.848c-.269 1.045-1.004 2.352-1.498 3.146 1.123.345 2.306.535 3.55.535 6.607 0 11.985-5.365 11.985-11.987C23.97 5.39 18.592.026 11.985.026z'/%3E%3C/svg%3E")}.shareon>.pocket{background-color:#ef4154}.shareon>.pocket:before{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg fill='%23fff' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M18.813 10.259l-5.646 5.419a1.649 1.649 0 01-2.282 0l-5.646-5.419a1.645 1.645 0 012.276-2.376l4.511 4.322 4.517-4.322a1.643 1.643 0 012.326.049 1.64 1.64 0 01-.045 2.326zm5.083-7.546a2.163 2.163 0 00-2.041-1.436H2.179c-.9 0-1.717.564-2.037 1.405-.094.25-.142.511-.142.774v7.245l.084 1.441c.348 3.277 2.047 6.142 4.682 8.139.045.036.094.07.143.105l.03.023a11.899 11.899 0 004.694 2.072c.786.158 1.591.24 2.389.24.739 0 1.481-.067 2.209-.204.088-.029.176-.045.264-.06.023 0 .049-.015.074-.029a12.002 12.002 0 004.508-2.025l.029-.031.135-.105c2.627-1.995 4.324-4.862 4.686-8.148L24 10.678V3.445c0-.251-.031-.5-.121-.742z'/%3E%3C/svg%3E")}.shareon>.reddit{background-color:#ff4500}.shareon>.reddit:before{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg fill='%23fff' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M19.53 1.148a1.83 1.83 0 00-1.667 1.054l-4.372-.928a.522.522 0 00-.358.063.515.515 0 00-.21.297L11.59 7.925c-2.807.086-5.32.909-7.137 2.237a2.668 2.668 0 00-1.815-.737A2.64 2.64 0 000 12.064a2.634 2.634 0 001.563 2.407 4.95 4.95 0 00-.065.803c0 4.053 4.71 7.326 10.537 7.326s10.537-3.273 10.537-7.326a4.548 4.548 0 00-.063-.782 2.732 2.732 0 001.519-2.428 2.64 2.64 0 00-2.639-2.64 2.53 2.53 0 00-1.816.74c-1.796-1.288-4.287-2.134-7.031-2.239l1.204-5.637 3.906.823a1.888 1.888 0 001.878 1.777c1.024 0 1.87-.837 1.88-1.861a1.884 1.884 0 00-1.88-1.88zM7.907 18.066c-.13 0-.254.05-.347.141a.498.498 0 000 .697c1.266 1.267 3.736 1.373 4.454 1.373s3.167-.084 4.454-1.373a.546.546 0 00.044-.697.5.5 0 00-.698 0c-.823.802-2.533 1.099-3.779 1.099s-2.977-.295-3.779-1.099a.49.49 0 00-.349-.142zm-1.932-4.122c0-1.035.844-1.88 1.88-1.88 1.034 0 1.878.843 1.878 1.879S8.89 15.82 7.856 15.82a1.882 1.882 0 01-1.88-1.877zm10.155-1.88c1.035 0 1.88.845 1.88 1.879 0 1.035-.844 1.878-1.879 1.878s-1.879-.843-1.879-1.877c0-1.037.844-1.88 1.878-1.88z' fill-rule='evenodd'/%3E%3C/svg%3E")}.shareon>.telegram{background-color:#179cde}.shareon>.telegram:before{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg fill='%23fff' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M1.517 11.31c-.962.382-1.466.756-1.512 1.122-.088.702.924.921 2.196 1.335 1.037.337 2.433.731 3.158.747.658.014 1.393-.257 2.204-.814 5.533-3.735 8.39-5.622 8.57-5.663.126-.029.301-.065.42.04.119.106.107.306.095.36-.101.429-5.3 5.156-5.599 5.467-1.143 1.187-2.443 1.913-.437 3.235 1.735 1.144 2.746 1.873 4.534 3.045 1.142.75 2.039 1.637 3.218 1.529.543-.05 1.104-.56 1.389-2.083.673-3.598 1.996-11.392 2.302-14.604a3.585 3.585 0 00-.034-.8c-.027-.158-.084-.383-.29-.55-.243-.197-.619-.24-.787-.236-.764.013-1.936.42-7.579 2.767C11.39 7.03 7.44 8.73 1.517 11.31z'/%3E%3C/svg%3E")}.shareon>.twitter{background-color:#1da1f2}.shareon>.twitter:before{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg fill='%23fff' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M23.954 4.569a10 10 0 01-2.825.775 4.958 4.958 0 002.163-2.723c-.951.555-2.005.959-3.127 1.184a4.92 4.92 0 00-8.384 4.482C7.691 8.094 4.066 6.13 1.64 3.161a4.822 4.822 0 00-.666 2.475c0 1.71.87 3.213 2.188 4.096a4.904 4.904 0 01-2.228-.616v.061a4.923 4.923 0 003.946 4.827 4.996 4.996 0 01-2.212.085 4.937 4.937 0 004.604 3.417 9.868 9.868 0 01-6.102 2.105c-.39 0-.779-.023-1.17-.067a13.995 13.995 0 007.557 2.209c9.054 0 13.999-7.496 13.999-13.986 0-.209 0-.42-.015-.63a9.936 9.936 0 002.46-2.548l-.047-.02z'/%3E%3C/svg%3E")}.shareon>.viber{background-color:#7360f2}.shareon>.viber:before{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg fill='%23fff' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M11.398.002C9.473.028 5.331.344 3.014 2.467 1.294 4.177.693 6.698.623 9.82c-.06 3.11-.13 8.95 5.5 10.541v2.42s-.038.97.602 1.17c.79.25 1.24-.499 1.99-1.299l1.4-1.58c3.85.32 6.8-.419 7.14-.529.78-.25 5.181-.811 5.901-6.652.74-6.031-.36-9.831-2.34-11.551l-.01-.002c-.6-.55-3-2.3-8.37-2.32 0 0-.396-.025-1.038-.016zm.067 1.697c.545-.003.88.02.88.02 4.54.01 6.711 1.38 7.221 1.84 1.67 1.429 2.528 4.856 1.9 9.892-.6 4.88-4.17 5.19-4.83 5.4-.28.09-2.88.73-6.152.52 0 0-2.439 2.941-3.199 3.701-.12.13-.26.17-.35.15-.13-.03-.17-.19-.16-.41l.02-4.019c-4.771-1.32-4.491-6.302-4.441-8.902.06-2.6.55-4.732 2-6.172 1.957-1.77 5.475-2.01 7.11-2.02zm.36 2.6a.299.299 0 00-.3.299.3.3 0 00.3.3 5.631 5.631 0 014.03 1.59c1.09 1.06 1.621 2.48 1.641 4.34a.3.3 0 00.3.3v-.009a.3.3 0 00.3-.3 6.451 6.451 0 00-1.81-4.76c-1.19-1.16-2.692-1.76-4.462-1.76zm-3.954.69a.955.955 0 00-.615.12h-.012c-.41.24-.788.54-1.148.94-.27.32-.421.639-.461.949a1.24 1.24 0 00.05.541l.02.01a13.722 13.722 0 001.2 2.6 15.383 15.383 0 002.32 3.171l.03.04.04.03.03.03.03.03a15.603 15.603 0 003.18 2.33c1.32.72 2.122 1.06 2.602 1.2v.01c.14.04.268.06.398.06a1.84 1.84 0 001.102-.472c.39-.35.7-.738.93-1.148v-.01c.23-.43.15-.841-.18-1.121a13.632 13.632 0 00-2.15-1.54c-.51-.28-1.03-.11-1.24.17l-.45.569c-.23.28-.65.24-.65.24l-.012.01c-3.12-.8-3.95-3.959-3.95-3.959s-.04-.43.25-.65l.56-.45c.27-.22.46-.74.17-1.25a13.522 13.522 0 00-1.54-2.15.843.843 0 00-.504-.3zm4.473.89a.3.3 0 00.002.6 3.78 3.78 0 012.65 1.15 3.5 3.5 0 01.9 2.57.3.3 0 00.3.299l.01.012a.3.3 0 00.3-.301c.03-1.19-.34-2.19-1.07-2.99-.73-.8-1.75-1.25-3.05-1.34a.3.3 0 00-.042 0zm.49 1.619a.305.305 0 00-.018.611c.99.05 1.47.55 1.53 1.58a.3.3 0 00.3.29h.01a.3.3 0 00.29-.32c-.07-1.34-.8-2.091-2.1-2.161a.305.305 0 00-.012 0z'/%3E%3C/svg%3E")}.shareon>.vkontakte{background-color:#4680c2}.shareon>.vkontakte:before{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg fill='%23fff' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M23.058 19.504h-2.616c-.99 0-1.297-.787-3.076-2.59-1.55-1.501-2.236-1.704-2.617-1.704-.534 0-.687.153-.687.89v2.363c0 .636-.202 1.017-1.88 1.017-2.77 0-5.845-1.677-8.004-4.804C.925 10.103.034 6.672.034 5.961c0-.381.153-.737.89-.737H3.54c.66 0 .915.305 1.17 1.016 1.295 3.736 3.456 7.014 4.345 7.014.33 0 .483-.153.483-.99V8.399c-.102-1.78-1.042-1.931-1.042-2.566 0-.306.255-.61.66-.61h4.117c.56 0 .762.304.762.964v5.211c0 .558.255.762.407.762.33 0 .61-.204 1.22-.813 1.88-2.11 3.227-5.362 3.227-5.362.178-.381.483-.737 1.145-.737h2.616c.788 0 .966.405.788.965-.33 1.526-3.532 6.048-3.532 6.048-.28.457-.381.66 0 1.17.28.381 1.194 1.169 1.805 1.88 1.118 1.27 1.98 2.338 2.21 3.076.255.735-.128 1.116-.864 1.116z'/%3E%3C/svg%3E")}.shareon>.whatsapp{background-color:#25d366}.shareon>.whatsapp:before{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg fill='%23fff' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51a12.8 12.8 0 00-.57-.01c-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0012.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 005.683 1.448h.005c6.554 0 11.89-5.335 11.893-11.893a11.821 11.821 0 00-3.48-8.413z'/%3E%3C/svg%3E")} -------------------------------------------------------------------------------- /docs/css/build-your-universe.css: -------------------------------------------------------------------------------- 1 | .swan-nebula { 2 | background-image: url(../figures/swan-nebula-m17.jpg); 3 | background-size: 100%; 4 | background-position: center middle 5 | } 6 | .carina-nebula { 7 | background-image: url(../figures/carina-nebula.jpg); 8 | background-size: 100%; 9 | background-position: top left 10 | } 11 | .carina-nebula-soft { 12 | background-image: url(../figures/carina-nebula-soft.jpg); 13 | background-size: 100%; 14 | background-position: top left 15 | } 16 | .world { 17 | position: absolute; 18 | border-radius: 50%; 19 | background-size: 115%; 20 | background-position: center center 21 | } 22 | .world-it { 23 | width: 200px; 24 | height: 200px; 25 | top: 40%; 26 | left: 20%; 27 | background-color: #14608d 28 | } 29 | .world-it::before { 30 | content: "IT"; 31 | color: #fff; 32 | font-size: 1.75em; 33 | position: absolute; 34 | top: -30px; 35 | left: -30px 36 | } 37 | .world-150 { 38 | width: 150px; 39 | height: 150px 40 | } 41 | .world-it1 { 42 | background-image: url(../figures/worlds/IT1.png); 43 | background-repeat: no-repeat 44 | } 45 | .world-it2 { 46 | background-image: url(../figures/worlds/IT2.png); 47 | background-repeat: no-repeat; 48 | transform: scale(1.25) 49 | } 50 | .world-it3 { 51 | background-image: url(../figures/worlds/IT3.png); 52 | background-repeat: no-repeat; 53 | transform: scale(1.33) 54 | } 55 | .rocket { 56 | background-image: url(../figures/worlds/rocket.png); 57 | background-repeat: no-repeat; 58 | position: absolute; 59 | width: 155px; 60 | height: 70px; 61 | background-size: contain; 62 | transform-origin: 0 50px; 63 | top: 670px; 64 | left: -93px; 65 | transform: rotate(185deg); 66 | z-index: 100; 67 | offset-path: path('M 0 0 C 133.3333 -60.6667 84 -177 393 -117 A 1 1 0 0 0 441 -437'); 68 | offset-anchor: center; 69 | offset-distance: 100% 70 | } 71 | @media not all and (min-resolution:.001dpcm) { @media { 72 | .rocket { 73 | top: 150px; 74 | left: 400px; 75 | -webkit-transform: rotate(13deg) 76 | } 77 | }} 78 | .rocket::before { 79 | content: "Health Informatics"; 80 | position: absolute; 81 | color: #fff; 82 | font-size: 1.25em; 83 | transform: rotate(-13deg); 84 | top: -90px; 85 | left: -40px; 86 | text-align: center 87 | } 88 | #slide-rocket .rocket::before { 89 | opacity: 0; 90 | animation: fadeIn .5s linear forwards; 91 | animation-delay: 3s 92 | } 93 | #slide-rocket .rocket { 94 | offset-distance: 0 95 | } 96 | #slide-rocket .rocket { 97 | animation: rocket-fly-in 3s cubic-bezier(.41,.72,.61,.32) forwards 98 | } 99 | @keyframes rocket-fly-in { 100 | 0% { 101 | offset-distance: 0 102 | } 103 | 100% { 104 | offset-distance: 100% 105 | } 106 | } 107 | #slide-rocket .world-it::before { 108 | top: 70px; 109 | left: -70px 110 | } 111 | .fullscreen-blur-fade-in { 112 | position: absolute; 113 | top: 0; 114 | bottom: 0; 115 | left: 0; 116 | right: 0; 117 | animation: blur-fade-in 5s linear 118 | } 119 | @keyframes blur-fade-in { 120 | 0% { 121 | opacity: 0; 122 | filter: blur(20px); 123 | transform: scale(1.25) 124 | } 125 | 100% { 126 | opacity: 1; 127 | filter: blur(0); 128 | transform: scale(1) 129 | } 130 | } 131 | .rotate-zoom-in { 132 | animation: rotate-zoom-in 1s ease-out 133 | } 134 | @keyframes rotate-zoom-in { 135 | 0% { 136 | transform: scale(0) rotate(-180deg) 137 | } 138 | 100% { 139 | transform: scale(1) rotate(+0deg) 140 | } 141 | } 142 | @keyframes moon-in-1 { 143 | 0% { 144 | opacity: 0; 145 | transform: scale(0) rotate(-180deg) 146 | } 147 | 100% { 148 | opacity: 1; 149 | transform: scale(1) rotate(66deg) 150 | } 151 | } 152 | @keyframes moon-in-2 { 153 | 0% { 154 | opacity: 0; 155 | transform: scale(0) rotate(-90deg) 156 | } 157 | 100% { 158 | opacity: 1; 159 | transform: scale(1) rotate(140deg) 160 | } 161 | } 162 | @keyframes moon-in-3 { 163 | 0% { 164 | opacity: 0; 165 | transform: scale(0) rotate(0) 166 | } 167 | 100% { 168 | opacity: 1; 169 | transform: scale(1) rotate(210deg) 170 | } 171 | } 172 | @keyframes moon-in-4 { 173 | 0% { 174 | opacity: 0; 175 | transform: scale(0) rotate(90deg) 176 | } 177 | 100% { 178 | opacity: 1; 179 | transform: scale(1) rotate(280deg) 180 | } 181 | } 182 | .moons { 183 | position: absolute; 184 | width: 200px; 185 | height: 200px; 186 | top: 40%; 187 | left: 20% 188 | } 189 | .moons-1 { 190 | opacity: 0; 191 | animation: moon-in-1 2s ease-out forwards; 192 | animation-delay: 1.75s 193 | } 194 | .moons-1-static { 195 | opacity: 1; 196 | transform: scale(1) rotate(66deg) 197 | } 198 | .moons-2 { 199 | opacity: 0; 200 | animation: moon-in-2 2s ease-out forwards; 201 | animation-delay: 1.5s 202 | } 203 | .moons-2-static { 204 | opacity: 1; 205 | transform: scale(1) rotate(140deg) 206 | } 207 | .moons-3 { 208 | opacity: 0; 209 | animation: moon-in-3 2s ease-out forwards; 210 | animation-delay: 1.25s 211 | } 212 | .moons-3-static { 213 | opacity: 1; 214 | transform: scale(1) rotate(210deg) 215 | } 216 | .moons-4 { 217 | opacity: 0; 218 | animation: moon-in-4 2s ease-out forwards; 219 | animation-delay: 1s 220 | } 221 | .moons-4-static { 222 | opacity: 1; 223 | transform: scale(1) rotate(280deg) 224 | } 225 | .moons-text { 226 | color: #fff; 227 | opacity: 0; 228 | display: inline-block; 229 | position: absolute 230 | } 231 | .moons-1-static.moons-text-reveal .moons-text { 232 | opacity: 1; 233 | transform: rotate(-66deg); 234 | top: -90px; 235 | left: -190px; 236 | width: 200px; 237 | text-align: center; 238 | transition: all .25s ease-in 239 | } 240 | .moons-2-static.moons-text-reveal .moons-text { 241 | opacity: 1; 242 | transform: rotate(220deg); 243 | top: -160px; 244 | left: -230px; 245 | width: 200px; 246 | text-align: left; 247 | transition: all .25s ease-in 248 | } 249 | .moons-3-static.moons-text-reveal .moons-text { 250 | opacity: 1; 251 | transform: rotate(150deg); 252 | top: -20px; 253 | left: -280px; 254 | width: 220px; 255 | text-align: left; 256 | transition: all .25s ease-in 257 | } 258 | .moons-4-static.moons-text-reveal .moons-text { 259 | opacity: 1; 260 | transform: rotate(80deg); 261 | top: -40px; 262 | left: -200px; 263 | width: 220px; 264 | text-align: center; 265 | transition: all .25s ease-in 266 | } 267 | .moon-circle { 268 | position: absolute; 269 | width: 50px; 270 | height: 50px; 271 | border-radius: 50%; 272 | top: -50px; 273 | left: -50px 274 | } 275 | .zoomToStepOne { 276 | animation: zoomToStepOne 3s linear forwards 277 | } 278 | .zoomStepOne { 279 | transform: scale(.9) translate(-50px,25px) 280 | } 281 | @keyframes zoomToStepOne { 282 | 0% { 283 | transform: scale(1) translate(0,0) 284 | } 285 | 20% { 286 | transform: scale(1) translate(0,0) 287 | } 288 | 100% { 289 | transform: scale(.9) translate(-50px,25px) 290 | } 291 | } 292 | .zoomToStepTwo { 293 | animation: zoomToStepTwo 3s linear forwards 294 | } 295 | .zoomStepTwo { 296 | transform: scale(.8) translate(-65px,45px) 297 | } 298 | @keyframes zoomToStepTwo { 299 | 0% { 300 | transform: scale(.9) translate(-50px,25px) 301 | } 302 | 20% { 303 | transform: scale(.9) translate(-50px,25px) 304 | } 305 | 100% { 306 | transform: scale(.8) translate(-65px,45px) 307 | } 308 | } 309 | .zoomToStepThree { 310 | animation: zoomToStepThree 3s linear forwards 311 | } 312 | .zoomStepThree { 313 | transform: scale(.7) translate(-130px,45px) 314 | } 315 | @keyframes zoomToStepThree { 316 | 0% { 317 | transform: scale(.8) translate(-65px,45px) 318 | } 319 | 20% { 320 | transform: scale(.8) translate(-65px,45px) 321 | } 322 | 100% { 323 | transform: scale(.7) translate(-130px,45px) 324 | } 325 | } 326 | .world-label { 327 | position: absolute; 328 | font-size: 1.5em 329 | } 330 | .world-label-entrance { 331 | opacity: 0; 332 | animation: blur-fade-in .5s linear forwards; 333 | animation-delay: 3s 334 | } 335 | .world-bi { 336 | background-color: #f0633c; 337 | background-image: url(../figures/worlds/BI.png); 338 | background-repeat: no-repeat; 339 | width: 160px; 340 | height: 160px; 341 | top: 10%; 342 | left: 60% 343 | } 344 | .world-bi-entrance { 345 | animation: world-bi-entrance 3s ease-in-out forwards 346 | } 347 | .world-label-bi { 348 | top: 105px; 349 | right: 205px; 350 | width: 100px 351 | } 352 | #slide-world-bi .world-label-bi { 353 | opacity: 0; 354 | animation: blur-fade-in .5s linear forwards; 355 | animation-delay: 3s 356 | } 357 | @keyframes world-bi-entrance { 358 | 0% { 359 | opacity: 0; 360 | width: 50px; 361 | height: 50px; 362 | transform: translate(-345px,114px) 363 | } 364 | 20% { 365 | opacity: 1; 366 | width: 50px; 367 | height: 50px; 368 | transform: translate(-345px,114px) 369 | } 370 | 100% { 371 | opacity: 1; 372 | width: 160px; 373 | height: 160px; 374 | transform: translate(0,0) 375 | } 376 | } 377 | .world-cdsc { 378 | background-color: #14608d; 379 | background-image: url(../figures/worlds/CDSC.png); 380 | background-repeat: no-repeat; 381 | width: 140px; 382 | height: 140px; 383 | top: 45%; 384 | right: 275px 385 | } 386 | .world-label-cdsc { 387 | bottom: 230px; 388 | width: 190px; 389 | right: 70px 390 | } 391 | #slide-world-cdsc .world-label-cdsc { 392 | opacity: 0; 393 | animation: blur-fade-in .5s linear forwards; 394 | animation-delay: 3s 395 | } 396 | #slide-world-cdsc .world-cdsc { 397 | opacity: 1; 398 | animation: world-cdsc-entrance 3s cubic-bezier(.65,0,.35,1) forwards 399 | } 400 | @keyframes world-cdsc-entrance { 401 | 0% { 402 | opacity: 0; 403 | width: 44px; 404 | height: 44px; 405 | transform: translate(-431px,78px) 406 | } 407 | 20% { 408 | opacity: 1; 409 | width: 44px; 410 | height: 44px; 411 | transform: translate(-431px,78px) 412 | } 413 | 100% { 414 | opacity: 1; 415 | width: 140px; 416 | height: 140px; 417 | transform: translate(0,0) 418 | } 419 | } 420 | .world-dqs { 421 | background-color: #fdcea0; 422 | background-image: url(../figures/worlds/DQS.png); 423 | background-repeat: no-repeat; 424 | width: 120px; 425 | height: 120px; 426 | top: 75%; 427 | left: 57% 428 | } 429 | .world-label-dqs { 430 | bottom: 58px; 431 | width: 140px; 432 | right: 245px 433 | } 434 | #slide-world-dqs .world-label-dqs { 435 | opacity: 0; 436 | animation: blur-fade-in .5s linear forwards; 437 | animation-delay: 3s 438 | } 439 | #slide-world-cdsc .world-label-cdsc { 440 | opacity: 0; 441 | animation: blur-fade-in .5s linear forwards; 442 | animation-delay: 3s 443 | } 444 | #slide-world-dqs .world-dqs { 445 | opacity: 1; 446 | animation: world-dqs-entrance 3s cubic-bezier(.65,0,.35,1) forwards 447 | } 448 | @keyframes world-dqs-entrance { 449 | 0% { 450 | opacity: 0; 451 | width: 40px; 452 | height: 40px; 453 | transform: translate(-330px,8px) 454 | } 455 | 20% { 456 | opacity: 1; 457 | width: 40px; 458 | height: 40px; 459 | transform: translate(-330px,8px) 460 | } 461 | 100% { 462 | opacity: 1; 463 | width: 120px; 464 | height: 120px; 465 | transform: translate(0,0) 466 | } 467 | } 468 | :root { 469 | --world-easing-function: cubic-bezier(.65,.05,.36,1) 470 | } 471 | #slide-data-engineering .world-it1 { 472 | animation: world-it1-exit 3s var(--world-easing-function) forwards 473 | } 474 | @keyframes world-it1-exit { 475 | 0% { 476 | transform: rotate(0) scale(1) 477 | } 478 | 50% { 479 | opacity: 1; 480 | transform: rotate(-450deg) scale(1.33) 481 | } 482 | 100% { 483 | opacity: 0; 484 | transform: rotate(-2160deg) scale(1.25) 485 | } 486 | } 487 | #slide-data-engineering .world-it2 { 488 | opacity: 0; 489 | animation: world-it2-enter 3s var(--world-easing-function) forwards 490 | } 491 | @keyframes world-it2-enter { 492 | 0% { 493 | opacity: 0; 494 | transform: rotate(2160deg) scale(1.33) 495 | } 496 | 50% { 497 | opacity: 0; 498 | transform: rotate(2160deg) scale(1.33) 499 | } 500 | 100% { 501 | opacity: 1; 502 | transform: rotate(0) scale(1.25) 503 | } 504 | } 505 | #slide-data-engineering .world-it::before { 506 | top: 70px; 507 | left: -70px; 508 | opacity: 0; 509 | animation: fadeInLeft .5s linear forwards; 510 | animation-delay: 3s 511 | } 512 | .world-it2::after { 513 | content: "Data Eng"; 514 | color: #000; 515 | position: absolute; 516 | top: 100px; 517 | left: 120px; 518 | width: 50px 519 | } 520 | #slide-data-engineering .world-it2::after { 521 | opacity: 0; 522 | animation: fadeInUp .5s linear forwards; 523 | animation-delay: 3s 524 | } 525 | #slide-data-engineering .world-it1::before { 526 | display: none 527 | } 528 | #slide-data-engineering .moons-4-static { 529 | animation: find-db-exit 1.5s linear forwards 530 | } 531 | @keyframes find-db-exit { 532 | 0% { 533 | opacity: 1; 534 | transform: scale(1) rotate(280deg) 535 | } 536 | 100% { 537 | opacity: 0; 538 | transform: scale(.15) 539 | } 540 | } 541 | #slide-app-dev .world-it2 { 542 | animation: world-it2-exit 2s var(--world-easing-function) forwards 543 | } 544 | @keyframes world-it2-exit { 545 | 0% { 546 | opacity: 1; 547 | transform: scale(1.25) rotate(0) 548 | } 549 | 100% { 550 | opacity: 0; 551 | transform: scale(1.33) rotate(-720deg) 552 | } 553 | } 554 | .world-it3::after { 555 | content: "Data Eng"; 556 | color: #000; 557 | position: absolute; 558 | top: 100px; 559 | left: 120px; 560 | width: 50px 561 | } 562 | .world-it3::before { 563 | content: "App Dev"; 564 | color: #000; 565 | font-size: 1em; 566 | position: absolute; 567 | top: 50px; 568 | left: 20px; 569 | width: 50px 570 | } 571 | #slide-app-dev .world-it3::after { 572 | opacity: 0; 573 | animation: fadeInUp .5s linear forwards; 574 | animation-delay: 2s 575 | } 576 | #slide-app-dev .world-it3::before { 577 | opacity: 0; 578 | animation: fadeInUp .5s linear forwards; 579 | animation-delay: 2s 580 | } 581 | #slide-app-dev .world-it2::before { 582 | display: none 583 | } 584 | #slide-app-dev .world-it3 { 585 | animation: world-it3-enter 2s var(--world-easing-function) forwards 586 | } 587 | @keyframes world-it3-enter { 588 | 0% { 589 | opacity: 0; 590 | transform: scale(1.25) rotate(0) 591 | } 592 | 100% { 593 | opacity: 1; 594 | transform: scale(1.33) rotate(-720deg) 595 | } 596 | } 597 | .world-label-it { 598 | position: absolute; 599 | color: #fff; 600 | font-size: 1.5em; 601 | top: 369px; 602 | left: 181px 603 | } 604 | #slide-app-dev .world-label-it { 605 | opacity: 0; 606 | animation: fadeInLeft .5s linear forwards; 607 | animation-delay: 2s 608 | } 609 | .vibrate-1 { 610 | animation: vibrate-1 6s linear infinite both 611 | } 612 | @keyframes vibrate-1 { 613 | 0% { 614 | transform: rotate(30deg) translate(0) 615 | } 616 | 20% { 617 | transform: rotate(28deg) translate(-4px,4px) 618 | } 619 | 40% { 620 | transform: rotate(30deg) translate(-4px,-4px) 621 | } 622 | 60% { 623 | transform: rotate(32deg) translate(4px,4px) 624 | } 625 | 80% { 626 | transform: rotate(30deg) translate(4px,-4px) 627 | } 628 | 100% { 629 | transform: rotate(30deg) translate(0) 630 | } 631 | } 632 | .space-person { 633 | background-image: url(../figures/worlds/space-suit.png); 634 | background-size: contain; 635 | background-repeat: no-repeat; 636 | position: absolute; 637 | width: 120px; 638 | height: 200px; 639 | top: 50px; 640 | left: 130px 641 | } 642 | .space-person::before { 643 | content: 'CDO'; 644 | color: #fff; 645 | font-size: 1.25em; 646 | position: absolute; 647 | left: -70px; 648 | transform: rotate(-30deg); 649 | top: 90px; 650 | animation: fadeIn .5s linear forwards 651 | } -------------------------------------------------------------------------------- /docs/index.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Build your own universe" 3 | subtitle: "Scale high-quality research data provisioning with R packages" 4 | author: 5 | - .b[Travis Gerke] .gray-2[@travisgerke] 6 | - .b[Garrick Aden-Buie] .gray-2[@grrrck] 7 | institute: ".small[.blue-medium[Moffitt Cancer Center]]" 8 | date: 'August 28, 2020' 9 | output: 10 | xaringan::moon_reader: 11 | lib_dir: libs 12 | css: 13 | - css/moffitt-xaringan.css 14 | - css/moffitt-xaringan-extra.css 15 | - css/talk-base.css 16 | - css/build-your-universe.min.css 17 | seal: false 18 | nature: 19 | titleSlideClass: ["bottom", "left"] 20 | slideNumberFormat: "%current%" 21 | highlightStyle: atom-one-light 22 | highlightLines: true 23 | ratio: 16:9 24 | countIncrementalSlides: true 25 | --- 26 | 27 | name: title 28 | class: left bottom hide-count 29 | background-color: #FBFCFF; 30 | 31 | 32 | ```{r setup, echo=FALSE} 33 | source("library.R") 34 | source("setup.R")$value 35 | ``` 36 | 37 | 38 | 39 |
40 |
41 | 42 | ```{r title-slides, echo=FALSE, message = FALSE, warning = FALSE} 43 | htmltools::withTags( 44 | div( 45 | class = "talk-meta", 46 | div( 47 | class = "talk-title", 48 | h1(rmarkdown::metadata$title), 49 | p(rmarkdown::metadata$subtitle) 50 | ), 51 | div( 52 | class = "talk-author", 53 | paste(rmarkdown::metadata$author, collapse = "
"), 54 | br(), 55 | span(paste(rmarkdown::metadata$institute, collapse = ", ")) 56 | ), 57 | div( 58 | class = "talk-date", 59 | knitr::knit(text = rmarkdown::metadata$date, quiet = TRUE) 60 | ) 61 | ) 62 | ) 63 | ``` 64 | 65 | 66 | 67 | ??? 68 | Thanks to the amazing organizers of R/Medicine! Intro self. 69 | 70 | This is a co-presented talk with Garrick Aden-Buie; I'll hand off to him at the conclusion of my slides 71 | 72 | Since I do not own the last slide, I'll inject a couple formalities here: 73 | - You're about to see some really cool slides, and I can personally take credit for effectively none of them. Garrick is the wizard. 74 | - We're hiring multiple positions with a specific eye out for R devs/DS over the next few months; feel free to reach out 75 | 76 | To orient you to the origin of this talk, where Garrick and I are coming from, and how it came to be that we gain high value from an R pkg universe, I'll begin with a story which is based on Moffitt's experience but could represent any institution's data-related journey... 77 | 78 | --- 79 | class: hide-count no-logo no-border bg-black 80 | 81 | --- 82 | class: hide-count no-logo no-border bg-black 83 | 84 |
85 | 86 | ??? 87 | Once upon a time, our organization conducted all data-related business in an amorphous cloud known as the IT department. 88 | 89 | This is a common paradigm for many healthcare organizations in early stages of data maturity. 90 | 91 | --- 92 | class: hide-count no-logo no-border bg-black carina-nebula-soft animated fadeIn 93 | 94 |
Help you make a dashboard!
95 |
Fulfill your research data request!
96 |
Remember how the data got here!
97 |
Help you find that database!
98 |
99 | 100 | ??? 101 | The IT dept had many roles. 102 | 103 | --- 104 | class: hide-count no-logo no-border bg-black carina-nebula-soft 105 | 106 |
Help you make a dashboard!
107 |
Fulfill your research data request!
108 |
Help you find that database!
109 |
Remember how the data got here!
110 |
111 | 112 | ??? 113 | Hospital operations needed dashboards for planning purposes 114 | 115 | --- 116 | class: hide-count no-logo no-border bg-black carina-nebula-soft 117 | 118 |
Help you make a dashboard!
119 |
Fulfill your research data request!
120 |
Help you find that database!
121 |
Remember how the data got here!
122 |
123 | 124 | ??? 125 | Researchers needed patient or biospecimen data for their IRB-approved protocols 126 | 127 | --- 128 | class: hide-count no-logo no-border bg-black carina-nebula-soft 129 | 130 |
Help you make a dashboard!
131 |
Fulfill your research data request!
132 |
Remember how the data got here!
133 |
Help you find that database!
134 |
135 | 136 | ??? 137 | Somebody needed to know about data lineage as well as coding or metadata standards; basically, how the data got here why it looks like it does 138 | 139 | --- 140 | class: hide-count no-logo no-border bg-black carina-nebula-soft 141 | 142 |
Help you make a dashboard!
143 |
Fulfill your research data request!
144 |
Help you find that database!
145 |
Remember how the data got here!
146 |
147 | 148 | ??? 149 | Organizing databases into a warehouse and granting access was important. We had someone for that! 150 | 151 | But eventually, some of these teams were operating at a scale which would be better situated as independent of the IT gravity field 152 | 153 | --- 154 | name: world-bi 155 | class: hide-count no-logo no-border bg-black carina-nebula-soft fullscreen 156 | 157 |
158 |
Help you make a dashboard!
159 |
Fulfill your research data request!
160 |
Remember how the data got here!
161 |
Help you find that database!
162 |
163 |
164 | 165 |
166 |
Business
Intelligence
167 | 168 | ??? 169 | One of these, was business intelligence. That person who was making dashboards for operational, i.e. non-research stakeholders, is now part of a larger team that creates such products at scale 170 | 171 | --- 172 | name: world-cdsc 173 | class: hide-count no-logo no-border bg-black carina-nebula-soft fullscreen 174 | 175 |
176 |
Fulfill your research data request!
177 |
Help you find that database!
178 |
Remember how the data got here!
179 |
180 |
181 | 182 |
183 |
Business
Intelligence
184 | 185 |
186 |
Collaborative
Data
Services
187 | 188 | ??? 189 | Next, our research-focused stakeholders had many of the same needs as the operational end-users, such as reporting/dashboarding and, importantly data provisioning. The twist in the research space is that such activities must be conducted in accordance with IRB and ethical approval, and study design feasibility as it relates to data availability and structure requires specialized training. Hence, the CDS team was formed. This is one of the groups that Garrick and I are representing today. 190 | 191 | --- 192 | name: world-dqs 193 | class: hide-count no-logo no-border bg-black carina-nebula-soft fullscreen 194 | 195 |
196 |
Remember how the data got here!
197 |
Help you find that database!
198 |
199 |
200 | 201 |
202 |
Business
Intelligence
203 | 204 |
205 |
Collaborative
Data
Services
206 | 207 |
208 |
Data
Quality
209 | 210 | ??? 211 | But CDS can't operate at scale in a vacuum either. A critical and complementary team, Data Quality and Standards, formed from IT's "data historian." They ensure that data dictionaries are robust and data lineage is understood by the BI and CDS teams for appropriate downstream data usage. 212 | 213 | --- 214 | name: data-engineering 215 | class: hide-count no-logo no-border bg-black carina-nebula-soft fullscreen 216 | 217 |
218 |
Help you find that database!
219 |
220 |
221 |
222 | 223 |
224 |
Business
Intelligence
225 | 226 |
227 |
Collaborative
Data
Services
228 | 229 |
230 |
Data
Quality
231 | 232 | ??? 233 | As institutional data assets grew, warehousing and access rules became necessarily complex. Data engineering formed a new continent within IT to meet the challenge. 234 | 235 | --- 236 | name: rocket 237 | class: hide-count no-logo no-border bg-black carina-nebula-soft fullscreen 238 | 239 |
240 |
241 |
242 | 243 |
244 |
Business
Intelligence
245 | 246 |
247 |
Collaborative
Data
Services
248 | 249 |
250 |
Data
Quality
251 | 252 |
253 | 254 | ??? 255 | Now, with so many teams completing data-related operations at a rapid pace, we needed a shuttlecraft to coordinate technology strategy, inform general data governance, and mine valuable software ore from the astRoid belt 256 | 257 | --- 258 | name: app-dev 259 | class: hide-count no-logo no-border bg-black carina-nebula-soft fullscreen animated fadeIn 260 | 261 |
262 |
263 |
264 |
265 | 266 |
267 |
Business
Intelligence
268 | 269 |
270 |
Collaborative
Data
Services
271 | 272 |
273 |
Data
Quality
274 | 275 |
276 | 277 |
IT
278 | 279 | ??? 280 | When those tools are ready for placement and maintenance in the institutionally supported production environment, the new Applications Development land mass within IT can help out. For example, they would maintain software such as RStudio Server or GitHub Enterprise. 281 | 282 | --- 283 | name: cdo 284 | class: hide-count no-logo no-border bg-black carina-nebula-soft fullscreen animated fadeIn 285 | 286 |
287 |
288 |
289 | 290 |
291 |
Business
Intelligence
292 | 293 |
294 |
Collaborative
Data
Services
295 | 296 |
297 |
Data
Quality
298 | 299 |
300 | 301 |
IT
302 | 303 |
304 | 305 | ??? 306 | This whole story, admittedly with some shortcuts for clarity, mirrors the rise of the Chief Data Officer role across the healthcare industry. Indeed, all of these groups tend to roll up or be horizontally aligned in some way with the CDO's vertical. 307 | 308 | --- 309 | class: left middle blueprint2 310 | 311 | .f4[ 312 | Scaling .b.blue[provisioning]
313 | by scaling .b.em.red[people] 314 | ] 315 | 316 | .absolute.bottom-1.right-1[ 317 | 318 | ] 319 | 320 | ??? 321 | Taken together, this is our first hint that scaling data provisioning isn't just about scaling data: it's about scaling the people who are doing the provisioning. In part 2, Garrick is going to tell you more about the "how" 322 | 323 | --- 324 | class: left middle blueprint2 325 | 326 | .f4[ 327 | [...continued in part 2 →](part-two.html) 328 | ] 329 | 330 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Build your own universe 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 339 | 340 | 341 | 430 | 431 | 450 | 451 | 461 | 462 | 463 | -------------------------------------------------------------------------------- /docs/part-two.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Build your own universe" 3 | subtitle: "Scale high-quality research data provisioning with R packages" 4 | author: 5 | - Travis Gerke 6 | - Garrick Aden-Buie 7 | institute: ".small[.blue-medium[Moffitt Cancer Center]]" 8 | date: 'August 28, 2020' 9 | output: 10 | xaringan::moon_reader: 11 | lib_dir: libs 12 | css: 13 | - css/moffitt-xaringan.css 14 | - css/moffitt-xaringan-extra.css 15 | - css/talk-base.css 16 | - css/part-two.min.css 17 | includes: 18 | in_header: head.html 19 | seal: false 20 | nature: 21 | titleSlideClass: ["bottom", "left"] 22 | slideNumberFormat: "%current%" 23 | highlightStyle: atom-one-light 24 | highlightLines: true 25 | ratio: 16:9 26 | countIncrementalSlides: true 27 | --- 28 | 29 | class: left middle blueprint2 30 | 31 | ```{r setup, echo=FALSE} 32 | source("library.R") 33 | source("setup.R")$value 34 | ``` 35 | 36 | .f4[ 37 | Scaling .b.blue[provisioning]
38 | by scaling .b.red[people] 39 | ] 40 | 41 | .absolute.bottom-1.right-1[ 42 | [...continued from part 1](index.html) 43 | ] 44 | 45 | ??? 46 | 47 | Travis ends here... 48 | 49 | 50 | --- 51 | class: left middle blueprint2 52 | 53 | .f4[ 54 | Scaling .blue[provisioning]
55 | by scaling .red[people]
56 | ... by scaling .b.green[access] 57 | ] 58 | 59 | 60 | ??? 61 | 62 | So as Travis talked about scaling provisioning 63 | by scaling systems of people, 64 | I'm going to talk about how we scale 65 | access to data systems 66 | through R packages 67 | 68 | I'm going to start with an entirely hypothetical but familiar story... 69 | 70 | --- 71 | 72 | class: center middle 73 | 74 | .center.w-50[ 75 | .bubble.thought.vanishIn[ 76 | How do I connect our .blue.b[tissue sample] inventory to a .orange.b[patient's clinical] data? 77 | ] 78 | 79 | .tr.f6[ 80 | 🤔 81 | ] 82 | ] 83 | 84 | ??? 85 | 86 | It starts with a question 87 | 88 | I want to connect **our tissue sample** inventory 89 | to **patient clinical** data. 90 | 91 | It's not something I've done before, 92 | so I'm not quite sure how to access the samples table 93 | or how to link a sample to a patient 94 | but obviously it has to be possible. 95 | 96 | So how do I get started? 97 | 98 | --- 99 | class: no-border no-logo hide-count 100 | background-image: url(figures/big-data-sensor-display.jpg) 101 | background-size: cover 102 | background-position: center right 103 | 104 | ??? 105 | 106 | If you believe the big data stock photos, 107 | I go to the self-service data wall 108 | and point at the numbers I want. 109 | 110 | --- 111 | 112 | .ba.b--gray-4.br2.pv2.ph3.mv5.shadow-4.animated.fadeIn[ 113 | .b[Aden-Buie, Garrick]
114 | Question about Sample Availability Table 115 | 116 | To: .gray-3[data-ops@acmemed.org] 117 | 118 | *** 119 | 120 | .o-0[ 121 | How can I connect our sample inventory to patient-level clinical data? 122 | 123 | I've heard you know the secret. 124 | 125 | Thanks! 126 | 127 | .gray-3[Garrick] 128 | ] 129 | ] 130 | 131 | ??? 132 | 133 | In reality, it probably starts with an email. 134 | Or many emails. 135 | 136 | I start by reaching out to someone I know 137 | in data engineering 138 | who manages that particular data resource 139 | to see what they can tell me... 140 | 141 | --- 142 | 143 | .ba.b--gray-4.br2.pv2.ph3.mv5.shadow-4[ 144 | .b[Aden-Buie, Garrick]
145 | Question about Sample Availability Table 146 | 147 | To: .gray-3[data-ops@acmemed.org] 148 | 149 | *** 150 | 151 | .css-typing[ 152 | How can I connect our sample inventory to patient-level clinical data? 153 | 154 | I've heard you know the secret. 155 | 156 | Thanks! 157 | 158 | .gray-3[Garrick] 159 | ] 160 | ] 161 | 162 | 163 | --- 164 | 165 | .w-90.ba.b--gray-4.br2.pv2.ph3.mv5.shadow-4.absolute.animated.lightSpeedOut[ 166 | .b[Aden-Buie, Garrick]
167 | Question about Sample Availability Table 168 | 169 | To: .gray-3[data-ops@acmemed.org] 170 | 171 | *** 172 | 173 | How can I connect our sample inventory to patient-level clinical data? 174 | 175 | I've heard you know the secret. 176 | 177 | Thanks! 178 | 179 | .gray-3[Garrick] 180 | ] 181 | 182 | .ba.b--gray-4.br2.pv2.ph3.mv5.shadow-4.relative.animated.bounceInDown.delay-2s[ 183 | .b[Friendly Data Ops Person]
184 | RE: Question about Sample Availability Table 185 |
186 | 187 |
188 | 189 | To: .gray-3[Aden-Buie, Garrick] 190 | 191 | 192 | *** 193 | Hey Garrick, 194 | 195 | Here's the query we use to populate the table. 196 | 197 | Good Luck! 198 | 199 | .gray-3[Data Ops] 200 | ] 201 | 202 | ??? 203 | 204 | I fire off the email and a little while later I get a reply. 205 | 206 | _read email_ 207 | 208 | And look, the email came with an attachment that I can open up. 209 | 210 | --- 211 | name: sql-example 212 | 213 |
214 | 215 | .code-two-column.shadow-4.animated.zoomIn[ 216 |
217 | sample-inventory-patient.sql 218 |
219 | 220 |
221 |
222 | 223 | ```{sql example-query, eval=FALSE} 224 | SELECT SA.PERSON_ID,SA.SAMPLE_ID,SA.SAMPLE_FAMILY_ID 225 | ,CH1.CHOICE_NAME AS COLLECTION_CONSORTIUM 226 | ,CH2.CHOICE_NAME AS CELFILE_RELATED 227 | ,CH3.CHOICE_NAME AS FROZEN_TUMOR_TISSUE 228 | ,CH4.CHOICE_NAME AS FROZEN_NORMAL_TISSUE 229 | ,CH5.CHOICE_NAME AS WHOLE_BLOOD 230 | ,CH6.CHOICE_NAME AS BUFFY_COAT 231 | ,CH7.CHOICE_NAME AS DNA_FROM_BLOOD 232 | ,CH8.CHOICE_NAME AS PLASMA 233 | ,CH9.CHOICE_NAME AS SERUM 234 | ,CH10.CHOICE_NAME AS PARAFFIN 235 | ,CH11.CHOICE_NAME AS URINE 236 | ,CH12.CHOICE_NAME AS RNA 237 | ,CH13.CHOICE_NAME AS MNC 238 | ,CH14.CHOICE_NAME AS DNA_FROM_SOLID 239 | ,CH15.CHOICE_NAME AS ADJACENT_NORMAL 240 | ,CH16.CHOICE_NAME AS LCS_PROTOCOL_BLOOD_AVA 241 | ,CH17.CHOICE_NAME AS SOO_CATEGORY 242 | ,CH36.CHOICE_NAME AS SOO_TISSUETYPE 243 | ,CH19.CHOICE_NAME AS HISTOLOGYCAP 244 | ,CH20.CHOICE_NAME AS PRIMARY_METASTATIC 245 | ,PI.MEDICAL_ID 246 | ,PI.PARTNER_ID 247 | ,CH21.Anatomic_Site AS SOO_ANATOMICSITE 248 | ,CH22.CHOICE_NAME AS GROSS_DIAGNOSIS 249 | ,CH23.CHOICE_NAME AS PRIMARY_SITE_PATIENT 250 | ,CH24.CHOICE_NAME AS HISTOLOGY_PATIENT 251 | ,CH25.CHOICE_NAME AS DERIVED_CATEGORY 252 | ,CH27.CHOICE_NAME AS DERIVED_TISSUETYPE 253 | ,SA.DERIVED_ANATOMIC_SITE AS DERIVED_ANATOMICSITE 254 | ,CH29.TISSUE_TYPE AS COL_SITE_TISSUE_TYPE 255 | ,SA.SPECIMEN_COLLECTION_DATE 256 | ,CH29.ANATOMIC_SITE AS COL_SITE_ANATOMIC 257 | ,CH30.CHOICE_NAME AS COL_SITE_CATEGORY 258 | ,SA.BIOBANKING_SUBJECT_ID AS SUBJECT_ID 259 | ,CH31.CHOICE_NAME AS WHOLE_EXOME 260 | ,CH32.CHOICE_NAME AS TARGET_EXOME 261 | ,CH33.CHOICE_NAME AS SAMPLE_TYPE 262 | ,SA.CURRENT_QUANTITY 263 | ,CH34.CHOICE_NAME AS PATH_DIAGNOSIS_PQC 264 | ,SA.SAMPLE_TO_CANR_CHAR_LINK 265 | ,CH35.CHOICE_NAME AS PROTOCOL 266 | FROM ABC.SAMPLE SA 267 | LEFT JOIN ABC.CHOICE CH1 ON SA.COLLECTION_FACILITY = CH1.CHOICE_ID 268 | LEFT JOIN ABC.CHOICE CH2 ON SA.CELFILE_RELATED = CH2.CHOICE_ID 269 | LEFT JOIN ABC.PATIENT PI ON SA.PERSON_ID = PI.PERSON_ID 270 | LEFT JOIN ABC.SAMPLE_INDICATOR SI ON SA.SAMPLE_KEY = SI.SAMPLE_KEY 271 | LEFT JOIN ABC.CHOICE CH3 ON SI.FROZ_TTISE_AVL_FOR_RIND = CH3.CHOICE_ID 272 | LEFT JOIN ABC.CHOICE CH4 ON SI.FROZ_NORM_TISS_AVL_FOR_RIND = CH4.CHOICE_ID 273 | LEFT JOIN ABC.CHOICE CH5 ON SI.WHLBAVLFDNAEXTNDR_INCL_PAXDNAS = CH5.CHOICE_ID 274 | LEFT JOIN ABC.CHOICE CH6 ON SI.BUFY_COAT_AVL_FOR_RIND = CH6.CHOICE_ID 275 | LEFT JOIN ABC.CHOICE CH7 ON SI.DNAEXTRF_BLOD_AVL_FOR_RIND = CH7.CHOICE_ID 276 | LEFT JOIN ABC.CHOICE CH8 ON SI.PLA_AVL_FOR_RIND = CH8.CHOICE_ID 277 | LEFT JOIN ABC.CHOICE CH9 ON SI.SER_AVL_FOR_RIND = CH9.CHOICE_ID 278 | LEFT JOIN ABC.CHOICE CH10 ON SI.PARAF_BLK_AVL_FOR_RIND = CH10.CHOICE_ID 279 | LEFT JOIN ABC.CHOICE CH11 ON SI.UNINE_AVL_FOR_RIND = CH11.CHOICE_ID 280 | LEFT JOIN ABC.CHOICE CH12 ON SI.RNAEXTRF_SOLID_TTISE_FOR_RIND = CH12.CHOICE_ID 281 | LEFT JOIN ABC.CHOICE CH13 ON SI.MONO_CELS_EXTR_FOR_RIND = CH13.CHOICE_ID 282 | LEFT JOIN ABC.CHOICE CH14 ON SI.DNAEXTRF_SOLID_TTISE_FOR_RIND = CH14.CHOICE_ID 283 | LEFT JOIN ABC.CHOICE CH15 ON SI.ADJA_NSLD_SAMPLE_AVL_INDR = CH15.CHOICE_ID 284 | LEFT JOIN ABC.CHOICE CH16 ON SI.LCS_PROTC_BLOD_AVL_FOR_RIND = CH16.CHOICE_ID 285 | LEFT JOIN ABC.CHOICE CH17 ON SA.DERIVED_SOO_CATEGORY = CH17.CHOICE_ID 286 | LEFT JOIN ABC.CHOICE CH18 ON SA.DERIVED_SOO_TISSUE_TYPE = CH18.CHOICE_ID 287 | LEFT JOIN ABC.CHOICE CH19 ON SA.HISTOLOGY_TYPE_CONFORMED_CAP = CH19.CHOICE_ID 288 | LEFT JOIN ABC.CHOICE CH20 ON SA.DERIVED_PRIMARY_VS_METASTATIC = CH20.CHOICE_ID 289 | LEFT JOIN ABC.TISSUE_TYPE CH21 290 | ON SUBSTR(SA.DERIVED_SOO_ANATOMIC_SITE,0,8) = CH21.TISSUE_TYPE_KEY 291 | LEFT JOIN ABC.CHOICE CH22 ON SA.DERIVED_GROSS_DIAGNOSIS = CH22.CHOICE_ID 292 | LEFT JOIN ABC.CHOICE CH23 ON SA.PRIMARY_SITE_PATIENT = CH23.CHOICE_ID 293 | LEFT JOIN ABC.CHOICE CH24 ON SA.HISTOLOGY_PATIENT = CH24.CHOICE_ID 294 | LEFT JOIN ABC.CHOICE CH25 ON SA.DERIVED_CATEGORY = CH25.CHOICE_ID 295 | LEFT JOIN ABC.CHOICE CH26 ON SA.DERIVED_TISSUE_TYPE = CH26.CHOICE_ID 296 | LEFT JOIN ABC.CHOICE CH27 ON SA.DERIVED_TISSUE_TYPE = CH27.CHOICE_ID 297 | LEFT JOIN ABC.CHOICE CH28 ON SA.COLLECTION_SITE_TISSUE_TYPE = CH28.CHOICE_ID 298 | LEFT JOIN ABC.TISSUE_TYPE CH29 ON SA.COLLECTION_SITE_ANATOMIC = CH29.TISSUE_TYPE_KEY 299 | LEFT JOIN ABC.CHOICE CH30 ON SA.COLLECTION_SITE_CATEGORY = CH30.CHOICE_ID 300 | LEFT JOIN ABC.CHOICE CH31 ON SI.WHL_EXOM_SEQ_AVL_INDR = CH31.CHOICE_ID 301 | LEFT JOIN ABC.CHOICE CH32 ON SI.TAR_EXOM_SEQ_AVL_INDR = CH32.CHOICE_ID 302 | LEFT JOIN ABC.CHOICE CH33 ON SA.SAMPLE_TYPE = CH33.CHOICE_ID 303 | LEFT JOIN ABC.CHOICE CH34 ON SA.PATH_DIAGNOSIS_PQC = CH34.CHOICE_ID 304 | LEFT JOIN ABC.CHOICE CH35 ON SA.PROTOCOL = CH35.CHOICE_ID 305 | LEFT JOIN ABC.CHOICE CH36 ON SA.DERIVED_SOO_TISSUE_TYPE = CH36.CHOICE_ID 306 | WHERE SA.ISDELETED = 0 307 | ``` 308 | ] 309 | 310 | ??? 311 | 312 | And I'm immediately hit with a wall of sequel. 313 | 314 | This query doesn't look pretty 315 | but in a couple hours I'll probably get the gist of it... 316 | 317 | Somewhere in here, I'll find that this query uses... 318 | `sample`, 319 | `patient` and 320 | `sample_indicator` tables 321 | 322 | and that all these lines 323 | are for translated coded columns into text labels 324 | 325 | And hey, it's at least code, right? 326 | 327 | Well... since we're emailing files around, sometimes you'll get a query like this 328 | in a slightly different format... 329 | 330 | --- 331 | class: no-logo no-border hide-count word-doc animated-slide slideInRight boingOutDown 332 | 333 | 334 | 335 | 336 | 337 | ```{sql ref.label="example-query", eval=FALSE} 338 | ``` 339 | 340 | ??? 341 | 342 | Like a word document! 343 | 344 | Where the query doesn't really fit on screen or a page... 345 | 346 | and formatting choices are... fluid 347 | 348 | --- 349 | class: middle animated fadeIn delay-1s 350 | 351 | .f6.b[SQL is .red[robot logic] 🤖] 352 | 353 | ![](figures/apress-select-sqlite.jpg) 354 | 355 | .footnote.tr[ 356 | [_Definitive Guide to SQLite_](https://www.apress.com/gp/book/9781430232254) 357 | ] 358 | 359 | ??? 360 | 361 | Putting aside the emailing and the word document format, 362 | SQL queries aren't a great vehicle for knowledge transfer 363 | 364 | They're good for precisely communicating data specifications 365 | in the robot logic databases understand 366 | 367 | but we have other ways of working with data 368 | that have been specifically designed with humans in mind 369 | 370 | --- 371 | class: middle 372 | 373 | .f6.b[.code[dplyr] is .green[human logic] 🤗] 374 | 375 |
376 | Programs must be written for people to read, and only incidentally for machines to execute. 377 | 378 |

Harold Abelson, Structure and Interpretation of Computer Programs 379 |
380 | 381 | ??? 382 | 383 | for example, dplyr 384 | 385 | whose API is very intentionally designed 386 | in line with the philopsophy that code is 387 | written for people to read 388 | and only incidentally for machines to execute 389 | 390 | Which reminds me of a great quote from Jenny Bryan... 391 | 392 | --- 393 | class: middle 394 | 395 |
396 | Of course someone
397 | has to write for loops.
398 | It doesn't have to be you. 399 |
400 | 401 |
— Jenny Bryan
402 | 403 | --- 404 | class: middle animated-slide fadeIn 405 | 406 | 407 |
408 | Of course someone
409 | has to write for loops.
410 | It doesn't have to be you. 411 |
412 | 413 |
414 |
SQL
415 | 416 |
— Garrick Aden-Buie
417 | 418 | ??? 419 | 420 | Let's take a look at what this query might look like 421 | in an alternate universe... 422 | 423 | --- 424 | name: moffittverse-code-example 425 | class: animated-slide fadeIn 426 | 427 | .remark-code.code-example.pa2[ 428 | .purple[library](moffittverse)

429 | use_backend(.green["abc"])

430 | samples    <- abc_tbl(.green["sample_inventory"])
431 | patients   <- abc_tbl(.green["patient"])
432 | sample_ind <- abc_tbl(.green["sample_indicators"])

433 | samples %>% 
434 |   left_join(sample_ind, by = .green["SAMPLE_ID"]) %>% 
435 |   left_join(patients, by = .green["PERSON_ID"]) %>% 
436 |   abc_not_deleted() %>% 
437 |   abc_choice_replace() 438 | ] 439 | 440 | ??? 441 | 442 | Here's the same query, 443 | rewritten using a blend of `dplyr` 444 | and custom functions that support our particular setup 445 | 446 | --- 447 | 448 | 449 | .remark-code.code-example.pa2.gray-4[ 450 | library(moffittverse)

451 | use_backend("abc")

452 | samples    <- abc_tbl("sample_inventory")
453 | patients   <- abc_tbl("patient")
454 | sample_ind <- abc_tbl("sample_indicators")

455 | samples %>% 
456 |   left_join(sample_ind, by = "SAMPLE_ID") %>% 
457 |   left_join(patients, by = "PERSON_ID") %>% 
458 |   abc_not_deleted() %>% 
459 |   abc_choice_replace()) 460 | ] 461 | 462 | ??? 463 | 464 | Let's walk through what this code represents step by setup 465 | 466 | --- 467 | class: animated-slide fadeIn 468 | 469 | .remark-code.code-example.pa2.gray-4[ 470 | .leader-end-1.gray-1[library(moffittverse)]

471 | use_backend("abc")

472 | samples    <- abc_tbl("sample_inventory")
473 | patients   <- abc_tbl("patient")
474 | sample_ind <- abc_tbl("sample_indicators")

475 | samples %>% 
476 |   left_join(sample_ind, by = "SAMPLE_ID") %>% 
477 |   left_join(patients, by = "PERSON_ID") %>% 
478 |   abc_not_deleted() %>% 
479 |   abc_choice_replace() 480 | ] 481 | 482 |
Our universe
483 | 484 | ??? 485 | 486 | We call our universe _moffittverse_. 487 | 488 | Very much inspired by the _tidyverse_, 489 | where a single `library(moffittverse)` 490 | loads a common set of packages 491 | that we use for nearly every data request 492 | 493 | --- 494 | 495 | .remark-code.code-example.pa2.gray-4[ 496 | .leader-end-1.gray-1[library(moffittverse)]
497 | .expand-in.h-250px.gray-1.bg-gray-8.ph3.pv1[ 498 | ── .b[Attaching packages] ──────── moffittverse 0.0.1 ──
499 | .green[✓] tibble     3.0.1          .green[✓] purrr      0.3.4    
500 | .green[✓] tidyr      1.1.0          .green[✓] dplyr      1.0.0    
501 | .green[✓] fs         1.5.0          .green[✓] stringr    1.4.0    
502 | .green[✓] glue       1.4.1          .green[✓] forcats    0.5.0    
503 | .green[✓] readr      1.3.1          .green[✓] lubridate  1.7.9    
504 | .green[✓] readxl     1.3.1          .green[✓] moffittCDS 0.1.0 505 | ]

506 | use_backend("abc")

507 | samples    <- abc_tbl("sample_inventory")
508 | patients   <- abc_tbl("patient")
509 | sample_ind <- abc_tbl("sample_indicators")

510 | samples %>% 
511 |   left_join(sample_ind, by = "SAMPLE_ID") %>% 
512 |   left_join(patients, by = "PERSON_ID") %>% 
513 |   abc_not_deleted() %>% 
514 |   abc_choice_replace() 515 | ] 516 | 517 |
Our universe
518 | 519 | ??? 520 | 521 | Most of the packages are from the tidyverse, 522 | 523 | but we also include our own supporting package: 524 | ▶️ `moffittCDS` 525 | specifically tailored to my team's workflow. 526 | 527 | This creates a common starting point 528 | for everyone on the team. 529 | 530 | --- 531 | class: animated-slide fadeIn 532 | 533 | .remark-code.code-example.pa2.gray-4[ 534 | library(moffittverse)

535 | .leader-end-2.gray-1[use_backend("abc")]

536 | samples    <- abc_tbl("sample_inventory")
537 | patients   <- abc_tbl("patient")
538 | sample_ind <- abc_tbl("sample_indicators")

539 | samples %>% 
540 |   left_join(sample_ind, by = "SAMPLE_ID") %>% 
541 |   left_join(patients, by = "PERSON_ID") %>% 
542 |   abc_not_deleted() %>% 
543 |   abc_choice_replace() 544 | ] 545 | 546 |
Standardized,
managed connections
547 | 548 | ??? 549 | 550 | It also gives us a formal "on ramp" 551 | to install and set up database dependencies 552 | that we can leverage in specific packages 553 | to interface with our many back-end systems 554 | 555 | This makes connecting to a specific databse straightforward... 556 | 557 | you call _use backend_ 558 | with the name of the database or server 559 | that you need to connect to 560 | 561 | --- 562 | class: animated-slide fadeIn 563 | 564 | .remark-code.code-example.pa2.gray-4[ 565 | library(moffittverse)

566 | .leader-end-2.gray-1[use_backend("abc")]

567 | .expand-in.h-150px.gray-1.bg-gray-8.ph3[ 568 | .purple[library](DBI)
569 | .purple[library](dbplyr)
570 | .purple[library](moffittABC)
571 | con <- dbConnect(odbc::odbc(), uid = .green["fiddly"], pwd = .green["details"], ...) 572 | ]
573 | samples    <- abc_tbl("sample_inventory")
574 | patients   <- abc_tbl("patient")
575 | sample_ind <- abc_tbl("sample_indicators")

576 | samples %>% 
577 |   left_join(sample_ind, by = "SAMPLE_ID") %>% 
578 |   left_join(patients, by = "PERSON_ID") %>% 
579 |   abc_not_deleted() %>% 
580 |   abc_choice_replace() 581 | ] 582 | 583 |
Standardized,
managed connections
584 | 585 | ??? 586 | 587 | and behind the scenes `use_backend()` 588 | loads additional packages like `dbplyr` 589 | as well as a package specifically for this back-end, 590 | ▶️ `moffittABC` 591 | 592 | Each backend package has two primary goals, 593 | the first is to simplify access. 594 | 595 | So by default, moffittABC will not only 596 | ▶️ remember the incancantations required to connect 597 | to the ABC database 598 | 599 | but it will actually manage the connection for users internally 600 | 601 | --- 602 | class: animated-slide fadeIn 603 | 604 | 605 | .remark-code.code-example.pa2.gray-4[ 606 | library(moffittverse)

607 | use_backend("abc")

608 | .leader-end-3.gray-1[.onClickStyle.red.bg-red-8[samples]    <- abc_tbl("sample_inventory")tbl(con, dbplyr::in_schema("ABC", "SAMPLE_INVENTORY"))]
609 | .gray-1[.onClickStyle.purple.bg-purple-8[patients]   <- abc_tbl("patient")]
610 | .gray-1[.onClickStyle.yellow-1.bg-yellow-8[sample_ind] <- abc_tbl("sample_indicators")]

611 | samples %>% 
612 |   left_join(sample_ind, by = "SAMPLE_ID") %>% 
613 |   left_join(patients, by = "PERSON_ID") %>% 
614 |   abc_not_deleted() %>% 
615 |   abc_choice_replace() 616 | ] 617 | 618 |
Easy schema/table access
619 | 620 | ??? 621 | 622 | and in addition it also provides easy access to tables 623 | with functions like `abc_tbl()`. 624 | 625 | Here we use the `abc_tbl()` function to 626 | ▶️ connect to a table in our database 627 | that not at all coincidentally is a table in the ABC schema. 628 | And notice that the connection is handled for the user. 629 | 630 | So in this step we connect to the three tables we need... 631 | 632 | --- 633 | 634 | .remark-code.code-example.pa2.gray-4[ 635 | library(moffittverse)

636 | use_backend("abc")

637 | samples    <- abc_tbl("sample_inventory")
638 | patients   <- abc_tbl("patient")
639 | sample_ind <- abc_tbl("sample_indicators")

640 | .gray-1[.red.bg-red-8[samples] %>% ]
641 | .leader-end-4.gray-1[  left_join(.yellow-1.bg-yellow-8[sample_ind], by = "SAMPLE_ID") %>% ]
642 | .gray-1[  left_join(.purple.bg-purple-8[patients], by = "PERSON_ID") %>% ]
643 |   abc_not_deleted() %>% 
644 |   abc_choice_replace() 645 | ] 646 | 647 |
samples patients
648 | 649 | ??? 650 | 651 | At this point we've set up our workspace and our environment, 652 | and connected to the database and tables we need 653 | 654 | ... so we can now focus on 655 | how these tables relate to each other 656 | so we can link samples to patients 657 | through a series of left joins 658 | 659 | --- 660 | 661 | .remark-code.code-example.pa2.gray-4[ 662 | library(moffittverse)

663 | use_backend("abc")

664 | samples    <- abc_tbl("sample_inventory")
665 | patients   <- abc_tbl("patient")
666 | sample_ind <- abc_tbl("sample_indicators")

667 | samples %>% 
668 |   left_join(sample_ind, by = "SAMPLE_ID") %>% 
669 |   left_join(patients, by = "PERSON_ID") %>% 
670 | .gray-1[  abc_not_deleted() %>% ]
671 | .leader-end-5.gray-1[  abc_choice_replace()] 672 | ] 673 | 674 |
Common, tedious, or error prone
675 | 676 | ??? 677 | 678 | The final lines speak to the second goal 679 | of the back-end specific packages 680 | which is to wrap **common, tedious or error-prone** 681 | data base moves into standard functions. 682 | 683 | Here, because we're working in R, 684 | we have a lot more flexibility to write functions, 685 | and use tidyselect, tidyeval, and more 686 | to do things that would otherwise be very hard to do in SQL, like: 687 | 688 | - ▶️ applying a "not deleted" filter to all tables used in the query 689 | - ▶️ automatically look up text labels of coded values 690 | 691 | --- 692 | template: moffittverse-code-example 693 | 694 | ??? 695 | 696 | 👋👋👋 697 | 698 | Let's take a step back and reflect on this code as a whole, 699 | 700 | it's not that it's fewer lines of code, 701 | or less repetition 702 | or a question of R vs SQL ... 703 | it's that this code does a much better job 704 | explaining to humans 705 | how the data is being collected and transformed 706 | 707 | There are still plenty of assumptions in this code, 708 | but as we'll see, 709 | because these functions live in R packages 710 | they bring a lot of context with them 711 | 712 | --- 713 | layout: true 714 | class: middle 715 | 716 | --- 717 | 718 | ```{r abc-choice-replace, eval=FALSE} 719 | #' Replace Coded Choice Values 720 | #' 721 | #' Uses `ABC.CHOICE` to replace integers with text labels 722 | #' 723 | #' @examples ... 724 | #' @param x A table in ABC 725 | #' @export 726 | abc_choice_replace <- function(x, ...) { 727 | 728 | choice_cols <- choice_column_info(x) 729 | 730 | for (column in choice_cols) { 731 | choice_lookup <- get_choices(column) 732 | x <- replace_choice(x, column, choice_lookup) 733 | } 734 | 735 | x 736 | } 737 | ``` 738 | 739 | ??? 740 | 741 | Let's take a look at the source of `abc_choice_replace()` function 742 | 743 | --- 744 | 745 | ```{r abc-choice-replace-2, eval=FALSE} 746 | #' Replace Coded Choice Values 747 | #' 748 | #' Uses `ABC.CHOICE` to replace integers with text labels 749 | #' 750 | #' @examples ... 751 | #' @param x A table in ABC 752 | #' @export 753 | abc_choice_replace <- function(x, ...) { #<< 754 | 755 | choice_cols <- choice_column_info(x) 756 | 757 | for (column in choice_cols) { 758 | choice_lookup <- get_choices(column) 759 | x <- replace_choice(x, column, choice_lookup) 760 | } 761 | 762 | x 763 | } 764 | ``` 765 | 766 | ??? 767 | 768 | We've already seen how the naming conventions 769 | and the functions' API communicate intent: 770 | 771 | "...and then we replace choices..." 772 | 773 | but on top of that, 774 | the function name is chosen to aid discoverability. 775 | 776 | in other words, 777 | a user can easily find other functions 778 | that operate on _choice columns_ 779 | by exploring autocomplete in RStudio 780 | 781 | --- 782 | class: middle 783 | 784 | ```{r abc-choice-replace-4, eval=FALSE} 785 | #' Replace Coded Choice Values #<< 786 | #' #<< 787 | #' Uses `ABC.CHOICE` to replace integers with text labels #<< 788 | #' #<< 789 | #' @examples ... #<< 790 | #' @param x A table in ABC #<< 791 | #' @export #<< 792 | abc_choice_replace <- function(x, ...) 793 | 794 | choice_cols <- choice_column_info(x) 795 | 796 | for (column in choice_cols) { 797 | choice_lookup <- get_choices(column) 798 | x <- replace_choice(x, column, choice_lookup) 799 | } 800 | 801 | x 802 | } 803 | ``` 804 | 805 | ??? 806 | 807 | Because this is a function in an R package, 808 | we can document **what** the function does and **why** 809 | right next to the code... 810 | 811 | And the documentation is comfortably available 812 | right inside the data analysis environment 813 | 814 | -- 815 | 816 | 817 | 818 | --- 819 | 820 | 821 | 822 | ```{r abc-choice-replace-3, eval=FALSE} 823 | #' Replace Coded Choice Values 824 | #' 825 | #' Uses `ABC.CHOICE` to replace integers with text labels 826 | #' 827 | #' @examples ... 828 | #' @param x A table in ABC 829 | #' @export 830 | abc_choice_replace <- function(x, ...) { 831 | 832 | choice_cols <- choice_column_info(x) #<< 833 | #<< 834 | for (column in choice_cols) { #<< 835 | choice_lookup <- get_choices(column) #<< 836 | x <- replace_choice(x, column, choice_lookup) #<< 837 | } #<< 838 | #<< 839 | x #<< 840 | } 841 | ``` 842 | 843 | ??? 844 | 845 | The body of the function 846 | can be considered **technical documentation** 847 | recording **how** the function works. 848 | 849 | It's more precise than **just a description** of the best practice, 850 | and we've learned that 851 | when interfacing with more technical teams, 852 | the function becomes the **specification** 853 | for how we accomplish tasks, 854 | which makes it easy to say to engineering: 855 | _this is what we do_ or 856 | _this is what the new platform needs to support_. 857 | 858 | --- 859 | 860 | ```{r abc-choice-replace-5, eval=FALSE} 861 | #' Replace Coded Choice Values 862 | #' 863 | #' Uses `ABC.CHOICE` to replace integers with text labels 864 | #' 865 | #' @examples ... 866 | #' @param x A table in ABC 867 | #' @export 868 | abc_choice_replace <- function(x, ...) { 869 | 870 | choice_cols <- choice_column_info(x) 871 | 872 | for (column in choice_cols) { 873 | choice_lookup <- get_choices(column) 874 | x <- replace_choice(x, column, choice_lookup) 875 | } 876 | 877 | x 878 | } 879 | ``` 880 | 881 | ??? 882 | 883 | So taking a step back, 884 | this function isn't **just** about making life easier 885 | for someone working with this data... 886 | 887 | it's also a self-contained unit of knowledge 888 | 889 | In this view, 890 | an R package isn't just a place to keep code, 891 | it's where store best practices 892 | or lessons learned 893 | and it's how you share that knowledge 894 | with others on your team 895 | 896 | --- 897 | layout: false 898 | class: center 899 | 900 |
901 | 902 | ??? 903 | 904 | In fancy websites! Seriously! 905 | 906 | R's tooling for package development 907 | is really quite amazing. 908 | 909 | Tools like `pkgdown` 910 | don't just make your code documentation pretty 911 | and browseable 912 | and shareable 913 | 914 | they make your package documentation 915 | a viable knowledge repository 916 | and a place to turn 917 | when you need to learn something new 918 | 919 | 920 | --- 921 | layout: false 922 | class: animated slideInRight 923 | 924 |
925 | 926 | ??? 927 | 928 | On top of this, 929 | if you using version control front-ends, 930 | like GitHub or GitLab, 931 | you also have a public place for sharing knowledge. 932 | 933 | Rather than sending emails 934 | that are only seen by the people copied in the email, 935 | I can open an issue 936 | 937 | --- 938 | class: animated fadeIn 939 | 940 |
941 | 942 | ??? 943 | 944 | where my question is seen by others, 945 | answered publicly, 946 | and available for reference in the future.. 947 | 948 | So I'd like to close with a few practical tips 949 | about how to make this happen for your organization and teams 950 | 951 | --- 952 | layout: true 953 | class: bigger highlight-last-item 954 | 955 | # Tips 956 | 957 | 958 | 959 | --- 960 | 961 | - .green[✔] **Do** start small 962 | 963 | ??? 964 | 965 | Start small 966 | 967 | Start with one team and make their lives better! 968 | 969 | I can guarantee that if you look for it 970 | you will find a painfully manual process 971 | just waiting for a hero like you 972 | 973 | --- 974 | 975 | - .green[✔] **Do** start small 976 | 977 | - .green[✔] **Do** stay small 978 | 979 | ??? 980 | 981 | Stay small 982 | 983 | Rather than throwing everything into 984 | a big, monolithic package 985 | I've had success creating smaller, focused packages 986 | 987 | this makes it easy to experiment 988 | and to provide targeted solutions 989 | 990 | --- 991 | 992 | - .green[✔] **Do** start small 993 | 994 | - .green[✔] **Do** stay small 995 | 996 | - .green[✔] **Do** use vignettes 997 | 998 | ??? 999 | 1000 | My next tip is to use vignettes. 1001 | 1002 | Vignettes are a great way to document and share 1003 | processes that aren't easily captured 1004 | in a single function 1005 | or even in R code. 1006 | 1007 | For example, 1008 | I've used vignettes to document 1009 | database driver setup and configuration 1010 | or whole-game analysis examples 1011 | 1012 | --- 1013 | 1014 | - .green[✔] **Do** start small 1015 | 1016 | - .green[✔] **Do** stay small 1017 | 1018 | - .green[✔] **Do** use vignettes 1019 | 1020 | - .green[✔] **Do** be opinionated 1021 | 1022 | ??? 1023 | 1024 | and finally, do be opinionated ... 1025 | 1026 | ... okay that came out wrong... 1027 | 1028 | --- 1029 | 1030 | - .green[✔] **Do** start small 1031 | 1032 | - .green[✔] **Do** stay small 1033 | 1034 | - .green[✔] **Do** use vignettes 1035 | 1036 | - .green[✔] **Do** be opinionated provide a happy path 1037 | 1038 | ??? 1039 | 1040 | provide a happy path 1041 | 1042 | consider that your users 1043 | are likely used to a range of workflows 1044 | so help them fall into a pit of success 1045 | by making sure the happy path 1046 | is smooth and as bump-free as possible 1047 | 1048 | --- 1049 | layout: true 1050 | class: big highlight-last-item 1051 | 1052 | # Use this 1053 | 1054 | --- 1055 | 1056 | - `usethis`, `devtools`, `roxygen2`, `pkgdown`
for happy package building 1057 | 1058 | .flex.h-25[ 1059 | .w-25[ 1060 | ![](figures/usethis.png) 1061 | ] 1062 | .w-25[ 1063 | ![](figures/devtools.png) 1064 | ] 1065 | .w-25[ 1066 | ![](figures/roxygen2.png) 1067 | ] 1068 | .w-25[ 1069 | ![](figures/pkgdown.png) 1070 | ] 1071 | ] 1072 | 1073 | ??? 1074 | 1075 | None of this would be possible without 1076 | a slew of packages and resources 1077 | 1078 | key among these are 1079 | usethis and devtools for package building 1080 | and roxygen2 and pkgdown for documentation 1081 | 1082 | --- 1083 | 1084 | - `usethis`, `devtools`, `roxygen2`, `pkgdown`
for happy package building 1085 | 1086 | - .b[R Packages] by Hadley Wickham & Jenny Bryan
[r-pkgs.org](https://r-pkgs.org) 1087 | 1088 | .absolute.right-2.bottom-1.w-25[ 1089 | ![](figures/rpkgs-cover.png) 1090 | ] 1091 | 1092 | ??? 1093 | 1094 | Hadley Wickham and Jenny Bryan's 1095 | R Packages 1096 | is a great place to start learning 1097 | about building R packages 1098 | 1099 | or to turn back to when you get stuck 1100 | 1101 | --- 1102 | 1103 | - `usethis`, `devtools`, `roxygen2`, `pkgdown`
for happy package building 1104 | 1105 | - .b[R Packages] by Hadley Wickham & Jenny Bryan
[r-pkgs.org](https://r-pkgs.org) 1106 | 1107 | 1108 | - `drat` by Dirk Eddelbuettel for internal CRAN-like repos
1109 | .gray-4[or [.gray-3[RStudio Package Manager]](https://rstudio.com/products/package-manager/)] 1110 | 1111 | ??? 1112 | 1113 | We use `drat` by Dirk Eddelbuettel 1114 | to create an internal CRAN-like package repository 1115 | and it has made package installation much much better 1116 | 1117 | Another solution is RStudio's package manager 1118 | 1119 | --- 1120 | 1121 | - `usethis`, `devtools`, `roxygen2`, `pkgdown`
for happy package building 1122 | 1123 | - .b[R Packages] by Hadley Wickham & Jenny Bryan
[r-pkgs.org](https://r-pkgs.org) 1124 | 1125 | 1126 | - `drat` by Dirk Eddelbuettel for internal CRAN-like repos
1127 | .gray-4[or [.gray-3[RStudio Package Manager]](https://rstudio.com/products/package-manager/)] 1128 | 1129 | - `pkgverse` by Mike Kearney to kickstart your universe
1130 | [pkgverse.mikewk.com](https://pkgverse.mikewk.com) 1131 | 1132 | ??? 1133 | 1134 | And finally Mike Kearney's pkgverse template 1135 | made it easy to pull together 1136 | our universe of package into a cohesive unit 1137 | 1138 | --- 1139 | layout: false 1140 | class: center middle hide-count no-border city-360-bg 1141 | background-color: #FBFCFF; 1142 | 1143 |
1144 |
1145 | 1146 | .pt5.z-2.animated-slide.lightSpeedIn[ 1147 | .blue.f6[Thank you!]
1148 | [.b[build-your-own-universe].netlify.app](https://build-your-own-universe.netlify.app) 1149 | ] 1150 | 1151 | .flex.z-9999.relative.animated.fadeInUp.delay-2s.slow[ 1152 | .w-40[ 1153 | [@travisgerke]()
Travis Gerke 1154 | ] 1155 | .w-20[ 1156 |   1157 | ] 1158 | .w-40[ 1159 | [@grrrck]()
Garrick Aden-Buie 1160 | ] 1161 | ] --------------------------------------------------------------------------------