From d2f322e9e6fd7493bd0c6798805494af6f69d679 Mon Sep 17 00:00:00 2001 From: Kromvel-X Date: Mon, 6 May 2024 17:07:05 +0300 Subject: [PATCH] Added header and footer; improved navigation tracking; new template. --- docs/css/docs_style.css | 260 ++ docs/css/docs_style.min.css | 1 + docs/css/docs_style_first.css | 164 + docs/css/docs_style_first.min.css | 1 + docs/css/style.css | 88 + docs/css/style.min.css | 2 +- docs/css/style_first.min.min.css | 1 - docs/css/style_v2.css | 570 +++ docs/css/style_v2.min.css | 2 +- docs/docs/1.x/index.html | 7067 ++++++++++++++--------------- docs/docs/1.x/template.html | 179 + docs/js/nav_scroll.js | 136 + docs/js/nav_scroll.min.js | 1 + docs/js/scripts.js | 17 +- docs/js/scripts.min.js | 2 +- docs/less/docs_style.less | 314 ++ docs/less/docs_style_first.less | 185 + docs/less/style.less | 104 + docs/less/style_v2.less | 743 +++ 19 files changed, 6286 insertions(+), 3551 deletions(-) create mode 100644 docs/css/docs_style.css create mode 100644 docs/css/docs_style.min.css create mode 100644 docs/css/docs_style_first.css create mode 100644 docs/css/docs_style_first.min.css delete mode 100644 docs/css/style_first.min.min.css create mode 100644 docs/css/style_v2.css create mode 100644 docs/docs/1.x/template.html create mode 100644 docs/js/nav_scroll.js create mode 100644 docs/js/nav_scroll.min.js create mode 100644 docs/less/docs_style.less create mode 100644 docs/less/docs_style_first.less create mode 100644 docs/less/style_v2.less diff --git a/docs/css/docs_style.css b/docs/css/docs_style.css new file mode 100644 index 0000000..e41d09d --- /dev/null +++ b/docs/css/docs_style.css @@ -0,0 +1,260 @@ +a:focus { + outline: none; +} +/*.footer start*/ +.footer { + padding: 23px 0; + background: #1B1C21; + text-align: center; +} +.footer span { + font-size: 0.875rem; + line-height: 1.714; +} +/*.footer end*/ +/*mobile navigatin start*/ +.d_m_m { + padding: 30px 15px; + background: #141518; + width: 100%; + top: 70px; + left: 0; + position: absolute; + z-index: 2; + height: calc(100vh - 70px); + box-sizing: border-box; + text-align: center; + display: flex; + flex-direction: column; + justify-content: space-between; +} +.d_m_m .d_gh { + margin-top: 30px; +} +.header .d_gh { + min-width: auto; + background: #383A43; + display: inline-flex; + column-gap: 8px; + padding: 6px 10px 6px 6px; + margin-right: initial; + text-transform: none; +} +.header .d_gh:hover { + background: #6E7281; + text-decoration: none; +} +.header .d_gh:active { + background: #fff; +} +.header .d_gh:active svg path { + fill: #333333; +} +.header .d_nav a { + font-size: 0.9375rem; + line-height: 1.2; +} +.header .d_nav ul { + list-style: none; + padding: 0; + margin: 0; +} +.header .d_nav ul li { + text-align: center; +} +.header .d_nav ul li + li { + margin-top: 38px; +} +.header .d_nav .d_gh { + justify-content: center; + width: 100%; + max-width: 330px; + padding: 13px; + box-sizing: border-box; +} +.d_hbrg.d_active i { + background-color: transparent; +} +.d_hbrg.d_active i:before { + top: 0; + width: 21px; + right: 0; + -webkit-transform: rotate(45deg); + -ms-transform: rotate(45deg); + transform: rotate(45deg); +} +.d_hbrg.d_active i::after { + bottom: 0; + width: 21px; + right: 0; + -webkit-transform: rotate(-45deg); + -ms-transform: rotate(-45deg); + transform: rotate(-45deg); +} +/*mobile navigatin end*/ +@media (min-width: 768px) { + #toc { + overflow: auto; + scroll-behavior: smooth; + margin-bottom: 35px; + height: calc(100% - 16px); + } + .d_nav { + position: sticky; + max-height: calc(100vh); + left: 0; + top: 0; + } + .d_cl_1_3\@s { + flex-basis: calc((100% / 3) - 30px); + } + .d_cl_2_3\@s { + flex-basis: calc(((100% / 3) * 2) - 30px); + } + /*.header start*/ + .d_n_xs { + display: block; + } + .header ul { + display: flex; + column-gap: 30px; + list-style: none; + padding: 0; + margin: 0; + } + .header ul a { + font-size: 0.9375rem; + line-height: 1.125; + } + .header .d_logo_box { + display: flex; + justify-content: center; + } + .header .d_m_m.d_show { + display: none; + } + .d_n_s { + display: none; + } + .d_v_s { + display: flex; + } + /*.header end*/ + .d_sctn { + padding: 60px 0 202px 0px; + } + .d_rw + .d_rw { + margin-top: 50px; + } + .d_cntnr { + padding-left: 40px; + padding-right: 40px; + } + /*.footer start*/ + .footer span { + font-size: 0.9375rem; + line-height: 1.6; + } + .footer p { + font-size: 1.0625rem; + line-height: 1.411; + } + /*.footer end*/ +} +@media (min-width: 1200px) { + .d_cntnr { + padding-left: 40px; + padding-right: 40px; + } + .footer p { + text-align: center; + } +} +.header ul li a { + word-wrap: normal; +} +.header ul:not(.d_dropdown) li { + position: relative; +} +.header .d_opener { + position: relative; + padding-right: 20px; + margin-right: -20px; +} +.header .d_opener::after { + content: ''; + background-image: url("data:image/svg+xml,%3Csvg width='8' height='4' viewBox='0 0 8 4' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M4 4L3.49691e-07 1.58735e-08L8 7.15256e-07L4 4Z' fill='white'/%3E%3C/svg%3E%0A"); + position: absolute; + top: 50%; + right: 7px; + width: 8px; + height: 4px; + transform: translateY(-50%); + transition: transform 0.2s ease-in-out; +} +.header .d_opener.d_active + .d_dropdown { + display: block; +} +.header .d_opener.d_active:after { + transform: translateY(-50%) rotate(180deg); +} +.header .d_opener:hover, +.header .d_opener.d_active, +.header .d_opener:active, +.header .d_opener:focus { + color: #fff; + text-decoration: none; +} +.header .d_dropdown { + display: none; + box-sizing: border-box; +} +.header .d_dropdown > li { + padding: 8px 0; + line-height: 1; +} +.header .d_dropdown > li a { + font-size: 0.875rem; + line-height: 1.428; + text-transform: none; + font-weight: 600; +} +.header .d_nav ul.d_dropdown li + li { + margin-top: 0; +} +@media (min-width: 768px) { + .header .d_rw { + column-gap: 47px; + } + .header .d_cl_1_3\@s { + flex-basis: auto; + min-width: 250px; + } + .header .d_cl_1_3\@s.d_logo_box { + min-width: auto; + } + .header .d_opener { + margin-right: 0; + } + .header .d_dropdown { + position: absolute; + top: 52px; + left: -20px; + width: 173px; + padding: 8px 20px; + background: #fff; + box-shadow: 0px 0px 8px 0px rgba(0, 0, 0, 0.08); + } + .header .d_dropdown > li a { + color: #333333; + } +} +@media (min-width: 1200px) { + .header .d_rw { + column-gap: 30px; + } + .header .d_cl_1_3\@s { + flex-basis: calc((100% / 3) - 30px); + min-width: auto; + } +} diff --git a/docs/css/docs_style.min.css b/docs/css/docs_style.min.css new file mode 100644 index 0000000..d0a74a4 --- /dev/null +++ b/docs/css/docs_style.min.css @@ -0,0 +1 @@ +a:focus{outline:0}.footer{padding:23px 0;background:#1b1c21;text-align:center}.footer span{font-size:.875rem;line-height:1.714}.d_m_m{padding:30px 15px;background:#141518;width:100%;top:70px;left:0;position:absolute;z-index:2;height:calc(100vh - 70px);box-sizing:border-box;text-align:center;display:flex;flex-direction:column;justify-content:space-between}.d_m_m .d_gh{margin-top:30px}.header .d_gh{min-width:auto;background:#383a43;display:inline-flex;column-gap:8px;padding:6px 10px 6px 6px;margin-right:initial;text-transform:none}.header .d_gh:hover{background:#6e7281;text-decoration:none}.header .d_gh:active{background:#fff}.header .d_gh:active svg path{fill:#333}.header .d_nav a{font-size:.9375rem;line-height:1.2}.header .d_nav ul{list-style:none;padding:0;margin:0}.header .d_nav ul li{text-align:center}.header .d_nav ul li+li{margin-top:38px}.header .d_nav .d_gh{justify-content:center;width:100%;max-width:330px;padding:13px;box-sizing:border-box}.d_hbrg.d_active i{background-color:transparent}.d_hbrg.d_active i:before{top:0;width:21px;right:0;-webkit-transform:rotate(45deg);-ms-transform:rotate(45deg);transform:rotate(45deg)}.d_hbrg.d_active i::after{bottom:0;width:21px;right:0;-webkit-transform:rotate(-45deg);-ms-transform:rotate(-45deg);transform:rotate(-45deg)}@media (min-width:768px){#toc{overflow:auto;scroll-behavior:smooth;margin-bottom:35px;height:calc(100% - 16px)}.d_nav{position:sticky;max-height:calc(100vh);left:0;top:0}.d_cl_1_3\@s{flex-basis:calc((100% / 3) - 30px)}.d_cl_2_3\@s{flex-basis:calc(((100% / 3) * 2) - 30px)}.d_n_xs{display:block}.header ul{display:flex;column-gap:30px;list-style:none;padding:0;margin:0}.header ul a{font-size:.9375rem;line-height:1.125}.header .d_logo_box{display:flex;justify-content:center}.header .d_m_m.d_show{display:none}.d_n_s{display:none}.d_v_s{display:flex}.d_sctn{padding:60px 0 202px 0}.d_rw+.d_rw{margin-top:50px}.d_cntnr{padding-left:40px;padding-right:40px}.footer span{font-size:.9375rem;line-height:1.6}.footer p{font-size:1.0625rem;line-height:1.411}}@media (min-width:1200px){.d_cntnr{padding-left:40px;padding-right:40px}.footer p{text-align:center}}.header ul li a{word-wrap:normal}.header ul:not(.d_dropdown) li{position:relative}.header .d_opener{position:relative;padding-right:20px;margin-right:-20px}.header .d_opener::after{content:'';background-image:url("data:image/svg+xml,%3Csvg width='8' height='4' viewBox='0 0 8 4' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M4 4L3.49691e-07 1.58735e-08L8 7.15256e-07L4 4Z' fill='white'/%3E%3C/svg%3E%0A");position:absolute;top:50%;right:7px;width:8px;height:4px;transform:translateY(-50%);transition:transform .2s ease-in-out}.header .d_opener.d_active+.d_dropdown{display:block}.header .d_opener.d_active:after{transform:translateY(-50%) rotate(180deg)}.header .d_opener.d_active,.header .d_opener:active,.header .d_opener:focus,.header .d_opener:hover{color:#fff;text-decoration:none}.header .d_dropdown{display:none;box-sizing:border-box}.header .d_dropdown>li{padding:8px 0;line-height:1}.header .d_dropdown>li a{font-size:.875rem;line-height:1.428;text-transform:none;font-weight:600}.header .d_nav ul.d_dropdown li+li{margin-top:0}@media (min-width:768px){.header .d_rw{column-gap:47px}.header .d_cl_1_3\@s{flex-basis:auto;min-width:250px}.header .d_cl_1_3\@s.d_logo_box{min-width:auto}.header .d_opener{margin-right:0}.header .d_dropdown{position:absolute;top:52px;left:-20px;width:173px;padding:8px 20px;background:#fff;box-shadow:0 0 8px 0 rgba(0,0,0,.08)}.header .d_dropdown>li a{color:#333}}@media (min-width:1200px){.header .d_rw{column-gap:30px}.header .d_cl_1_3\@s{flex-basis:calc((100% / 3) - 30px);min-width:auto}} \ No newline at end of file diff --git a/docs/css/docs_style_first.css b/docs/css/docs_style_first.css new file mode 100644 index 0000000..bdfb38b --- /dev/null +++ b/docs/css/docs_style_first.css @@ -0,0 +1,164 @@ +@font-face { + font-family: "Lato"; + src: url("../../fonts/Lato-Bold.woff2") format("woff2"), url("../../fonts/Lato-Bold.woff") format("woff"); + font-weight: bold; + font-style: normal; + font-display: swap; +} +@font-face { + font-family: "Lato"; + src: url("../../fonts/Lato-Regular.woff2") format("woff2"), url("../../fonts/Lato-Regular.woff") format("woff"); + font-weight: normal; + font-style: normal; + font-display: swap; +} +body { + margin: 0; +} +/*Main start*/ +.header, +.footer { + font-family: "Lato", sans-serif; + font-size: 16px; + font-weight: 400; + line-height: 1.5; + -webkit-text-size-adjust: 100%; + background: #141518; + color: #fff; + position: relative; + z-index: 9999; +} +.d_cntnr { + display: flow-root; + box-sizing: content-box; + max-width: 1120px; + margin-left: auto; + margin-right: auto; + padding-left: 15px; + padding-right: 15px; +} +.d_rw { + display: flex; + flex-wrap: wrap; + column-gap: 30px; + row-gap: 30px; + box-sizing: border-box; +} +.d_rw + .d_rw { + margin-top: 40px; +} +[class^="d_cl"] { + flex: 1 1 100%; + box-sizing: border-box; +} +.d_cl_a { + flex-grow: 0; + flex-shrink: 0; + flex-basis: auto; +} +.d_cl_expand { + flex: 1; +} +.d_n_xs { + display: none; +} +button { + background: none; + border: none; + padding: 0; +} +button:hover { + cursor: pointer; +} +.header { + margin-bottom: 40px; +} +.header a { + text-decoration: none; + color: #fff; + text-transform: uppercase; + font-weight: 600; +} +.header a:hover { + color: #fff; + text-decoration: underline; +} +.d_content h1 { + margin-top: 20px; +} +.d_btn { + margin: 0; + color: #fff; + display: inline-block; + font-size: 1rem; + line-height: 1.5; + font-weight: 700; + text-align: center; + text-decoration: none; + padding: 10px; + background: #1B1C21; + transition: background 0.3s ease-in-out; + margin-left: auto; + margin-right: auto; + border-radius: 40px; + min-width: 246px; +} +.d_btn:hover { + background: #383A43; +} +.d_btn:active { + background: #fff; + color: #333; +} +/*Main end*/ +/*.header start*/ +.header { + padding: 21px 0; + background: #1B1C21; +} +.header .d_rw { + justify-content: space-between; + align-items: center; +} +.header .d_m_m { + display: none; +} +.header .d_m_m.d_show { + display: flex; +} +.d_logo_box a { + display: flex; +} +.d_hbrg { + padding: 6px 0; + display: flex; +} +.d_hbrg i, +.d_hbrg i:after, +.d_hbrg i:before { + transition: all 0.2s ease-out; + background: #fff; + border-radius: 2px; + height: 2px; + width: 21px; +} +.d_hbrg i { + position: relative; + line-height: 1; +} +.d_hbrg i:after, +.d_hbrg i:before { + content: ""; + position: absolute; + left: 0; +} +.d_hbrg i:after { + bottom: -5px; +} +.d_hbrg i:before { + top: -5px; +} +.d_v_s { + display: none; +} +/*.header end*/ diff --git a/docs/css/docs_style_first.min.css b/docs/css/docs_style_first.min.css new file mode 100644 index 0000000..702528d --- /dev/null +++ b/docs/css/docs_style_first.min.css @@ -0,0 +1 @@ +@font-face{font-family:Lato;src:url(../../fonts/Lato-Bold.woff2) format("woff2"),url(../../fonts/Lato-Bold.woff) format("woff");font-weight:700;font-style:normal;font-display:swap}@font-face{font-family:Lato;src:url(../../fonts/Lato-Regular.woff2) format("woff2"),url(../../fonts/Lato-Regular.woff) format("woff");font-weight:400;font-style:normal;font-display:swap}body{margin:0}.footer,.header{font-family:Lato,sans-serif;font-size:16px;font-weight:400;line-height:1.5;-webkit-text-size-adjust:100%;background:#141518;color:#fff;position:relative;z-index:9999}.d_cntnr{display:flow-root;box-sizing:content-box;max-width:1120px;margin-left:auto;margin-right:auto;padding-left:15px;padding-right:15px}.d_rw{display:flex;flex-wrap:wrap;column-gap:30px;row-gap:30px;box-sizing:border-box}.d_rw+.d_rw{margin-top:40px}[class^=d_cl]{flex:1 1 100%;box-sizing:border-box}.d_cl_a{flex-grow:0;flex-shrink:0;flex-basis:auto}.d_cl_expand{flex:1}.d_n_xs{display:none}button{background:0 0;border:none;padding:0}button:hover{cursor:pointer}.header{margin-bottom:40px}.header a{text-decoration:none;color:#fff;text-transform:uppercase;font-weight:600}.header a:hover{color:#fff;text-decoration:underline}.d_content h1{margin-top:20px}.d_btn{margin:0;color:#fff;display:inline-block;font-size:1rem;line-height:1.5;font-weight:700;text-align:center;text-decoration:none;padding:10px;background:#1b1c21;transition:background .3s ease-in-out;margin-left:auto;margin-right:auto;border-radius:40px;min-width:246px}.d_btn:hover{background:#383a43}.d_btn:active{background:#fff;color:#333}.header{padding:21px 0;background:#1b1c21}.header .d_rw{justify-content:space-between;align-items:center}.header .d_m_m{display:none}.header .d_m_m.d_show{display:flex}.d_logo_box a{display:flex}.d_hbrg{padding:6px 0;display:flex}.d_hbrg i,.d_hbrg i:after,.d_hbrg i:before{transition:all .2s ease-out;background:#fff;border-radius:2px;height:2px;width:21px}.d_hbrg i{position:relative;line-height:1}.d_hbrg i:after,.d_hbrg i:before{content:"";position:absolute;left:0}.d_hbrg i:after{bottom:-5px}.d_hbrg i:before{top:-5px}.d_v_s{display:none} \ No newline at end of file diff --git a/docs/css/style.css b/docs/css/style.css index 981f3d1..f3a54a7 100644 --- a/docs/css/style.css +++ b/docs/css/style.css @@ -355,6 +355,94 @@ body.o_overflow_hidden { } /*modal end*/ } +header ul li a { + word-wrap: normal; +} +header ul:not(.d_dropdown) li { + position: relative; +} +header .d_opener { + position: relative; + padding-right: 20px; + margin-right: -20px; +} +header .d_opener::after { + content: ''; + background-image: url("data:image/svg+xml,%3Csvg width='8' height='4' viewBox='0 0 8 4' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M4 4L3.49691e-07 1.58735e-08L8 7.15256e-07L4 4Z' fill='white'/%3E%3C/svg%3E%0A"); + position: absolute; + top: 50%; + right: 7px; + width: 8px; + height: 4px; + transform: translateY(-50%); + transition: transform 0.2s ease-in-out; +} +header .d_opener.d_active + .d_dropdown { + display: block; +} +header .d_opener.d_active:after { + transform: translateY(-50%) rotate(180deg); +} +header .d_opener:hover, +header .d_opener.d_active, +header .d_opener:active, +header .d_opener:focus { + color: #fff; + text-decoration: none; +} +header .d_dropdown { + display: none; + box-sizing: border-box; +} +header .d_dropdown > li { + padding: 8px 0; + line-height: 1; +} +header .d_dropdown > li a { + font-size: 0.875rem; + line-height: 1.428; + text-transform: none; + font-weight: 600; +} +header .d_nav ul.d_dropdown li + li { + margin-top: 0; +} +@media (min-width: 768px) { + header .d_rw { + column-gap: 47px; + } + header .d_cl_1_3\@s { + flex-basis: auto; + min-width: 250px; + } + header .d_cl_1_3\@s.d_logo_box { + min-width: auto; + } + header .d_opener { + margin-right: 0; + } + header .d_dropdown { + position: absolute; + top: 52px; + left: -20px; + width: 173px; + padding: 8px 20px; + background: #fff; + box-shadow: 0px 0px 8px 0px rgba(0, 0, 0, 0.08); + } + header .d_dropdown > li a { + color: #333333; + } +} +@media (min-width: 1200px) { + header .d_rw { + column-gap: 30px; + } + header .d_cl_1_3\@s { + flex-basis: calc((100% / 3) - 30px); + min-width: auto; + } +} main, header, footer { diff --git a/docs/css/style.min.css b/docs/css/style.min.css index c213925..7dd82ce 100644 --- a/docs/css/style.min.css +++ b/docs/css/style.min.css @@ -1 +1 @@ -.d_v .d_btn{display:block;padding:18px 30px 18px 70px;position:relative}.d_v .d_btn::before{content:'';position:absolute;top:10px;left:10px;background-image:url("data:image/svg+xml,%3Csvg width='40' height='40' viewBox='0 0 40 40' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Ccircle cx='20' cy='20' r='20' fill='%2334C759'/%3E%3Cpath d='M27.7456 18.2765C29.0576 19.0503 29.0577 20.9482 27.7456 21.722L16.3494 28.4428C15.0162 29.2291 13.3334 28.2679 13.3334 26.7201L13.3334 13.2784C13.3334 11.7306 15.0162 10.7694 16.3494 11.5557L27.7456 18.2765Z' fill='white'/%3E%3C/svg%3E%0A");width:40px;height:40px}.d_v .d_btn.o_modal_open:active{background:#34c759;color:#fff}.d_v .d_btn.o_modal_open:active::before{background-image:url("data:image/svg+xml,%3Csvg width='40' height='40' viewBox='0 0 40 40' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Ccircle cx='20' cy='20' r='20' fill='%23fff'/%3E%3Cpath d='M27.7456 18.2765C29.0576 19.0503 29.0577 20.9482 27.7456 21.722L16.3494 28.4428C15.0162 29.2291 13.3334 28.2679 13.3334 26.7201L13.3334 13.2784C13.3334 11.7306 15.0162 10.7694 16.3494 11.5557L27.7456 18.2765Z' fill='%2334C759'/%3E%3C/svg%3E%0A")}p{margin:0}p:not(:first-child){margin-top:10px}.d_desc_box a{text-transform:none;color:#34c759}footer{padding:23px 0;background:#1b1c21;text-align:center}footer span{font-size:.875rem;line-height:1.714}.d_m_m{padding:30px 15px;background:#141518;width:100%;top:70px;left:0;position:absolute;z-index:2;height:calc(100% - 70px);box-sizing:border-box;text-align:center;display:flex;flex-direction:column;justify-content:space-between}.d_m_m .d_gh{margin-top:30px}header .d_gh{min-width:auto;background:#383a43;display:inline-flex;column-gap:8px;padding:6px 10px 6px 6px;margin-right:initial;text-transform:none}header .d_gh:hover{background:#6e7281;text-decoration:none}header .d_gh:active{background:#fff}header .d_gh:active svg path{fill:#333}header .d_nav a{font-size:.9375rem;line-height:1.2}header .d_nav ul{list-style:none;padding:0;margin:0}header .d_nav ul li{text-align:center}header .d_nav ul li+li{margin-top:38px}header .d_nav .d_gh{justify-content:center;width:100%;max-width:330px;padding:13px;box-sizing:border-box}.d_hbrg.d_active i{background-color:transparent}.d_hbrg.d_active i:before{top:0;width:21px;right:0;-webkit-transform:rotate(45deg);-ms-transform:rotate(45deg);transform:rotate(45deg)}.d_hbrg.d_active i::after{bottom:0;width:21px;right:0;-webkit-transform:rotate(-45deg);-ms-transform:rotate(-45deg);transform:rotate(-45deg)}body.o_overflow_hidden{overflow:hidden}.o_modal{padding:0;position:fixed;top:0;left:0;bottom:0;right:0;background:rgba(0,0,0,.6);z-index:-1;overflow-y:auto;flex:1 1 auto}.o_modal .d_cntnr{overflow:hidden;position:relative;padding:15px;box-sizing:content-box;flex:1 1 auto;margin-top:auto;margin-bottom:auto}.o_modal .o_body_modal{padding:8px 5px;background:#1b1c21;border-radius:10px}.o_modal .o_body_modal .o_iframe_box{position:relative;overflow:hidden}.o_modal .o_body_modal .o_iframe_box .o_placeholder{position:absolute;width:100%;top:0;bottom:0;margin:auto;height:100%;object-fit:cover}.o_modal .o_body_modal .o_iframe_box::before{content:'';display:block;padding-top:56.25%}.o_modal .o_body_modal iframe{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0;z-index:2}.o_modal .o_close{outline:0;padding:0;border:none;position:absolute;right:0;top:0;background:url(../images/icons/Cross.svg),#34c759;background-size:12px;background-position:center;background-repeat:no-repeat;width:28px;height:28px;z-index:3;border-radius:50%}.o_modal.o_open{z-index:999999999999;display:flex;animation:blur .4s cubic-bezier(0,.55,.45,1) both}.o_modal.o_open .o_container{opacity:1}@keyframes blur{0%{backdrop-filter:blur(0)}100%{backdrop-filter:blur(1px)}}@media (min-width:768px){.d_cl_1_3\@s{flex-basis:calc((100% / 3) - 30px)}.d_n_xs{display:block}header ul{display:flex;column-gap:30px;list-style:none;padding:0;margin:0}header ul a{font-size:.9375rem;line-height:1.125}header .d_logo_box{display:flex;justify-content:center}header .d_m_m.d_show{display:none}.d_n_s{display:none}.d_v_s{display:flex}h1{font-size:1.875rem;line-height:1.333}.d_sctn{padding:60px 0 202px 0}.d_rw+.d_rw{margin-top:50px}.d_cntnr{padding-left:40px;padding-right:40px}p{font-size:1.0625rem;line-height:1.411}.d_crd{padding:30px}.d_crd img{max-width:100%;height:auto}.d_crd:not(:last-child){position:relative}.d_crd:not(:last-child)::before{content:'';width:28px;height:24px;left:calc(100% + 6px);transform:translateY(50%);bottom:50%}footer span{font-size:.9375rem;line-height:1.6}.o_modal .d_cntnr{padding:40px}.o_modal .o_body_modal{padding:16px 10px}.o_modal .o_close{background-size:18px;right:18px;top:18px;width:44px;height:44px}}@media (min-width:1200px){.d_sctn{padding:92px 0 102px 0}.d_cntnr{padding-left:40px;padding-right:40px}p{text-align:center}.d_intro{max-width:757px;margin-inline:auto;text-align:center}.d_crd{padding:45px}.d_crd:not(:last-child){position:relative}.d_crd:not(:last-child)::before{content:'';width:30px;height:36px;left:calc(100%);transform:translateY(50%);bottom:50%}.d_desc_box .d_cl{max-width:927px;margin-inline:auto}.o_modal .d_cntnr{padding:40px}.o_modal .o_body_modal{padding:26px 16px}}footer,header,main{display:block}.o_preload{display:none} \ No newline at end of file +.d_v .d_btn{display:block;padding:18px 30px 18px 70px;position:relative}.d_v .d_btn::before{content:'';position:absolute;top:10px;left:10px;background-image:url("data:image/svg+xml,%3Csvg width='40' height='40' viewBox='0 0 40 40' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Ccircle cx='20' cy='20' r='20' fill='%2334C759'/%3E%3Cpath d='M27.7456 18.2765C29.0576 19.0503 29.0577 20.9482 27.7456 21.722L16.3494 28.4428C15.0162 29.2291 13.3334 28.2679 13.3334 26.7201L13.3334 13.2784C13.3334 11.7306 15.0162 10.7694 16.3494 11.5557L27.7456 18.2765Z' fill='white'/%3E%3C/svg%3E%0A");width:40px;height:40px}.d_v .d_btn.o_modal_open:active{background:#34c759;color:#fff}.d_v .d_btn.o_modal_open:active::before{background-image:url("data:image/svg+xml,%3Csvg width='40' height='40' viewBox='0 0 40 40' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Ccircle cx='20' cy='20' r='20' fill='%23fff'/%3E%3Cpath d='M27.7456 18.2765C29.0576 19.0503 29.0577 20.9482 27.7456 21.722L16.3494 28.4428C15.0162 29.2291 13.3334 28.2679 13.3334 26.7201L13.3334 13.2784C13.3334 11.7306 15.0162 10.7694 16.3494 11.5557L27.7456 18.2765Z' fill='%2334C759'/%3E%3C/svg%3E%0A")}p{margin:0}p:not(:first-child){margin-top:10px}.d_desc_box a{text-transform:none;color:#34c759}footer{padding:23px 0;background:#1b1c21;text-align:center}footer span{font-size:.875rem;line-height:1.714}.d_m_m{padding:30px 15px;background:#141518;width:100%;top:70px;left:0;position:absolute;z-index:2;height:calc(100% - 70px);box-sizing:border-box;text-align:center;display:flex;flex-direction:column;justify-content:space-between}.d_m_m .d_gh{margin-top:30px}header .d_gh{min-width:auto;background:#383a43;display:inline-flex;column-gap:8px;padding:6px 10px 6px 6px;margin-right:initial;text-transform:none}header .d_gh:hover{background:#6e7281;text-decoration:none}header .d_gh:active{background:#fff}header .d_gh:active svg path{fill:#333}header .d_nav a{font-size:.9375rem;line-height:1.2}header .d_nav ul{list-style:none;padding:0;margin:0}header .d_nav ul li{text-align:center}header .d_nav ul li+li{margin-top:38px}header .d_nav .d_gh{justify-content:center;width:100%;max-width:330px;padding:13px;box-sizing:border-box}.d_hbrg.d_active i{background-color:transparent}.d_hbrg.d_active i:before{top:0;width:21px;right:0;-webkit-transform:rotate(45deg);-ms-transform:rotate(45deg);transform:rotate(45deg)}.d_hbrg.d_active i::after{bottom:0;width:21px;right:0;-webkit-transform:rotate(-45deg);-ms-transform:rotate(-45deg);transform:rotate(-45deg)}body.o_overflow_hidden{overflow:hidden}.o_modal{padding:0;position:fixed;top:0;left:0;bottom:0;right:0;background:rgba(0,0,0,.6);z-index:-1;overflow-y:auto;flex:1 1 auto}.o_modal .d_cntnr{overflow:hidden;position:relative;padding:15px;box-sizing:content-box;flex:1 1 auto;margin-top:auto;margin-bottom:auto}.o_modal .o_body_modal{padding:8px 5px;background:#1b1c21;border-radius:10px}.o_modal .o_body_modal .o_iframe_box{position:relative;overflow:hidden}.o_modal .o_body_modal .o_iframe_box .o_placeholder{position:absolute;width:100%;top:0;bottom:0;margin:auto;height:100%;object-fit:cover}.o_modal .o_body_modal .o_iframe_box::before{content:'';display:block;padding-top:56.25%}.o_modal .o_body_modal iframe{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0;z-index:2}.o_modal .o_close{outline:0;padding:0;border:none;position:absolute;right:0;top:0;background:url(../images/icons/Cross.svg),#34c759;background-size:12px;background-position:center;background-repeat:no-repeat;width:28px;height:28px;z-index:3;border-radius:50%}.o_modal.o_open{z-index:999999999999;display:flex;animation:blur .4s cubic-bezier(0,.55,.45,1) both}.o_modal.o_open .o_container{opacity:1}@keyframes blur{0%{backdrop-filter:blur(0)}100%{backdrop-filter:blur(1px)}}@media (min-width:768px){.d_cl_1_3\@s{flex-basis:calc((100% / 3) - 30px)}.d_n_xs{display:block}header ul{display:flex;column-gap:30px;list-style:none;padding:0;margin:0}header ul a{font-size:.9375rem;line-height:1.125}header .d_logo_box{display:flex;justify-content:center}header .d_m_m.d_show{display:none}.d_n_s{display:none}.d_v_s{display:flex}h1{font-size:1.875rem;line-height:1.333}.d_sctn{padding:60px 0 202px 0}.d_rw+.d_rw{margin-top:50px}.d_cntnr{padding-left:40px;padding-right:40px}p{font-size:1.0625rem;line-height:1.411}.d_crd{padding:30px}.d_crd img{max-width:100%;height:auto}.d_crd:not(:last-child){position:relative}.d_crd:not(:last-child)::before{content:'';width:28px;height:24px;left:calc(100% + 6px);transform:translateY(50%);bottom:50%}footer span{font-size:.9375rem;line-height:1.6}.o_modal .d_cntnr{padding:40px}.o_modal .o_body_modal{padding:16px 10px}.o_modal .o_close{background-size:18px;right:18px;top:18px;width:44px;height:44px}}@media (min-width:1200px){.d_sctn{padding:92px 0 102px 0}.d_cntnr{padding-left:40px;padding-right:40px}p{text-align:center}.d_intro{max-width:757px;margin-inline:auto;text-align:center}.d_crd{padding:45px}.d_crd:not(:last-child){position:relative}.d_crd:not(:last-child)::before{content:'';width:30px;height:36px;left:calc(100%);transform:translateY(50%);bottom:50%}.d_desc_box .d_cl{max-width:927px;margin-inline:auto}.o_modal .d_cntnr{padding:40px}.o_modal .o_body_modal{padding:26px 16px}}header ul li a{word-wrap:normal}header ul:not(.d_dropdown) li{position:relative}header .d_opener{position:relative;padding-right:20px;margin-right:-20px}header .d_opener::after{content:'';background-image:url("data:image/svg+xml,%3Csvg width='8' height='4' viewBox='0 0 8 4' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M4 4L3.49691e-07 1.58735e-08L8 7.15256e-07L4 4Z' fill='white'/%3E%3C/svg%3E%0A");position:absolute;top:50%;right:7px;width:8px;height:4px;transform:translateY(-50%);transition:transform .2s ease-in-out}header .d_opener.d_active+.d_dropdown{display:block}header .d_opener.d_active:after{transform:translateY(-50%) rotate(180deg)}header .d_opener.d_active,header .d_opener:active,header .d_opener:focus,header .d_opener:hover{color:#fff;text-decoration:none}header .d_dropdown{display:none;box-sizing:border-box}header .d_dropdown>li{padding:8px 0;line-height:1}header .d_dropdown>li a{font-size:.875rem;line-height:1.428;text-transform:none;font-weight:600}header .d_nav ul.d_dropdown li+li{margin-top:0}@media (min-width:768px){header .d_rw{column-gap:47px}header .d_cl_1_3\@s{flex-basis:auto;min-width:250px}header .d_cl_1_3\@s.d_logo_box{min-width:auto}header .d_opener{margin-right:0}header .d_dropdown{position:absolute;top:52px;left:-20px;width:173px;padding:8px 20px;background:#fff;box-shadow:0 0 8px 0 rgba(0,0,0,.08)}header .d_dropdown>li a{color:#333}}@media (min-width:1200px){header .d_rw{column-gap:30px}header .d_cl_1_3\@s{flex-basis:calc((100% / 3) - 30px);min-width:auto}}footer,header,main{display:block}.o_preload{display:none} \ No newline at end of file diff --git a/docs/css/style_first.min.min.css b/docs/css/style_first.min.min.css deleted file mode 100644 index 9d0c0ac..0000000 --- a/docs/css/style_first.min.min.css +++ /dev/null @@ -1 +0,0 @@ -@font-face{font-family:Lato;src:url(fonts/Lato-Black.woff2) format("woff2"),url(fonts/Lato-Black.woff) format("woff");font-weight:900;font-style:normal;font-display:swap}@font-face{font-family:Lato;src:url(fonts/Lato-Bold.woff2) format("woff2"),url(fonts/Lato-Bold.woff) format("woff");font-weight:700;font-style:normal;font-display:swap}@font-face{font-family:Lato;src:url(fonts/Lato-Regular.woff2) format("woff2"),url(fonts/Lato-Regular.woff) format("woff");font-weight:400;font-style:normal;font-display:swap}html{font-family:Lato,sans-serif;font-size:16px;font-weight:400;line-height:1.5;-webkit-text-size-adjust:100%;background:#141518;color:#fff}body{margin:0;display:flex;flex-direction:column;justify-content:space-between;min-height:100vh}.d_sctn{display:flow-root;box-sizing:border-box;padding:40px 0}.d_cntnr{display:flow-root;box-sizing:content-box;max-width:1120px;margin-left:auto;margin-right:auto;padding-left:15px;padding-right:15px}.d_rw{display:flex;flex-wrap:wrap;column-gap:30px;row-gap:30px;box-sizing:border-box}.d_rw+.d_rw{margin-top:40px}[class^=d_cl]{flex:1 1 100%;box-sizing:border-box}.d_cl_a{flex-grow:0;flex-shrink:0;flex-basis:auto}.d_n_xs{display:none}button{background:0 0;border:none;padding:0}button:hover{cursor:pointer}h1{font-size:1.5rem;line-height:1.333;font-weight:700;margin-top:0;margin-bottom:0}h1 strong{color:#34c759;font-weight:800}a{text-decoration:none;color:#fff;text-transform:uppercase;font-weight:600}a:hover{text-decoration:underline}.d_btn{margin:0;color:#fff;display:inline-block;font-size:1rem;line-height:1.5;font-weight:700;text-align:center;text-decoration:none;padding:10px;background:#1b1c21;transition:background .3s ease-in-out;margin-left:auto;margin-right:auto;border-radius:40px;min-width:246px}.d_btn:hover{background:#383a43}.d_btn:active{background:#fff;color:#333}header{padding:21px 0;background:#1b1c21}header .d_rw{justify-content:space-between;align-items:center}header .d_m_m{display:none}header .d_m_m.d_show{display:flex}.d_logo_box a{display:flex}.d_hbrg{padding:6px 0;display:flex}.d_hbrg i,.d_hbrg i:after,.d_hbrg i:before{transition:all .2s ease-out;background:#fff;border-radius:2px;height:2px;width:21px}.d_hbrg i{position:relative;line-height:1}.d_hbrg i:after,.d_hbrg i:before{content:"";position:absolute;left:0}.d_hbrg i:after{bottom:-5px}.d_hbrg i:before{top:-5px}.d_v_s{display:none}.d_crd{background:#1b1c21;border-radius:19px;padding:34px 40px;display:flex;justify-content:center;align-items:center}.d_crd:not(:last-child){position:relative}.d_crd:not(:last-child)::before{content:'';width:28px;height:24px;position:absolute;background-image:url("data:image/svg+xml,%3Csvg width='30' height='36' viewBox='0 0 30 36' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M8 4L22 18L8 32' stroke='%238BA98F' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E%0A");background-size:contain;background-repeat:no-repeat;left:50%;transform:translateX(-50%) rotate(90deg);bottom:-32px}.o_modal{display:none}@media (min-width:768px){footer,header,main{display:none}}@media (min-width:768px){.o_preload{z-index:999999999;position:fixed;display:flex;top:0;left:0;height:100%;width:100%;background:#141518;justify-content:space-around;align-items:center}.o_preload::after{content:'';animation:rotate 2s linear infinite;width:75px;height:75px;position:absolute;left:50%;top:50%;background-image:url("data:image/svg+xml,%3Csvg width='200' height='200' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M200 100a100 100 0 1 1-200 0 100 100 0 0 1 200 0Zm-180 0a80 80 0 1 0 160 0 80 80 0 0 0-160 0Z' fill='%23FFECE6'/%3E%3Cpath d='M190 100c6 0 10-4 10-10a100 100 0 0 0-90-90c-6 0-10 4-10 10s4 10 10 11a80 80 0 0 1 69 69c1 6 5 10 11 10Z' fill='%2334C759'/%3E%3C/svg%3E%0A");background-size:cover;transform:translate(50%,50%)}}@keyframes rotate{from{transform:rotate(0)}to{transform:rotate(360deg)}} \ No newline at end of file diff --git a/docs/css/style_v2.css b/docs/css/style_v2.css new file mode 100644 index 0000000..5841296 --- /dev/null +++ b/docs/css/style_v2.css @@ -0,0 +1,570 @@ +/* cyrillic-ext */ +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2JL7W0Q5n-wU.woff2) format("woff2"); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa0ZL7W0Q5n-wU.woff2) format("woff2"); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* greek-ext */ +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2ZL7W0Q5n-wU.woff2) format("woff2"); + unicode-range: U+1F00-1FFF; +} +/* greek */ +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa1pL7W0Q5n-wU.woff2) format("woff2"); + unicode-range: U+0370-03FF; +} +/* vietnamese */ +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2pL7W0Q5n-wU.woff2) format("woff2"); + unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; +} +/* latin-ext */ +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa25L7W0Q5n-wU.woff2) format("woff2"); + unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa1ZL7W0Q5nw.woff2) format("woff2"); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* cyrillic-ext */ +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 500; + font-display: swap; + src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2JL7W0Q5n-wU.woff2) format("woff2"); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 500; + font-display: swap; + src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa0ZL7W0Q5n-wU.woff2) format("woff2"); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* greek-ext */ +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 500; + font-display: swap; + src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2ZL7W0Q5n-wU.woff2) format("woff2"); + unicode-range: U+1F00-1FFF; +} +/* greek */ +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 500; + font-display: swap; + src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa1pL7W0Q5n-wU.woff2) format("woff2"); + unicode-range: U+0370-03FF; +} +/* vietnamese */ +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 500; + font-display: swap; + src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2pL7W0Q5n-wU.woff2) format("woff2"); + unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; +} +/* latin-ext */ +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 500; + font-display: swap; + src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa25L7W0Q5n-wU.woff2) format("woff2"); + unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 500; + font-display: swap; + src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa1ZL7W0Q5nw.woff2) format("woff2"); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* cyrillic-ext */ +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 600; + font-display: swap; + src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2JL7W0Q5n-wU.woff2) format("woff2"); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 600; + font-display: swap; + src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa0ZL7W0Q5n-wU.woff2) format("woff2"); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* greek-ext */ +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 600; + font-display: swap; + src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2ZL7W0Q5n-wU.woff2) format("woff2"); + unicode-range: U+1F00-1FFF; +} +/* greek */ +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 600; + font-display: swap; + src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa1pL7W0Q5n-wU.woff2) format("woff2"); + unicode-range: U+0370-03FF; +} +/* vietnamese */ +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 600; + font-display: swap; + src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2pL7W0Q5n-wU.woff2) format("woff2"); + unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; +} +/* latin-ext */ +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 600; + font-display: swap; + src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa25L7W0Q5n-wU.woff2) format("woff2"); + unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 600; + font-display: swap; + src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa1ZL7W0Q5nw.woff2) format("woff2"); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* cyrillic-ext */ +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2JL7W0Q5n-wU.woff2) format("woff2"); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa0ZL7W0Q5n-wU.woff2) format("woff2"); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* greek-ext */ +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2ZL7W0Q5n-wU.woff2) format("woff2"); + unicode-range: U+1F00-1FFF; +} +/* greek */ +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa1pL7W0Q5n-wU.woff2) format("woff2"); + unicode-range: U+0370-03FF; +} +/* vietnamese */ +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2pL7W0Q5n-wU.woff2) format("woff2"); + unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; +} +/* latin-ext */ +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa25L7W0Q5n-wU.woff2) format("woff2"); + unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa1ZL7W0Q5nw.woff2) format("woff2"); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +html body { + font-family: "Inter", sans-serif; + color: #333; +} +div h1, +div h2, +div h3, +div #toctitle, +div .sidebarblock > .content > .title, +div h4, +div h5, +div h6, +div #toc ul, +div .admonitionblock td.content > .title, +div .audioblock > .title, +div .exampleblock > .title, +div .imageblock > .title, +div .listingblock > .title, +div .literalblock > .title, +div .stemblock > .title, +div .openblock > .title, +div .paragraph > .title, +div .quoteblock > .title, +div table.tableblock > .title, +div .verseblock > .title, +div .videoblock > .title, +div .dlist > .title, +div .olist > .title, +div .ulist > .title, +div .qlist > .title, +div .hdlist > .title, +div .admonitionblock > table td.icon .title, +div .verseblock pre, +div .conum[data-value] { + font-family: "Inter", sans-serif; +} +div code, +div pre, +div kbd, +div samp { + font-family: monospace, monospace; +} +div .paragraph.lead > p, +div #preamble > .sectionbody > [class="paragraph"]:first-of-type p { + color: #333; +} +div h1, +div h2, +div h3, +div #toctitle, +div .sidebarblock > .content > .title, +div h4, +div h5, +div h6 { + color: #000; +} +div .sectlevel1 > li a { + color: #000; +} +div .sectlevel1 > li .sectlevel2 { + padding-right: 17px; +} +div .sectlevel1 > li .sectlevel2 > li a { + color: #333; +} +div h1, +div h2, +div h3, +div #toctitle, +div .sidebarblock > .content > .title, +div h4, +div h5, +div h6 { + font-weight: 700; + margin-bottom: 20px; +} +div p, +div .paragraph { + margin-bottom: 1.25em; +} +div p:last-child, +div .paragraph:last-child { + margin-bottom: 0; +} +#toc a { + color: #000; + font-weight: 600; + padding-top: 0.3125em; + padding-bottom: 0.3125em; +} +#toc li a { + padding-left: 0.3125em; + padding-right: 0.3125em; + display: block; +} +#toc .sectlevel2 { + margin-top: 0.25em; +} +#toc .sectlevel1 > li:first-child a { + margin-top: 0; +} +#toc .sectlevel2 a { + color: #333; + font-weight: 500; +} +#toc li a { + transition: background 250ms ease-in-out; + border-radius: 4px; + margin-top: 0.25em; +} +#toc li a:hover { + background: #f4f4f4; + cursor: pointer; +} +#toc .active { + background: #f4f4f4; +} +#toc .toc2 #toctitle { + margin-bottom: 0.9375em; +} +div.simplebar-scrollbar:before { + background: #C1C1C1; + border-radius: 0; +} +div.simplebar-scrollbar.simplebar-visible:before { + opacity: 1; +} +div .simplebar-track.simplebar-vertical { + top: 0; + width: 17px; + background: #F1F1F1; +} +div .simplebar-vertical:before { + content: ''; + position: absolute; + top: 0px; + right: 1px; + width: 15px; + height: 14px; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='7' height='7' viewBox='0 0 7 7' fill='none'%3E%3Cpath d='M8.74228e-08 2L7 2V3L6 3V4H5V5L4 5V6H3V5H2V4L1 4L1 3L0 3L8.74228e-08 2Z' fill='%23505050'/%3E%3C/svg%3E"); + background-position: center center; + background-repeat: no-repeat; + z-index: 2; + background-color: #f1f1f1; + transform: rotate(180deg); +} +div .simplebar-vertical:after { + content: ''; + position: absolute; + bottom: 0px; + right: 1px; + width: 15px; + height: 14px; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='7' height='7' viewBox='0 0 7 7' fill='none'%3E%3Cpath d='M8.74228e-08 2L7 2V3L6 3V4H5V5L4 5V6H3V5H2V4L1 4L1 3L0 3L8.74228e-08 2Z' fill='%23505050'/%3E%3C/svg%3E"); + background-position: center center; + background-repeat: no-repeat; + z-index: 2; + background-color: #f1f1f1; +} +div#header { + margin-top: 3.75em; +} +div#header .details { + color: #595959; + padding: 0; + border-bottom: 0; +} +div#header > h1:first-child { + margin-bottom: 10px; + margin-top: 0; +} +div#content { + margin-top: 1.875em; +} +div div .paragraph.lead > p, +div div#preamble > .sectionbody > [class="paragraph"]:first-of-type p { + font-size: 1.125em; + line-height: 150%; +} +div a { + color: #3553b8; + transition: color 250ms ease-in-out; +} +div a:hover, +div a:focus { + color: #333; +} +div :not(pre):not([class^="L"]) > code { + background: #efeff4; +} +div h2 { + margin-top: 1.92307em; + margin-bottom: 0.54054054em; +} +div h3 { + font-size: 1.375em; + margin-top: 1.875em; +} +div h4 { + font-size: 1.125em; +} +div p, +div blockquote, +div dt, +div td.content, +div span.alt, +div summary { + font-size: 1rem; +} +div .literalblock pre, +div .listingblock > .content > pre { + padding: 1.25em; + border-radius: 6px; +} +div code { + color: #333; +} +div .listingblock code[data-lang]::before { + top: 1.5625em; + right: 1.5625em; +} +div .listingblock code[data-lang]::before { + display: block; +} +div .CodeRay .string .content { + color: #dc143c; +} +div .admonitionblock { + border-radius: 6px; + background: #f2f5ff; + padding: 1.25em 1em; +} +div .admonitionblock :not(pre):not([class^="L"]) > code { + background: #DBE3FF; +} +div .admonitionblock > table:last-child { + margin-bottom: 0; +} +div .admonitionblock > table td.content { + border-left: 0; + padding: 0; + padding-left: 10px; + color: #212529; +} +div .admonitionblock > table td.icon { + text-align: center; + padding: 0; + width: 30px; +} +div .admonitionblock > table td.icon .icon-note { + position: relative; + width: 100%; + height: 30px; +} +div .admonitionblock > table td.icon .icon-note::before { + content: ""; + position: absolute; + width: 100%; + height: 100%; + left: 0; + top: 59%; + transform: translateY(-50%); + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30' fill='none'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M2.5 15C2.5 8.10655 8.10706 2.5 15 2.5C21.8919 2.5 27.4995 8.10655 27.5 15C27.5 21.8924 21.8924 27.5 15 27.5C8.10655 27.5 2.5 21.8924 2.5 15ZM16.1049 17.3549C15.8118 17.6479 15.4144 17.8125 15 17.8125C14.7948 17.8125 14.5916 17.7721 14.4021 17.6936C14.2125 17.615 14.0402 17.4999 13.8951 17.3549C13.7501 17.2098 13.635 17.0375 13.5564 16.8479C13.4779 16.6584 13.4375 16.4552 13.4375 16.25V9.0625C13.4375 8.6481 13.6021 8.25067 13.8951 7.95765C14.1882 7.66462 14.5856 7.5 15 7.5C15.4144 7.5 15.8118 7.66462 16.1049 7.95765C16.3979 8.25067 16.5625 8.6481 16.5625 9.0625V16.25C16.5625 16.6644 16.3979 17.0618 16.1049 17.3549ZM16.875 20.9375C16.875 21.973 16.0355 22.8125 15 22.8125C13.9645 22.8125 13.125 21.973 13.125 20.9375C13.125 19.902 13.9645 19.0625 15 19.0625C16.0355 19.0625 16.875 19.902 16.875 20.9375Z' fill='%23A4A8B8'/%3E%3C/svg%3E"); +} +div .sect1 + .sect1 { + border-top: 0; +} +div .sect1 { + padding-bottom: 0; +} +div ul, +div ol { + margin-left: 0.8em; +} +div .conum[data-value] { + background: #000; +} +div .colist td:not([class]):first-child { + padding-left: 0; + padding-right: 6px; + padding-top: 0.2em; +} +div .colist td:not([class]):last-child { + padding: 0; +} +div .colist tr:not([class]):not(:first-child) td { + padding-top: 1em; +} +div .colist tr:not([class]):not(:first-child) .conum { + margin-top: 0.3125em; +} +div .admonitionblock + .listingblock, +div .admonitionblock + .paragraph { + margin-top: 1.25em; +} +div .literalblock + .colist, +div .listingblock + .colist { + margin-top: 0; +} +div#footer { + background: #333333; +} +@media screen and (min-width: 768px) { + div #toc.toc2 { + background: #fff; + border-right: 0; + } + div h1 { + font-size: 2.25em; + } + div h2 { + font-size: 1.625em; + } + #footer { + z-index: 10001; + position: relative; + } +} diff --git a/docs/css/style_v2.min.css b/docs/css/style_v2.min.css index 68edda0..490850e 100644 --- a/docs/css/style_v2.min.css +++ b/docs/css/style_v2.min.css @@ -1 +1 @@ -@font-face{font-family:Inter;font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2JL7W0Q5n-wU.woff2) format("woff2");unicode-range:U+0460-052F,U+1C80-1C88,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:Inter;font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa0ZL7W0Q5n-wU.woff2) format("woff2");unicode-range:U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}@font-face{font-family:Inter;font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2ZL7W0Q5n-wU.woff2) format("woff2");unicode-range:U+1F00-1FFF}@font-face{font-family:Inter;font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa1pL7W0Q5n-wU.woff2) format("woff2");unicode-range:U+0370-03FF}@font-face{font-family:Inter;font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2pL7W0Q5n-wU.woff2) format("woff2");unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+0300-0301,U+0303-0304,U+0308-0309,U+0323,U+0329,U+1EA0-1EF9,U+20AB}@font-face{font-family:Inter;font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa25L7W0Q5n-wU.woff2) format("woff2");unicode-range:U+0100-02AF,U+0304,U+0308,U+0329,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Inter;font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa1ZL7W0Q5nw.woff2) format("woff2");unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Inter;font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2JL7W0Q5n-wU.woff2) format("woff2");unicode-range:U+0460-052F,U+1C80-1C88,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:Inter;font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa0ZL7W0Q5n-wU.woff2) format("woff2");unicode-range:U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}@font-face{font-family:Inter;font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2ZL7W0Q5n-wU.woff2) format("woff2");unicode-range:U+1F00-1FFF}@font-face{font-family:Inter;font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa1pL7W0Q5n-wU.woff2) format("woff2");unicode-range:U+0370-03FF}@font-face{font-family:Inter;font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2pL7W0Q5n-wU.woff2) format("woff2");unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+0300-0301,U+0303-0304,U+0308-0309,U+0323,U+0329,U+1EA0-1EF9,U+20AB}@font-face{font-family:Inter;font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa25L7W0Q5n-wU.woff2) format("woff2");unicode-range:U+0100-02AF,U+0304,U+0308,U+0329,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Inter;font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa1ZL7W0Q5nw.woff2) format("woff2");unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Inter;font-style:normal;font-weight:600;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2JL7W0Q5n-wU.woff2) format("woff2");unicode-range:U+0460-052F,U+1C80-1C88,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:Inter;font-style:normal;font-weight:600;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa0ZL7W0Q5n-wU.woff2) format("woff2");unicode-range:U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}@font-face{font-family:Inter;font-style:normal;font-weight:600;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2ZL7W0Q5n-wU.woff2) format("woff2");unicode-range:U+1F00-1FFF}@font-face{font-family:Inter;font-style:normal;font-weight:600;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa1pL7W0Q5n-wU.woff2) format("woff2");unicode-range:U+0370-03FF}@font-face{font-family:Inter;font-style:normal;font-weight:600;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2pL7W0Q5n-wU.woff2) format("woff2");unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+0300-0301,U+0303-0304,U+0308-0309,U+0323,U+0329,U+1EA0-1EF9,U+20AB}@font-face{font-family:Inter;font-style:normal;font-weight:600;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa25L7W0Q5n-wU.woff2) format("woff2");unicode-range:U+0100-02AF,U+0304,U+0308,U+0329,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Inter;font-style:normal;font-weight:600;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa1ZL7W0Q5nw.woff2) format("woff2");unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Inter;font-style:normal;font-weight:700;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2JL7W0Q5n-wU.woff2) format("woff2");unicode-range:U+0460-052F,U+1C80-1C88,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:Inter;font-style:normal;font-weight:700;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa0ZL7W0Q5n-wU.woff2) format("woff2");unicode-range:U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}@font-face{font-family:Inter;font-style:normal;font-weight:700;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2ZL7W0Q5n-wU.woff2) format("woff2");unicode-range:U+1F00-1FFF}@font-face{font-family:Inter;font-style:normal;font-weight:700;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa1pL7W0Q5n-wU.woff2) format("woff2");unicode-range:U+0370-03FF}@font-face{font-family:Inter;font-style:normal;font-weight:700;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2pL7W0Q5n-wU.woff2) format("woff2");unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+0300-0301,U+0303-0304,U+0308-0309,U+0323,U+0329,U+1EA0-1EF9,U+20AB}@font-face{font-family:Inter;font-style:normal;font-weight:700;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa25L7W0Q5n-wU.woff2) format("woff2");unicode-range:U+0100-02AF,U+0304,U+0308,U+0329,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Inter;font-style:normal;font-weight:700;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa1ZL7W0Q5nw.woff2) format("woff2");unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@media screen and (min-width:1280px){body.toc2{padding-left:0}div#toc.toc2{width:22.0625em;left:calc(calc((100vw - (22.0625em + 49.0625em))/ 2));padding:1.25em;margin-top:5em;top:3.75em;height:calc(100vh - 124px);overflow:auto}div#toc.toc2>ul{font-size:.875em}div#content,div#header{max-width:49.0625em;margin:0;margin-left:calc((calc((100vw - (22.0625em + 49.0625em))/ 2)) + 22.0625em);margin-right:calc(calc((100vw - (22.0625em + 49.0625em))/ 2));padding-left:33px}div#footer{padding-left:calc((calc((100vw - (22.0625em + 49.0625em))/ 2)) + 22.0625em + 33px)}}html body{font-family:Inter,sans-serif;color:#333}div #toc ul,div #toctitle,div .admonitionblock td.content>.title,div .admonitionblock>table td.icon .title,div .audioblock>.title,div .conum[data-value],div .dlist>.title,div .exampleblock>.title,div .hdlist>.title,div .imageblock>.title,div .listingblock>.title,div .literalblock>.title,div .olist>.title,div .openblock>.title,div .paragraph>.title,div .qlist>.title,div .quoteblock>.title,div .sidebarblock>.content>.title,div .stemblock>.title,div .ulist>.title,div .verseblock pre,div .verseblock>.title,div .videoblock>.title,div h1,div h2,div h3,div h4,div h5,div h6,div table.tableblock>.title{font-family:Inter,sans-serif}div code,div kbd,div pre,div samp{font-family:monospace,monospace}div #preamble>.sectionbody>[class=paragraph]:first-of-type p,div .paragraph.lead>p{color:#333}div #toctitle,div .sidebarblock>.content>.title,div h1,div h2,div h3,div h4,div h5,div h6{color:#000}div .sectlevel1>li a{color:#000}div .sectlevel1>li .sectlevel2{padding-right:17px}div .sectlevel1>li .sectlevel2>li a{color:#333}div #toctitle,div .sidebarblock>.content>.title,div h1,div h2,div h3,div h4,div h5,div h6{font-weight:700;margin-bottom:20px}div .paragraph,div p{margin-bottom:1.25em}div .paragraph:last-child,div p:last-child{margin-bottom:0}#toc a{color:#000;font-weight:600;padding-top:.3125em;padding-bottom:.3125em}#toc li a{padding-left:.3125em;padding-right:.3125em;display:block}#toc .sectlevel2{margin-top:.25em}#toc .sectlevel1>li:first-child a{margin-top:0}#toc .sectlevel2 a{color:#333;font-weight:500}#toc li a{transition:background 250ms ease-in-out;border-radius:4px;margin-top:.25em}#toc li a:hover{background:#f4f4f4;cursor:pointer}#toc .active{background:#f4f4f4}#toc .toc2 #toctitle{margin-bottom:.9375em}div#toc.toc2{overflow-x:hidden}div.simplebar-scrollbar:before{background:#c1c1c1;border-radius:0}div.simplebar-scrollbar.simplebar-visible:before{opacity:1}div .simplebar-track.simplebar-vertical{top:0;width:17px;background:#f1f1f1}div .simplebar-vertical:before{content:'';position:absolute;top:0;right:1px;width:15px;height:14px;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='7' height='7' viewBox='0 0 7 7' fill='none'%3E%3Cpath d='M8.74228e-08 2L7 2V3L6 3V4H5V5L4 5V6H3V5H2V4L1 4L1 3L0 3L8.74228e-08 2Z' fill='%23505050'/%3E%3C/svg%3E");background-position:center center;background-repeat:no-repeat;z-index:2;background-color:#f1f1f1;transform:rotate(180deg)}div .simplebar-vertical:after{content:'';position:absolute;bottom:0;right:1px;width:15px;height:14px;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='7' height='7' viewBox='0 0 7 7' fill='none'%3E%3Cpath d='M8.74228e-08 2L7 2V3L6 3V4H5V5L4 5V6H3V5H2V4L1 4L1 3L0 3L8.74228e-08 2Z' fill='%23505050'/%3E%3C/svg%3E");background-position:center center;background-repeat:no-repeat;z-index:2;background-color:#f1f1f1}div#header{margin-top:3.75em}div#header .details{color:#595959;padding:0;border-bottom:0}div#header>h1:first-child{margin-bottom:10px;margin-top:0}div#content{margin-top:1.875em}div div .paragraph.lead>p,div div#preamble>.sectionbody>[class=paragraph]:first-of-type p{font-size:1.125em;line-height:150%}div a{color:#3553b8;transition:color 250ms ease-in-out}div a:focus,div a:hover{color:#333}div :not(pre):not([class^="L"])>code{background:#efeff4}div h2{margin-top:1.92307em;margin-bottom:.54054054em}div h3{font-size:1.375em;margin-top:1.875em}div h4{font-size:1.125em}div blockquote,div dt,div p,div span.alt,div summary,div td.content{font-size:1rem}div .listingblock>.content>pre,div .literalblock pre{padding:1.25em;border-radius:6px}div code{color:#333}div .listingblock code[data-lang]::before{top:1.5625em;right:1.5625em}div .listingblock code[data-lang]::before{display:block}div .CodeRay .string .content{color:#dc143c}div .admonitionblock{border-radius:6px;background:#f2f5ff;padding:1.25em 1em}div .admonitionblock :not(pre):not([class^="L"])>code{background:#dbe3ff}div .admonitionblock>table:last-child{margin-bottom:0}div .admonitionblock>table td.content{border-left:0;padding:0;padding-left:10px;color:#212529}div .admonitionblock>table td.icon{text-align:center;padding:0;width:30px}div .admonitionblock>table td.icon .icon-note{position:relative;width:100%;height:30px}div .admonitionblock>table td.icon .icon-note::before{content:"";position:absolute;width:100%;height:100%;left:0;top:59%;transform:translateY(-50%);background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30' fill='none'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M2.5 15C2.5 8.10655 8.10706 2.5 15 2.5C21.8919 2.5 27.4995 8.10655 27.5 15C27.5 21.8924 21.8924 27.5 15 27.5C8.10655 27.5 2.5 21.8924 2.5 15ZM16.1049 17.3549C15.8118 17.6479 15.4144 17.8125 15 17.8125C14.7948 17.8125 14.5916 17.7721 14.4021 17.6936C14.2125 17.615 14.0402 17.4999 13.8951 17.3549C13.7501 17.2098 13.635 17.0375 13.5564 16.8479C13.4779 16.6584 13.4375 16.4552 13.4375 16.25V9.0625C13.4375 8.6481 13.6021 8.25067 13.8951 7.95765C14.1882 7.66462 14.5856 7.5 15 7.5C15.4144 7.5 15.8118 7.66462 16.1049 7.95765C16.3979 8.25067 16.5625 8.6481 16.5625 9.0625V16.25C16.5625 16.6644 16.3979 17.0618 16.1049 17.3549ZM16.875 20.9375C16.875 21.973 16.0355 22.8125 15 22.8125C13.9645 22.8125 13.125 21.973 13.125 20.9375C13.125 19.902 13.9645 19.0625 15 19.0625C16.0355 19.0625 16.875 19.902 16.875 20.9375Z' fill='%23A4A8B8'/%3E%3C/svg%3E")}div .sect1+.sect1{border-top:0}div .sect1{padding-bottom:0}div ol,div ul{margin-left:.8em}div .conum[data-value]{background:#000}div .colist td:not([class]):first-child{padding-left:0;padding-right:6px;padding-top:.2em}div .colist td:not([class]):last-child{padding:0}div .colist tr:not([class]):not(:first-child) td{padding-top:1em}div .colist tr:not([class]):not(:first-child) .conum{margin-top:.3125em}div .admonitionblock+.listingblock,div .admonitionblock+.paragraph{margin-top:1.25em}div .listingblock+.colist,div .literalblock+.colist{margin-top:0}div#footer{background:#333}@media screen and (min-width:768px){div #toc.toc2{background:#fff;border-right:0}div h1{font-size:2.25em}div h2{font-size:1.625em}#footer{z-index:10001;position:relative}} \ No newline at end of file +@font-face{font-family:Inter;font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2JL7W0Q5n-wU.woff2) format("woff2");unicode-range:U+0460-052F,U+1C80-1C88,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:Inter;font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa0ZL7W0Q5n-wU.woff2) format("woff2");unicode-range:U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}@font-face{font-family:Inter;font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2ZL7W0Q5n-wU.woff2) format("woff2");unicode-range:U+1F00-1FFF}@font-face{font-family:Inter;font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa1pL7W0Q5n-wU.woff2) format("woff2");unicode-range:U+0370-03FF}@font-face{font-family:Inter;font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2pL7W0Q5n-wU.woff2) format("woff2");unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+0300-0301,U+0303-0304,U+0308-0309,U+0323,U+0329,U+1EA0-1EF9,U+20AB}@font-face{font-family:Inter;font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa25L7W0Q5n-wU.woff2) format("woff2");unicode-range:U+0100-02AF,U+0304,U+0308,U+0329,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Inter;font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa1ZL7W0Q5nw.woff2) format("woff2");unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Inter;font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2JL7W0Q5n-wU.woff2) format("woff2");unicode-range:U+0460-052F,U+1C80-1C88,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:Inter;font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa0ZL7W0Q5n-wU.woff2) format("woff2");unicode-range:U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}@font-face{font-family:Inter;font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2ZL7W0Q5n-wU.woff2) format("woff2");unicode-range:U+1F00-1FFF}@font-face{font-family:Inter;font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa1pL7W0Q5n-wU.woff2) format("woff2");unicode-range:U+0370-03FF}@font-face{font-family:Inter;font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2pL7W0Q5n-wU.woff2) format("woff2");unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+0300-0301,U+0303-0304,U+0308-0309,U+0323,U+0329,U+1EA0-1EF9,U+20AB}@font-face{font-family:Inter;font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa25L7W0Q5n-wU.woff2) format("woff2");unicode-range:U+0100-02AF,U+0304,U+0308,U+0329,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Inter;font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa1ZL7W0Q5nw.woff2) format("woff2");unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Inter;font-style:normal;font-weight:600;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2JL7W0Q5n-wU.woff2) format("woff2");unicode-range:U+0460-052F,U+1C80-1C88,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:Inter;font-style:normal;font-weight:600;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa0ZL7W0Q5n-wU.woff2) format("woff2");unicode-range:U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}@font-face{font-family:Inter;font-style:normal;font-weight:600;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2ZL7W0Q5n-wU.woff2) format("woff2");unicode-range:U+1F00-1FFF}@font-face{font-family:Inter;font-style:normal;font-weight:600;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa1pL7W0Q5n-wU.woff2) format("woff2");unicode-range:U+0370-03FF}@font-face{font-family:Inter;font-style:normal;font-weight:600;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2pL7W0Q5n-wU.woff2) format("woff2");unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+0300-0301,U+0303-0304,U+0308-0309,U+0323,U+0329,U+1EA0-1EF9,U+20AB}@font-face{font-family:Inter;font-style:normal;font-weight:600;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa25L7W0Q5n-wU.woff2) format("woff2");unicode-range:U+0100-02AF,U+0304,U+0308,U+0329,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Inter;font-style:normal;font-weight:600;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa1ZL7W0Q5nw.woff2) format("woff2");unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Inter;font-style:normal;font-weight:700;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2JL7W0Q5n-wU.woff2) format("woff2");unicode-range:U+0460-052F,U+1C80-1C88,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:Inter;font-style:normal;font-weight:700;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa0ZL7W0Q5n-wU.woff2) format("woff2");unicode-range:U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}@font-face{font-family:Inter;font-style:normal;font-weight:700;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2ZL7W0Q5n-wU.woff2) format("woff2");unicode-range:U+1F00-1FFF}@font-face{font-family:Inter;font-style:normal;font-weight:700;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa1pL7W0Q5n-wU.woff2) format("woff2");unicode-range:U+0370-03FF}@font-face{font-family:Inter;font-style:normal;font-weight:700;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2pL7W0Q5n-wU.woff2) format("woff2");unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+0300-0301,U+0303-0304,U+0308-0309,U+0323,U+0329,U+1EA0-1EF9,U+20AB}@font-face{font-family:Inter;font-style:normal;font-weight:700;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa25L7W0Q5n-wU.woff2) format("woff2");unicode-range:U+0100-02AF,U+0304,U+0308,U+0329,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Inter;font-style:normal;font-weight:700;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa1ZL7W0Q5nw.woff2) format("woff2");unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}html body{font-family:Inter,sans-serif;color:#333}div #toc ul,div #toctitle,div .admonitionblock td.content>.title,div .admonitionblock>table td.icon .title,div .audioblock>.title,div .conum[data-value],div .dlist>.title,div .exampleblock>.title,div .hdlist>.title,div .imageblock>.title,div .listingblock>.title,div .literalblock>.title,div .olist>.title,div .openblock>.title,div .paragraph>.title,div .qlist>.title,div .quoteblock>.title,div .sidebarblock>.content>.title,div .stemblock>.title,div .ulist>.title,div .verseblock pre,div .verseblock>.title,div .videoblock>.title,div h1,div h2,div h3,div h4,div h5,div h6,div table.tableblock>.title{font-family:Inter,sans-serif}div code,div kbd,div pre,div samp{font-family:monospace,monospace}div #preamble>.sectionbody>[class=paragraph]:first-of-type p,div .paragraph.lead>p{color:#333}div #toctitle,div .sidebarblock>.content>.title,div h1,div h2,div h3,div h4,div h5,div h6{color:#000}div .sectlevel1>li a{color:#000}div .sectlevel1>li .sectlevel2{padding-right:17px}div .sectlevel1>li .sectlevel2>li a{color:#333}div #toctitle,div .sidebarblock>.content>.title,div h1,div h2,div h3,div h4,div h5,div h6{font-weight:700;margin-bottom:20px}div .paragraph,div p{margin-bottom:1.25em}div .paragraph:last-child,div p:last-child{margin-bottom:0}#toc a{color:#000;font-weight:600;padding-top:.3125em;padding-bottom:.3125em}#toc li a{padding-left:.3125em;padding-right:.3125em;display:block}#toc .sectlevel2{margin-top:.25em}#toc .sectlevel1>li:first-child a{margin-top:0}#toc .sectlevel2 a{color:#333;font-weight:500}#toc li a{transition:background 250ms ease-in-out;border-radius:4px;margin-top:.25em}#toc li a:hover{background:#f4f4f4;cursor:pointer}#toc .active{background:#f4f4f4}#toc .toc2 #toctitle{margin-bottom:.9375em}div.simplebar-scrollbar:before{background:#c1c1c1;border-radius:0}div.simplebar-scrollbar.simplebar-visible:before{opacity:1}div .simplebar-track.simplebar-vertical{top:0;width:17px;background:#f1f1f1}div .simplebar-vertical:before{content:'';position:absolute;top:0;right:1px;width:15px;height:14px;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='7' height='7' viewBox='0 0 7 7' fill='none'%3E%3Cpath d='M8.74228e-08 2L7 2V3L6 3V4H5V5L4 5V6H3V5H2V4L1 4L1 3L0 3L8.74228e-08 2Z' fill='%23505050'/%3E%3C/svg%3E");background-position:center center;background-repeat:no-repeat;z-index:2;background-color:#f1f1f1;transform:rotate(180deg)}div .simplebar-vertical:after{content:'';position:absolute;bottom:0;right:1px;width:15px;height:14px;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='7' height='7' viewBox='0 0 7 7' fill='none'%3E%3Cpath d='M8.74228e-08 2L7 2V3L6 3V4H5V5L4 5V6H3V5H2V4L1 4L1 3L0 3L8.74228e-08 2Z' fill='%23505050'/%3E%3C/svg%3E");background-position:center center;background-repeat:no-repeat;z-index:2;background-color:#f1f1f1}div#header{margin-top:3.75em}div#header .details{color:#595959;padding:0;border-bottom:0}div#header>h1:first-child{margin-bottom:10px;margin-top:0}div#content{margin-top:1.875em}div div .paragraph.lead>p,div div#preamble>.sectionbody>[class=paragraph]:first-of-type p{font-size:1.125em;line-height:150%}div a{color:#3553b8;transition:color 250ms ease-in-out}div a:focus,div a:hover{color:#333}div :not(pre):not([class^="L"])>code{background:#efeff4}div h2{margin-top:1.92307em;margin-bottom:.54054054em}div h3{font-size:1.375em;margin-top:1.875em}div h4{font-size:1.125em}div blockquote,div dt,div p,div span.alt,div summary,div td.content{font-size:1rem}div .listingblock>.content>pre,div .literalblock pre{padding:1.25em;border-radius:6px}div code{color:#333}div .listingblock code[data-lang]::before{top:1.5625em;right:1.5625em}div .listingblock code[data-lang]::before{display:block}div .CodeRay .string .content{color:#dc143c}div .admonitionblock{border-radius:6px;background:#f2f5ff;padding:1.25em 1em}div .admonitionblock :not(pre):not([class^="L"])>code{background:#dbe3ff}div .admonitionblock>table:last-child{margin-bottom:0}div .admonitionblock>table td.content{border-left:0;padding:0;padding-left:10px;color:#212529}div .admonitionblock>table td.icon{text-align:center;padding:0;width:30px}div .admonitionblock>table td.icon .icon-note{position:relative;width:100%;height:30px}div .admonitionblock>table td.icon .icon-note::before{content:"";position:absolute;width:100%;height:100%;left:0;top:59%;transform:translateY(-50%);background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30' fill='none'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M2.5 15C2.5 8.10655 8.10706 2.5 15 2.5C21.8919 2.5 27.4995 8.10655 27.5 15C27.5 21.8924 21.8924 27.5 15 27.5C8.10655 27.5 2.5 21.8924 2.5 15ZM16.1049 17.3549C15.8118 17.6479 15.4144 17.8125 15 17.8125C14.7948 17.8125 14.5916 17.7721 14.4021 17.6936C14.2125 17.615 14.0402 17.4999 13.8951 17.3549C13.7501 17.2098 13.635 17.0375 13.5564 16.8479C13.4779 16.6584 13.4375 16.4552 13.4375 16.25V9.0625C13.4375 8.6481 13.6021 8.25067 13.8951 7.95765C14.1882 7.66462 14.5856 7.5 15 7.5C15.4144 7.5 15.8118 7.66462 16.1049 7.95765C16.3979 8.25067 16.5625 8.6481 16.5625 9.0625V16.25C16.5625 16.6644 16.3979 17.0618 16.1049 17.3549ZM16.875 20.9375C16.875 21.973 16.0355 22.8125 15 22.8125C13.9645 22.8125 13.125 21.973 13.125 20.9375C13.125 19.902 13.9645 19.0625 15 19.0625C16.0355 19.0625 16.875 19.902 16.875 20.9375Z' fill='%23A4A8B8'/%3E%3C/svg%3E")}div .sect1+.sect1{border-top:0}div .sect1{padding-bottom:0}div ol,div ul{margin-left:.8em}div .conum[data-value]{background:#000}div .colist td:not([class]):first-child{padding-left:0;padding-right:6px;padding-top:.2em}div .colist td:not([class]):last-child{padding:0}div .colist tr:not([class]):not(:first-child) td{padding-top:1em}div .colist tr:not([class]):not(:first-child) .conum{margin-top:.3125em}div .admonitionblock+.listingblock,div .admonitionblock+.paragraph{margin-top:1.25em}div .listingblock+.colist,div .literalblock+.colist{margin-top:0}div#footer{background:#333}@media screen and (min-width:768px){div #toc.toc2{background:#fff;border-right:0}div h1{font-size:2.25em}div h2{font-size:1.625em}#footer{z-index:10001;position:relative}} \ No newline at end of file diff --git a/docs/docs/1.x/index.html b/docs/docs/1.x/index.html index 2e61203..8feaf30 100644 --- a/docs/docs/1.x/index.html +++ b/docs/docs/1.x/index.html @@ -1,681 +1,400 @@ - - - - - - -DFLib Documentation - - - - - - - -
-
-
-
-

"DFLib" (short for "DataFrame Library") is a lightweight, pure Java implementation of DataFrame. DataFrame is a -very common structure in data science and Big Data worlds. It provides operations like search, -filtering, joins, aggregations, statistical functions, etc., that are reminiscent of SQL (also of Excel), except you run -them in your app over dynamic in-memory data sets.

-
-
-

There are DataFrame implementations in Python (pandas), R, -Apache Spark, etc. -DFLib project’s goal is to provide the same functionality for regular Java applications. It is a simple library, that -requires no special infrastructure. DFLib core is dependency-free.

-
-
-

The code in this documentation can be run in any Java IDE, as well as (and this is pretty cool!) in a Jupyter notebook. You will need Java 11 or newer.

-
-
-
-
-

Get Started with DFLib

-
-
-

Include DFLib in a project. Assuming you are using Maven, start by declaring a "BOM" to have a common version for -multiple DFLib modules:

-
-
-
-
<dependencyManagement>
+  
+    
+    
+    
+    
+    
+    DFLib Documentation
+    
+    
+    
+    
+    
+    
+    
+  
+  
+    
+
+ + +
+
+
+
+ +
+

DFLib Documentation

+
+ Andrus Adamchik and other contributors
+
+
+
+
+
+

+ "DFLib" (short for "DataFrame Library") is a lightweight, pure Java implementation of DataFrame. DataFrame is a very common structure in data science and Big Data worlds. It provides + operations like search, filtering, joins, aggregations, statistical functions, etc., that are reminiscent of SQL (also of Excel), except you run them in your app over dynamic in-memory data sets. +

+
+
+

+ There are DataFrame implementations in Python (pandas), R, + Apache Spark, etc. DFLib project’s goal is to provide the same functionality for regular Java + applications. It is a simple library, that requires no special infrastructure. DFLib core is dependency-free. +

+
+
+

The code in this documentation can be run in any Java IDE, as well as (and this is pretty cool!) in a Jupyter notebook. You will need Java 11 or newer.

+
+
+
+
+
+

Get Started with DFLib

+
+

Include DFLib in a project. Assuming you are using Maven, start by declaring a "BOM" to have a common version for multiple DFLib modules:

+
+
+
+
<dependencyManagement>
   <dependencies>
     <dependency>
       <groupId>org.dflib</groupId>
@@ -686,39 +405,40 @@ 

Get Started with DFLib

</dependency> </dependencies> </dependencyManagement>
-
-
-
-

Next include DFLib core as a dependency:

-
-
-
-
<dependency>
+                        
+
+
+

Next include DFLib core as a dependency:

+
+
+
+
<dependency>
     <groupId>org.dflib</groupId>
     <artifactId>dflib</artifactId>
 </dependency>
-
-
-
-

Create a DataFrame, do some manipulations with it, print the result:

-
-
-
-
DataFrame df1 = DataFrame
+                        
+
+
+

Create a DataFrame, do some manipulations with it, print the result:

+
+
+
+
DataFrame df1 = DataFrame
         .foldByRow("a", "b", "c")
         .ofStream(IntStream.range(1, 10000));
 
 DataFrame df2 = df1.rows(r -> r.getInt(0) % 2 == 0).select();
 
 System.out.println(Printers.tabular.toString(df2));
-
-
-
-

When you run this code, console output will look like this:

-
-
-
-
   a    b    c
+                        
+
+
+

When you run this code, console output will look like this:

+
+
+
+
+   a    b    c
 ---- ---- ----
    4    5    6
   10   11   12
@@ -727,92 +447,104 @@ 

Get Started with DFLib

9982 9983 9984 9988 9989 9990 9994 9995 9996 -1666 rows x 3 columns
-
-
-
- - - - - -
- - -We’ll omit print statements in all the following examples, and will simply display their output. Details of -printing are discussed in the in "Printers" chapter. -
-
-
-
-
-

Main Data Structures

-
-
-

The two essential DFLib classes are Series and DataFrame. Series is a 1-dimensional array-like object, and DataFrame -is a 2-dimensional table-like object. Columns in a DataFrame are stored as Series. Additionally, we will discuss -Index object that stores DataFrame column names.

-
-
-

Both DataFrame and Series (and Index) are fully immutable, so all operations on them return a new instance. Behind -the scenes the library shares as much data as possible between instances, so copying these objects does not cause -significant performance degradation, and in turn makes DFLib fully thread-safe, with multiple concurrent operations -possible on the same data structure. Also, immutability means that you can have full snapshots of your data at each -step of a transformation, simplifying debugging and auditing of data pipelines.

-
-
-

Series

-
-

Series is the simplest of the two data structures. You can think of it as a wrapper around an array of values. -You can use Series to model sequences of data such as timestamps in a time series. Series object is parameterized for -the type of data that it holds. So there can be Series<String> or a Series<LocalDate>, etc. There is also an -important category of "primitive" Series (IntSeries, LongSeries, DoubleSeries, BooleanSeries) that are optimized -for memory use and arithmetic operations. Of course, each primitive Series can also pose as Series of a corresponding -wrapper object type (e.g. IntSeries is also a Series<Integer>).

-
-
-

Series object is an important building block of DataFrame, but it defines a number of useful data -manipulation and transformation operations on its own. Those will be covered in the following chapters. Here we’ll -discuss how to create Series.

-
-
-

Creation of Series from Arrays

-
-

Series can be created using static ofXyz(..) methods on the Series interface:

-
-
-
-
Series<String> s = Series.of("a", "bcd", "ef", "g");
-
-
-
-
-
a
+1666 rows x 3 columns
+                            
+
+
+
+ + + + + +
+ + + We’ll omit print statements in all the following examples, and will simply display their output. Details of printing are discussed in the in "Printers" chapter. +
+
+
+
+
+
+

Main Data Structures

+
+

+ The two essential DFLib classes are Series and DataFrame. Series is a 1-dimensional array-like object, and DataFrame + is a 2-dimensional table-like object. Columns in a DataFrame are stored as Series. Additionally, we will discuss + Index object that stores DataFrame column names. +

+
+
+

+ Both DataFrame and Series (and Index) are fully immutable, so all operations on them return a new instance. Behind the scenes the library shares as much data as possible between instances, so copying + these objects does not cause significant performance degradation, and in turn makes DFLib fully thread-safe, with multiple concurrent operations possible on the same data structure. Also, immutability means that + you can have full snapshots of your data at each step of a transformation, simplifying debugging and auditing of data pipelines. +

+
+
+
+
+

Series

+
+

+ Series is the simplest of the two data structures. You can think of it as a wrapper around an array of values. You can use Series to model sequences of data such as timestamps in a + time series. Series object is parameterized for the type of data that it holds. So there can be Series<String> or a Series<LocalDate>, etc. There is also an important + category of "primitive" Series (IntSeries, LongSeries, DoubleSeries, BooleanSeries) that are optimized for memory use and arithmetic operations. Of course, + each primitive Series can also pose as Series of a corresponding wrapper object type (e.g. IntSeries is also a Series<Integer>). +

+
+
+

+ Series object is an important building block of DataFrame, but it defines a number of useful data manipulation and transformation operations on its own. Those will be covered in the following + chapters. Here we’ll discuss how to create Series. +

+
+
+

Creation of Series from Arrays

+
+

Series can be created using static ofXyz(..) methods on the Series interface:

+
+
+
+
Series<String> s = Series.of("a", "bcd", "ef", "g");
+
+
+
+
+
+a
 bcd
 ...
 g
-4 elements
-
-
-
-

Primitive Series classes have their own factory methods. E.g.:

-
-
-
-
IntSeries is = Series.ofInt(0, 1, -300, Integer.MAX_VALUE);
-
-
-
-
-

Creation of Series By Element

-
-

If we don’t have a data array or collection to start with, and instead somehow produce a sequence of -values of unpredictable length, we can use Series.byElement() API. E.g. the following example reads data from -an InputStream line by line as Strings:

-
-
-
-
// InputStream inputStream = ...
+4 elements
+                                    
+
+
+
+

Primitive Series classes have their own factory methods. E.g.:

+
+
+
+
IntSeries is = Series.ofInt(0, 1, -300, Integer.MAX_VALUE);
+
+
+
+
+

Creation of Series By Element

+
+

+ If we don’t have a data array or collection to start with, and instead somehow produce a sequence of values of unpredictable length, we can use Series.byElement() API. E.g. the + following example reads data from an InputStream line by line as Strings: +

+
+
+
+
// InputStream inputStream = ...
 SeriesAppender<String, String> appender = Series
         .byElement(Extractor.<String>$col()) (1)
         .appender();
@@ -823,100 +555,99 @@ 

Creation of Series By Element

} Series<String> s = appender.toSeries();
-
-
-
- - - - - - - - - -
1Create Series "appender" that will accumulate values. For primitive Series you would use Extractor.$int(..), -Extractor.$long(..) and so on
2Append values as they are read one-by-one
-
-
-
-
-

DataFrame

-
-

DataFrame is an in-memory table made of an Index header and a number of named columns. Each column is -a Series, and has an associated name in the Index. DataFrame can contain columns of different -kinds, so it is not parameterized for any single type.

-
-
-

"Rows" is a purely virtual concept as the data is organized by column, yet there is a number of APIs that appear to -operate on rows for user convenience.

-
-
-

There are a few ways to create a DataFrame. Here we’ll show how to convert various in-memory objects to DataFrames -(arrays, Streams, Collections, Series).

-
-
- - - - - -
- - -More often than not, a DataFrame is not created from an in-memory object, but is rather loaded from (and saved to) an -external source, like a database or a CSV file. Those are discussed in separate chapters. -
-
-
-

First example, adding data row by row:

-
-
-
-
DataFrame df = DataFrame
+                                
+
+
+ + + + + + + + + +
1Create Series "appender" that will accumulate values. For primitive Series you would use Extractor.$int(..), Extractor.$long(..) and so on
2Append values as they are read one-by-one
+
+
+
+
+

DataFrame

+
+

+ DataFrame is an in-memory table made of an Index header and a number of named columns. Each column is a Series, and has an associated name in the Index. + DataFrame can contain columns of different kinds, so it is not parameterized for any single type. +

+
+
+

"Rows" is a purely virtual concept as the data is organized by column, yet there is a number of APIs that appear to operate on rows for user convenience.

+
+
+

There are a few ways to create a DataFrame. Here we’ll show how to convert various in-memory objects to DataFrames (arrays, Streams, Collections, Series).

+
+
+ + + + + +
+ + + More often than not, a DataFrame is not created from an in-memory object, but is rather loaded from (and saved to) an external source, like a database or a + CSV file. Those are discussed in separate chapters. +
+
+
+

First example, adding data row by row:

+
+
+
+
DataFrame df = DataFrame
         .byArrayRow("name", "age") (1)
         .appender() (2)
         .append("Joe", 18)   (3)
         .append("Andrus", 49)
         .append("Joan", 32)
         .toDataFrame();
-
-
-
- - - - - - - - - - - - - -
1A special builder is created to append each row as a vararg array
2The builder creates an "appender" object. While here we are using the builder with default settings, it has extra -methods to configure capacity, data sampling, etc.
3Passing individual rows to the appender one-by-one
-
-
-

The resulting DataFrame looks like this:

-
-
-
-
name   age
+                            
+
+
+ + + + + + + + + + + + + +
1A special builder is created to append each row as a vararg array
2The builder creates an "appender" object. While here we are using the builder with default settings, it has extra methods to configure capacity, data sampling, etc.
3Passing individual rows to the appender one-by-one
+
+
+

The resulting DataFrame looks like this:

+
+
+
+
+name   age
 ------ ---
 Joe    18
 Andrus 49
-Joan   32
-
-
-
-

A more general example - creating a DataFrame from a list of objects by "extracting" column data from object properties:

-
-
-
-
record Person(String name, int age) {
+Joan   32
+                                
+
+
+
+

A more general example - creating a DataFrame from a list of objects by "extracting" column data from object properties:

+
+
+
+
record Person(String name, int age) {
 }
 
 List<Person> people = List.of(
@@ -932,87 +663,91 @@ 

DataFrame

.appender() (3) .append(people) (4) .toDataFrame();
-
-
-
- - - - - - - - - - - - - - - - - -
1The builder is started with an array of Extractors. Each extractor generates its own column, filling it with a -corresponding object property.
2Specifying the names of the DataFrame columns. If omitted, column names are assigned automatically
3Creating a row-by-row appender
4Appending the list.
-
-
-

The resulting DataFrame looks like this:

-
-
-
-
name   age
+                            
+
+
+ + + + + + + + + + + + + + + + + +
1The builder is started with an array of Extractors. Each extractor generates its own column, filling it with a corresponding object property.
2Specifying the names of the DataFrame columns. If omitted, column names are assigned automatically
3Creating a row-by-row appender
4Appending the list.
+
+
+

The resulting DataFrame looks like this:

+
+
+
+
+name   age
 ------ ---
 Joe    18
 Andrus 49
-Joan   32
-
-
-
-

Another example - a single-dimensional array can be "folded" into a DataFrame row-by-row:

-
-
-
-
DataFrame df = DataFrame
+Joan   32
+                                
+
+
+
+

Another example - a single-dimensional array can be "folded" into a DataFrame row-by-row:

+
+
+
+
DataFrame df = DataFrame
         .foldByRow("name", "age") (1)
         .of("Joe", 18, "Andrus", 49, "Joan", 32); (2)
-
-
-
- - - - - - - - - -
1Folding Builder is created
2Passing a varargs array of values, that is folded to match the specified number of columns, row by row.
-
-
-

Same, but folding column-by-column:

-
-
-
-
DataFrame df = DataFrame
+                            
+
+
+ + + + + + + + + +
1Folding Builder is created
2Passing a varargs array of values, that is folded to match the specified number of columns, row by row.
+
+
+

Same, but folding column-by-column:

+
+
+
+
DataFrame df = DataFrame
         .foldByColumn("name", "age")
         .of("Joe", "Andrus", "Joan", 18, 49, 32);
-
-
-
-

You can also create DataFrames from collections or Streams (folded either by row or by column). Here is an example -of how to use a Stream of primitive ints, creating a DataFrame made of memory-efficient IntSeries columns:

-
-
-
-
DataFrame df = DataFrame
+                            
+
+
+

+ You can also create DataFrames from collections or Streams (folded either by row or by column). Here is an example of how to use a Stream of primitive ints, creating a DataFrame made of memory-efficient + IntSeries columns: +

+
+
+
+
DataFrame df = DataFrame
         .foldByColumn("col1", "col2")
         .ofStream(IntStream.range(0, 10000));
-
-
-
-
-
col1 col2
+                            
+
+
+
+
+col1 col2
 ---- ----
    0 5000
    1 5001
@@ -1021,72 +756,76 @@ 

DataFrame

4997 9997 4998 9998 4999 9999 -5000 rows x 2 columns
-
-
-
-

Finally, a DataFrame can be created from an array of Series, each Series representing a column:

-
-
-
-
DataFrame df = DataFrame
+5000 rows x 2 columns
+                                
+
+
+
+

Finally, a DataFrame can be created from an array of Series, each Series representing a column:

+
+
+
+
DataFrame df = DataFrame
         .byColumn("name", "age")
         .of(
                 Series.of("Joe", "Andrus", "Joan"),
                 Series.ofInt(18, 49, 32)
         );
-
-
-
-

This is the most efficient way, as an array of Series is how each DataFrame is structured internally.

-
-
-
-

Index

-
-

Index is somewhat similar to Series. It serves as a DataFrame "header" and internally allows to quickly resolve -column String labels to their numeric positions. You’d work with Index instances outside a DataFrame only -occasionally, but it is still good to know its capabilities. E.g., here is how to get all the column labels:

-
-
-
-
String[] labels = df.getColumnsIndex().getLabels();
-
-
-
-
-
-
-

Printers

-
-
-

When doing data exploration and running data pipelines, it is important to be able to visualize data at every step. -More advanced forms of visualization include charts and diagrams. But the simplest thing you can do is printing -data to the console. Both DataFrame and Series implement toString() method, that will print their contents -as a single line, truncating large data sets in the process, which is good for debugging applications running on a -server.

-
-
-

A more human-friendly form of output is produced by a tabular printer. Here is how to use the default tabular -printer:

-
-
-
-
DataFrame df = DataFrame
+                            
+
+
+

This is the most efficient way, as an array of Series is how each DataFrame is structured internally.

+
+
+
+

Index

+
+

+ Index is somewhat similar to Series. It serves as a DataFrame "header" and internally allows to quickly resolve column String labels to their numeric positions. You’d work with + Index instances outside a DataFrame only occasionally, but it is still good to know its capabilities. E.g., here is how to get all the column labels: +

+
+
+
+
String[] labels = df.getColumnsIndex().getLabels();
+
+
+
+
+
+
+
+

Printers

+
+

+ When doing data exploration and running data pipelines, it is important to be able to visualize data at every step. More advanced forms of visualization include charts and diagrams. But the simplest thing you can + do is printing data to the console. Both DataFrame and Series implement toString() method, that will print their contents as a single line, truncating large data sets in the + process, which is good for debugging applications running on a server. +

+
+
+

A more human-friendly form of output is produced by a tabular printer. Here is how to use the default tabular printer:

+
+
+
+
DataFrame df = DataFrame
         .foldByColumn("col1", "col2", "col3")
         .ofStream(IntStream.range(0, 10000));
 
 String table = Printers.tabular.toString(df);
 System.out.println(table);
-
-
-
-

It prints the data as follows, displaying at most 6 rows and truncating the rest. Same goes for cell values. Values -longer than 30 chars are also truncated (since they are pretty short in our example, value truncation is not obvious here).

-
-
-
-
col1 col2 col3
+                        
+
+
+

+ It prints the data as follows, displaying at most 6 rows and truncating the rest. Same goes for cell values. Values longer than 30 chars are also truncated (since they are pretty short in our example, value + truncation is not obvious here). +

+
+
+
+
+col1 col2 col3
 ---- ---- ----
    0 3334 6668
    1 3335 6669
@@ -1095,540 +834,576 @@ 

Printers

3331 6665 9999 3332 6666 0 3333 6667 0 -3334 rows x 3 columns
-
-
-
-

You can change the truncation parameters by creating your own printer:

-
-
-
-
Printer printer = new TabularPrinter(3, 3); (1)
+3334 rows x 3 columns
+                            
+
+
+
+

You can change the truncation parameters by creating your own printer:

+
+
+
+
Printer printer = new TabularPrinter(3, 3); (1)
 String table = printer.toString(df);
 System.out.println(table);
-
-
-
- - - - - -
1Create a printer that displays at most 3 rows and up to 3 characters in each cell.
-
-
-
-
c.. c.. c..
+                        
+
+
+ + + + + +
1Create a printer that displays at most 3 rows and up to 3 characters in each cell.
+
+
+
+
+c.. c.. c..
 --- --- ---
   0 3.. 6..
   1 3.. 6..
 ...
 3.. 6..   0
-3334 rows x 3 columns
-
-
-
- - - - - -
- - -If you are using Jupyter Notebook, all the printers are already setup for you. So if the last -statement in a Jupyter cell is a DataFrame or a Series, it will be printed as a table in the notebook. -
-
-
-
-
-

Expressions

-
-
-

DFLib includes a built-in expression language (implemented as a Java "DSL"). It allows to perform column-centric -operations on DataFrames and Series. Exp is the interface representing an expression that takes a DataFrame or a -Series and produces a Series of the specified type. It also contains static factory methods to create all kinds of -expressions.

-
-
-

To use expressions, you’d often start by adding a static import of the Exp interface, so that all its factory methods -are available directly in the code:

-
-
-
-
import static org.dflib.Exp.*;
-
-
-
-

Now let’s create two simple expressions that return a named and a positional column of the requested type:

-
-
-
-
StrExp lastExp = $str("last");
+3334 rows x 3 columns
+                            
+
+
+
+ + + + + +
+ + + If you are using Jupyter Notebook, all the printers are already setup for you. So if the last statement in a Jupyter cell is a DataFrame or a Series, it + will be printed as a table in the notebook. +
+
+
+
+
+
+

Expressions

+
+

+ DFLib includes a built-in expression language (implemented as a Java "DSL"). It allows to perform column-centric operations on DataFrames and Series. Exp is the interface representing an expression + that takes a DataFrame or a Series and produces a Series of the specified type. It also contains static factory methods to create all kinds of expressions. +

+
+
+

To use expressions, you’d often start by adding a static import of the Exp interface, so that all its factory methods are available directly in the code:

+
+
+
+
import static org.dflib.Exp.*;
+
+
+
+

Now let’s create two simple expressions that return a named and a positional column of the requested type:

+
+
+
+
StrExp lastExp = $str("last");
 DecimalExp salaryExp = $decimal(2);
-
-
-
-

And now let’s evaluate the expressions:

-
-
-
-
DataFrame df = DataFrame.foldByRow("first", "last", "salary").of(
+                        
+
+
+

And now let’s evaluate the expressions:

+
+
+
+
DataFrame df = DataFrame.foldByRow("first", "last", "salary").of(
         "Jerry", "Cosin", new BigDecimal("120000"),
         "Juliana", "Walewski", new BigDecimal("80000"),
         "Joan", "O'Hara", new BigDecimal("95000"));
 
 Series<String> last = lastExp.eval(df);
 Series<BigDecimal> salary = salaryExp.eval(df);
-
-
-
-

This doesn’t look like much (other DFLib APIs would do the same), but this basic abstraction allows to implement a -wide range of operations (filtering, sorting, aggregation, etc). The following chapters will demonstrate various use -cases for expressions.

-
-
- - - - - -
- - -DFLib expression language Expressions work on Series instead of individual values, so they can achieve the best -possible performance for any given operation. Expressions should be the preferred way to manipulate your data instead of -more "direct" API. -
-
-
-

Now let’s look at various types of expressions…​

-
-
-

Column Expressions

-
-

$str(..) and $decimal(..) expressions in the example above are the column lookup expressions. They return a DataFrame -column with a given name or at a given (zero-based) position without applying any transformations to the data.

-
-
- - - - - -
- - -So what is returned when you evaluate a column expression with a Series object? Series can be thought of as a -single (unnamed) column. So a column expression simply returns the Series unchanged, ignoring implied column -name or position. -
-
-
-

Factory methods for column expressions are easy to spot in the Exp interface - they all start with a dollar sign. -Return type of the column expression is implied from the method name ($str(..) produces a Series<String>, $decimal(..) - -Series<BigDecimal>, $int(..) - Series<Integer>, $date(..) - Series<LocalDate> and so on).

-
-
- - - - - -
- - -DFLib doesn’t do any type conversion of the original column data (the exception being $bool). So you -need to select the right type in your code to avoid ClassCastExceptions downstream. -
-
-
-
-

Constant Expressions

-
-

If we want to generate a Series with the same repeating value, we can use the $val(..) expression:

-
-
-
-
Series<String> hi = $val("hi!").eval(df);
-
-
-
-
-
hi!
+                        
+
+
+

+ This doesn’t look like much (other DFLib APIs would do the same), but this basic abstraction allows to implement a wide range of operations (filtering, sorting, aggregation, etc). The following chapters + will demonstrate various use cases for expressions. +

+
+
+ + + + + +
+ + + DFLib expression language Expressions work on Series instead of individual values, so they can achieve the best possible performance for any given operation. Expressions should be the preferred way to + manipulate your data instead of more "direct" API. +
+
+
+

Now let’s look at various types of expressions…​

+
+
+
+
+

Column Expressions

+
+

+ $str(..) and $decimal(..) expressions in the example above are the column lookup expressions. They return a DataFrame column with a given name or at a given (zero-based) position + without applying any transformations to the data. +

+
+
+ + + + + +
+ + + So what is returned when you evaluate a column expression with a Series object? Series can be thought of as a single (unnamed) column. So a column expression simply returns the Series unchanged, + ignoring implied column name or position. +
+
+
+

+ Factory methods for column expressions are easy to spot in the Exp interface - they all start with a dollar sign. Return type of the column expression is implied from the method name ( + $str(..) produces a Series<String>, $decimal(..) - Series<BigDecimal>, $int(..) - Series<Integer>, + $date(..) - Series<LocalDate> and so on). +

+
+
+ + + + + +
+ + + DFLib doesn’t do any type conversion of the original column data (the exception being $bool). So you need to select the right type in your code to avoid ClassCastExceptions + downstream. +
+
+
+
+

Constant Expressions

+
+

If we want to generate a Series with the same repeating value, we can use the $val(..) expression:

+
+
+
+
Series<String> hi = $val("hi!").eval(df);
+
+
+
+
+
+hi!
+hi!
 hi!
-hi!
-
-
-
-

A useful case for $val(..) is to create a separator for String concatenation:

-
-
-
-
Series<String> fn = concat( (1)
+                                
+
+
+
+

A useful case for $val(..) is to create a separator for String concatenation:

+
+
+
+
Series<String> fn = concat( (1)
         $str("first"),
         $val(" "), (2)
         $str("last")).eval(df);
-
-
-
- - - - - - - - - -
1This is a static import of Exp.concat(..), an expression that takes a variable number of arguments
2Inserting space between first and last name
-
-
-
-
Jerry Cosin
+                            
+
+
+ + + + + + + + + +
1This is a static import of Exp.concat(..), an expression that takes a variable number of arguments
2Inserting space between first and last name
+
+
+
+
+Jerry Cosin
 Juliana Walewski
-Joan O'Hara
-
-
-
-
-

Complex Expressions

-
-

Expressions can be expanded by calling methods on the Exp object. Also, as we’ve already -seen above, they can be composed of other expressions by invoking static methods of Exp. E.g.:

-
-
-
-
(1)
+Joan O'Hara
+                                
+
+
+
+
+

Complex Expressions

+
+

+ Expressions can be expanded by calling methods on the Exp object. Also, as we’ve already seen above, they can be composed of other expressions by invoking static methods of + Exp. E.g.: +

+
+
+
+
(1)
 Condition c = and( (2)
         $str("last").startsWith("A"), (3)
         $decimal("salary").add($decimal("benefits")).gt(100000.) (4)
 );
-
-
-
- - - - - - - - - - - - - - - - - -
1"Condition" is an Exp<Boolean> described in more detail below
2and(..) is a statically-imported Exp.and(..)
3startsWith(..) produces a condition based on another string expression
4add(..) produces an addition operation for two numeric expressions, gt(..) produces a condition from the result of the addition
-
-
-
-

Numeric Expressions

-
-

TODO

-
-
-
-

Date Expressions

-
-

TODO

-
-
-
-

Conditions

-
-

TODO

-
-
-
-

Sorters

-
-

Sorter is a special object, that allows to sort DFLib data structures. Internally a Sorter is using an -expression to retrieve values that comprise sorting criteria and index them in the specified order. Sorter can be -created from any expression by calling its asc() or desc() methods:

-
-
-
-
// sort by last name in the ascending order
+                            
+
+
+ + + + + + + + + + + + + + + + + +
1"Condition" is an Exp<Boolean> described in more detail below
2and(..) is a statically-imported Exp.and(..)
3startsWith(..) produces a condition based on another string expression
4add(..) produces an addition operation for two numeric expressions, gt(..) produces a condition from the result of the addition
+
+
+
+

Numeric Expressions

+
+

TODO

+
+
+
+

Date Expressions

+
+

TODO

+
+
+
+

Conditions

+
+

TODO

+
+
+
+

Sorters

+
+

+ Sorter is a special object, that allows to sort DFLib data structures. Internally a Sorter is using an expression to retrieve values that comprise sorting criteria and + index them in the specified order. Sorter can be created from any expression by calling its asc() or desc() methods: +

+
+
+
+
// sort by last name in the ascending order
 Sorter s = $str("last").asc();
-
-
-
- - - - - -
- - -DFLib would allow you to create a Sorter based on any expression type. In runtime, the actual type must be -either a Java primitive or an instance of Comparable, or a ClassCastException will be thrown during sorting. -
-
-
-
-
-
-

Column Operations

-
-
-

To manipulate data in a single DataFrame, with a few exceptions you would start by picking a subset of columns or -rows. Those are represented as ColumnSet and RowSet objects. Let’s look at columns first…​

-
-
-

Pick Columns

-
-

Columns are picked either by condition, by name, by position or implicitly. Let’s take a look at each style…​

-
-
-

By Condition

-
-

A condition is specified as a "predicate" lambda:

-
-
-
-
DataFrame df1 = df.cols(c -> !"middle".equals(c)).select();
-
-
-
-

This form of cols(..) does not allow to reorder the columns. The resulting columns will always follow the relative -order of the original DataFrame:

-
-
-
-
first   last
+                            
+
+
+ + + + + +
+ + + DFLib would allow you to create a Sorter based on any expression type. In runtime, the actual type must be either a Java primitive or an instance of Comparable, or a ClassCastException + will be thrown during sorting. +
+
+
+
+
+
+
+

Column Operations

+
+

+ To manipulate data in a single DataFrame, with a few exceptions you would start by picking a subset of columns or rows. Those are represented as ColumnSet and + RowSet objects. Let’s look at columns first…​ +

+
+
+
+
+

Pick Columns

+
+

Columns are picked either by condition, by name, by position or implicitly. Let’s take a look at each style…​

+
+
+

By Condition

+
+

A condition is specified as a "predicate" lambda:

+
+
+
+
DataFrame df1 = df.cols(c -> !"middle".equals(c)).select();
+
+
+
+

This form of cols(..) does not allow to reorder the columns. The resulting columns will always follow the relative order of the original DataFrame:

+
+
+
+
+first   last
 ------- --------
 Jerry   Cosin
-Joan    O'Hara
-
-
-
-
-

By Names

-
-

Here is a simple example of how to pick two columns from a DataFrame by name, and return a new DataFrame with -just those two columns:

-
-
-
-
DataFrame df = DataFrame.foldByRow("first", "last", "middle").of(
+Joan    O'Hara
+                                    
+
+
+
+
+

By Names

+
+

Here is a simple example of how to pick two columns from a DataFrame by name, and return a new DataFrame with just those two columns:

+
+
+
+
DataFrame df = DataFrame.foldByRow("first", "last", "middle").of(
         "Jerry", "Cosin", "M",
         "Joan", "O'Hara", null);
 
 DataFrame df1 = df
         .cols("last", "first") (1)
         .select(); (2)
-
-
-
- - - - - - - - - -
1Define a ColumnSet of two columns matching provided names. -The order of columns in the ColumnSet matches the order of column names passed to this method.
2Return a new DataFrame matching the ColumnSet. More on this in the next chapter.
-
-
-
-
last     first
+                                
+
+
+ + + + + + + + + +
1Define a ColumnSet of two columns matching provided names. The order of columns in the ColumnSet matches the order of column names passed to this method.
2Return a new DataFrame matching the ColumnSet. More on this in the next chapter.
+
+
+
+
+last     first
 -------- -------
 Cosin    Jerry
-O'Hara   Joan
-
-
-
- - - - - -
- - -Passing names in a specific order to cols(..) allowed us to reorder the columns in the result. -
-
-
-

Instead of listing included columns, you might specify which columns should be excluded from selection. This flavor -doesn’t support reordering:

-
-
-
-
DataFrame df1 = df.colsExcept("middle").select();
-
-
-
-
-

By Positions

-
-

You can also pick columns by positions. The first position is "0".

-
-
-
-
DataFrame df1 = df.cols(1, 0).select();
-
-
-
-
-

Implicitly

-
-

If you specify no columns at all when building a RowSet, the returned DataFrame will be the same as the source one.

-
-
-
-
DataFrame df1 = df.cols().select();
-
-
-
- - - - - -
- - -In this particular case, implicit column selection is not very useful. But in fact, this style is dynamic -and may result in a smaller subset of columns selected based on the semantics of an operation applied to the -ColumnSet (e.g. named expressions making specific columns). Also, it allows to apply a transformation to -all columns at once. These cases are described later in the columns transformation chapter. -
-
-
-
-
-

Rename Columns

-
-

We saw how we can select a subset of columns. While doing that, we can also assign new names to all or some of the -columns via selectAs(..).

-
-
-

Rename all column set columns:

-
-
-
-
DataFrame df1 = df
+O'Hara   Joan
+                                    
+
+
+
+ + + + + +
+ + Passing names in a specific order to cols(..) allowed us to reorder the columns in the result.
+
+
+

Instead of listing included columns, you might specify which columns should be excluded from selection. This flavor doesn’t support reordering:

+
+
+
+
DataFrame df1 = df.colsExcept("middle").select();
+
+
+
+
+

By Positions

+
+

You can also pick columns by positions. The first position is "0".

+
+
+
+
DataFrame df1 = df.cols(1, 0).select();
+
+
+
+
+

Implicitly

+
+

If you specify no columns at all when building a RowSet, the returned DataFrame will be the same as the source one.

+
+
+
+
DataFrame df1 = df.cols().select();
+
+
+
+ + + + + +
+ + + In this particular case, implicit column selection is not very useful. But in fact, this style is dynamic and may result in a smaller subset of columns selected based on the semantics of an + operation applied to the + ColumnSet (e.g. named expressions making specific columns). Also, it allows to apply a transformation to all columns at once. These cases are described later in the + columns transformation chapter. +
+
+
+
+
+

Rename Columns

+
+

We saw how we can select a subset of columns. While doing that, we can also assign new names to all or some of the columns via selectAs(..).

+
+
+

Rename all column set columns:

+
+
+
+
DataFrame df1 = df
         .cols("last", "first")
         .selectAs("last_name", "first_name"); (1)
-
-
-
- - - - - -
1Passing new names for the ColumnSet columns. The order of the names must correspond to the order of columns -in the ColumnSet, regardless of how it was defined.
-
-
-
-
last_name first_name
+                            
+
+
+ + + + + +
1Passing new names for the ColumnSet columns. The order of the names must correspond to the order of columns in the ColumnSet, regardless of how it was defined.
+
+
+
+
+last_name first_name
 --------- ----------
 Cosin     Jerry
-Walewski  Juliana
-
-
-
-

Rename a subset of columns. Specifying names of all columns at once may not be practical for -"wide" column sets. Instead, you can pass a map of old to new names to selectAs(..) to rename just some columns. -Names not present in the map will remain unchanged:

-
-
-
-
DataFrame df1 = df
+Walewski  Juliana
+                                
+
+
+
+

+ Rename a subset of columns. Specifying names of all columns at once may not be practical for "wide" column sets. Instead, you can pass a map of old to new names to selectAs(..) to rename just + some columns. Names not present in the map will remain unchanged: +

+
+
+
+
DataFrame df1 = df
         .cols("last", "first")
         .selectAs(Map.of("last", "LAST_NAME"));
-
-
-
-
-
LAST_NAME first
+                            
+
+
+
+
+LAST_NAME first
 --------- -------
 Cosin     Jerry
-O'Hara    Joan
-
-
-
-

Rename with a function, applied to all column names in turn. This is useful e.g. to convert all names to lower or -upper case:

-
-
-
-
DataFrame df1 = df
+O'Hara    Joan
+                                
+
+
+
+

Rename with a function, applied to all column names in turn. This is useful e.g. to convert all names to lower or upper case:

+
+
+
+
DataFrame df1 = df
         .cols("last", "first")
         .selectAs(String::toUpperCase);
-
-
-
-
-
LAST   FIRST
+                            
+
+
+
+
+LAST   FIRST
 ------ -----
 Cosin  Jerry
-O'Hara Joan
-
-
-
-
-

Transform Columns

-
-

In the above examples we performed DataFrame vertical slicing, columns reordering and renaming. In this chapter -we’ll show how to transform column data.

-
-
-

Generating columns with expressions:

-
-
-
-
Exp fmExp = concat(
+O'Hara Joan
+                                
+
+
+
+
+

Transform Columns

+
+

In the above examples we performed DataFrame vertical slicing, columns reordering and renaming. In this chapter we’ll show how to transform column data.

+
+
+

Generating columns with expressions:

+
+
+
+
Exp fmExp = concat(
         $str("first"),
         ifNull($str("middle").mapVal(s -> " " + s), ""));
 
 DataFrame df1 = df
         .cols("first_middle", "last") (1)
         .select(fmExp, $str("last")); (2)
-
-
-
- - - - - - - - - -
1When defining the ColumnSet, we are allowed to specify columns that are not present in the original DataFrame, as -we are naming the result columns, not the source columns.
2For each column in the ColumnSet, there should be an expression that generates the column. The first expression -here transforms the data, the second simply picks a column from the source without modification.
-
-
-
-
first_middle last
+                            
+
+
+ + + + + + + + + +
1When defining the ColumnSet, we are allowed to specify columns that are not present in the original DataFrame, as we are naming the result columns, not the source columns.
2 + For each column in the ColumnSet, there should be an expression that generates the column. The first expression here transforms the data, the second simply picks a column from the source + without modification. +
+
+
+
+
+first_middle last
 ------------ ------
 Jerry M      Cosin
-Joan         O'Hara
-
-
-
-

Generating columns row-by-row with RowMapper:

-
-
-
-
RowMapper mapper = (from, to) -> {
+Joan         O'Hara
+                                
+
+
+
+

Generating columns row-by-row with RowMapper:

+
+
+
+
RowMapper mapper = (from, to) -> {
     String middle = from.get("middle") != null
             ? " " + from.get("middle")
             : "";
@@ -1638,124 +1413,138 @@ 

Transform Columns

DataFrame df1 = df .cols("first_middle", "last") .select(mapper);
-
-
-
-

There’s also a way to generating columns row-by-row with per-column array of RowToValueMapper. It is not very different -from what we’ve seen so far, so we are leaving this as an exercise for the reader.

-
-
-
-

Split Columns

-
-

Values can be split using an expression into iterable or array elements, and new columns can be generated -from those elements, thus "expanding" each row. Here is an example of handling Lists of values (List is, of course, -an Iterable) :

-
-
-
-
DataFrame df = DataFrame.foldByRow("name", "phones").of(
+                            
+
+
+

+ There’s also a way to generating columns row-by-row with per-column array of RowToValueMapper. It is not very different from what we’ve seen so far, so we are leaving this as an + exercise for the reader. +

+
+
+
+

Split Columns

+
+

+ Values can be split using an expression into iterable or array elements, and new columns can be generated from those elements, thus "expanding" each row. Here is an example of handling Lists of values ( + List is, of course, an Iterable) : +

+
+
+
+
DataFrame df = DataFrame.foldByRow("name", "phones").of(
         "Cosin", List.of("111-555-5555","111-666-6666","111-777-7777"),
         "O'Hara", List.of("222-555-5555"));
 
 DataFrame df1 = df
         .cols("primary_phone", "secondary_phone")
         .selectExpand($col("phones"));
-
-
-
- - - - - -
1selectExpand(..) takes an expression that returns a column with lists. If each List has more than 2 numbers, -the rest are ignored, if less - nulls are used for the missing elements
-
-
-
-
primary_phone secondary_phone
+                            
+
+
+ + + + + +
1selectExpand(..) takes an expression that returns a column with lists. If each List has more than 2 numbers, the rest are ignored, if less - nulls are used for the missing elements
+
+
+
+
+primary_phone secondary_phone
 ------------- ---------------
 111-555-5555  111-666-6666
-222-555-5555  null
-
-
-
-

If you don’t know the exact number of phones, but would like to capture them all in separate columns, do not -specify any explicit names (as mentioned in the implicit column selection chapter). -As many columns as needed to fit the longest array of phone numbers will be generated on the fly, and the names will -be assigned dynamically:

-
-
-
-
DataFrame df1 = df
+222-555-5555  null
+                                
+
+
+
+

+ If you don’t know the exact number of phones, but would like to capture them all in separate columns, do not specify any explicit names (as mentioned in the + implicit column selection chapter). As many columns as needed to fit the longest array of phone numbers will be generated on the fly, and the names will be assigned + dynamically: +

+
+
+
+
DataFrame df1 = df
         .cols() (1)
         .selectExpand($col("phones"));
-
-
-
- - - - - -
1Not specifying any columns will result in split columns generated dynamically.
-
-
-
-
0            1            2
+                            
+
+
+ + + + + +
1Not specifying any columns will result in split columns generated dynamically.
+
+
+
+
+0            1            2
 ------------ ------------ ------------
 111-555-5555 111-666-6666 111-777-7777
-222-555-5555 null         null
-
-
-
-

If the phone numbers were provided as a comma-separated String instead of a List, you can split the String into an -array of numbers using the split(..) expression, and use selectExpandArray(..) to generate the columns:

-
-
-
-
DataFrame df = DataFrame.foldByRow("name", "phones").of(
+222-555-5555 null         null
+                                
+
+
+
+

+ If the phone numbers were provided as a comma-separated String instead of a List, you can split the String into an array of numbers using the split(..) expression, and + use selectExpandArray(..) to generate the columns: +

+
+
+
+
DataFrame df = DataFrame.foldByRow("name", "phones").of(
         "Cosin", "111-555-5555,111-666-6666,111-777-7777",
         "O'Hara", "222-555-5555");
 
 DataFrame df1 = df
         .cols("primary_phone", "secondary_phone")
         .selectExpandArray($str("phones").split(','));
-
-
-
-
-

Merge Columns

-
-

The ColumnSet operations demonstrated so far resulted in discarding the source DataFrame, and returning only the -columns defined within the set. But often we would like to recombine transformed ColumnSet with the original -DataFrame. Any in-place data cleanup, data set enrichment, etc. fall in this category.

-
-
- - - - - -
- - -Of course, we call them "in-place" only in a logical sense, as DataFrame is immutable, and all modifications -result in creation of a new instance. -
-
-
-

For this ColumnSet provides a number of "merging" methods. All the ColumnsSet methods that do not start with -select…​ are performing a merge with the source DataFrame. In fact, all the renaming and data transformation -operations that we just discussed can also be executed as "merges".

-
-
-

Here is an example of cleaning up a data set and adding a new column:

-
-
-
-
DataFrame df = DataFrame.foldByRow("first", "last", "middle").of(
+                            
+
+
+
+

Merge Columns

+
+

+ The ColumnSet operations demonstrated so far resulted in discarding the source DataFrame, and returning only the columns defined within the set. But often we would like to recombine + transformed ColumnSet with the original DataFrame. Any in-place data cleanup, data set enrichment, etc. fall in this category. +

+
+
+ + + + + +
+ + Of course, we call them "in-place" only in a logical sense, as DataFrame is immutable, and all modifications result in creation of a new instance.
+
+
+

+ For this ColumnSet provides a number of "merging" methods. All the ColumnsSet methods that do not start with select…​ are performing a merge with the source + DataFrame. In fact, all the renaming and data transformation operations that we just discussed can also be executed as "merges". +

+
+
+

Here is an example of cleaning up a data set and adding a new column:

+
+
+
+
DataFrame df = DataFrame.foldByRow("first", "last", "middle").of(
         "jerry", "cosin", "M",
         "Joan", "O'Hara", null);
 
@@ -1771,320 +1560,354 @@ 

Merge Columns

cleanup.apply("first"), concat($str("first"), $val(" "), $str("last")) );
-
-
-
- - - - - - - - - - - - - -
1An expression that capitalizes the first letter of the name
2The columns we are going to generate or transform
3Instead of select(..), use map(..) to merge to the original DataFrame
-
-
-

Now let’s check the result. The first two columns in the ColumnSet ("last", "first") were already present in the -DataFrame, so they got replaced with the cleaned up versions. The last column ("full") was new, so it was appended to -the right side of the DataFrame:

-
-
-
-
first last   middle full
+                            
+
+
+ + + + + + + + + + + + + +
1An expression that capitalizes the first letter of the name
2The columns we are going to generate or transform
3Instead of select(..), use map(..) to merge to the original DataFrame
+
+
+

+ Now let’s check the result. The first two columns in the ColumnSet ("last", "first") were already present in the DataFrame, so they got replaced with the cleaned up versions. The last + column ("full") was new, so it was appended to the right side of the DataFrame: +

+
+
+
+
+first last   middle full
 ----- ------ ------ -----------
 Jerry Cosin  M      jerry cosin
-Joan  O'Hara null   Joan O'Hara
-
-
-
-

General merge rules:

-
-
-
    -
  • -

    Merging is done by name. DataFrame’s columns with matching names are replaced with new versions, columns that -are not a part of the ColumnSet are left unchanged, and columns in the ColumnSet, but not in the DataFrame, are -appended on the right.

    -
  • -
  • -

    The order of the existing columns in the ColumnSet has no effect on the order of replaced columns (i.e. they are -placed in their original positions). The relative order of appended columns is respected.

    -
  • -
  • -

    All transformation operations (such as expressions in our example) are applied to the original DataFrame columns -(and can reference those outside the ColumnSet), but do not see the transformed columns. This is why there is a -lowercase name in the resulting "full" column.

    -
  • -
-
-
-

What if we want to ensure that all of the ColumnSet columns are appended, and no replacement of the original columns -occurs? For this you can manually specify ColumnSet labels that are not found in the source DataFrame, or you can use -DataFrame.colsAppend(..), and DFLib itself will ensure that the names are unique, appending _ to each label until -it doesn’t overlap with anything already in the DataFrame:

-
-
-
-
DataFrame df1 = df
+Joan  O'Hara null   Joan O'Hara
+                                
+
+
+
+

General merge rules:

+
+
+
    +
  • +

    + Merging is done by name. DataFrame’s columns with matching names are replaced with new versions, columns that are not a part of the ColumnSet are left unchanged, + and columns in the ColumnSet, but not in the DataFrame, are appended on the right. +

    +
  • +
  • +

    + The order of the existing columns in the ColumnSet has no effect on the order of replaced columns (i.e. they are placed in their original positions). The relative order of appended + columns is respected. +

    +
  • +
  • +

    + All transformation operations (such as expressions in our example) are applied to the original DataFrame columns (and can reference those outside the ColumnSet), but do not see the + transformed columns. This is why there is a lowercase name in the resulting "full" column. +

    +
  • +
+
+
+

+ What if we want to ensure that all of the ColumnSet columns are appended, and no replacement of the original columns occurs? For this you can manually specify ColumnSet labels that + are not found in the source DataFrame, or you can use DataFrame.colsAppend(..), and DFLib itself will ensure that the names are unique, appending _ to each label until it + doesn’t overlap with anything already in the DataFrame: +

+
+
+
+
DataFrame df1 = df
         .colsAppend("last", "first") (1)
         .map(
                 cleanup.apply("last"),
                 cleanup.apply("first")
         );
-
-
-
- - - - - -
1Using colsAppend(..) instead of cols() ensures that both pre- and post-cleanup columns are preserved.
-
-
-
-
first last   middle last_  first_
+                            
+
+
+ + + + + +
1Using colsAppend(..) instead of cols() ensures that both pre- and post-cleanup columns are preserved.
+
+
+
+
+first last   middle last_  first_
 ----- ------ ------ ------ ------
 jerry cosin  M      Cosin  Jerry
-Joan  O'Hara null   O'Hara Joan
-
-
-
- - - - - -
- - -Since we’ve already discussed various flavors of column renaming and transformation in the context of select(..), -we leave it as an exercise for the user to explore their "merging" counterparts - as(..), map(..), expand(..), etc. -
-
-
-
-

Compact Columns

-
-

This transformation converts columns to primitives. The values in the columns to be converted can be Numbers, Booleans -or Strings (or objects with toString() that can be parsed to a primitive type):

-
-
-
-
DataFrame df = DataFrame.foldByRow("year", "sales").of(
+Joan  O'Hara null   O'Hara Joan
+                                
+
+
+
+ + + + + +
+ + + Since we’ve already discussed various flavors of column renaming and transformation in the context of select(..), we leave it as an exercise for the user to explore their "merging" + counterparts - as(..), map(..), expand(..), etc. +
+
+
+
+

Compact Columns

+
+

+ This transformation converts columns to primitives. The values in the columns to be converted can be Numbers, Booleans or Strings (or objects with toString() that can be parsed to a primitive + type): +

+
+
+
+
DataFrame df = DataFrame.foldByRow("year", "sales").of(
         "2022", "2005365.01",
         "2023", "4355098.75");
 
 DataFrame df1 = df
         .cols("year").compactInt(0) (1)
         .cols("sales").compactDouble(0.);
-
-
-
- - - - - -
1The argument to compact is the primitive value to use if the value being converted is null
-
-
- - - - - -
- - -compactInt(..), compactDouble(..) and other similar methods are not just syntactic sugar. They convert -DataFrame columns to primitive Series, that take significantly less memory (hence, the name "compact") and compute -most operations faster than Object-based Series. So you should consider using them where possible. -
-
-
-
-

Fill Nulls

-
-

As a part of a dataset cleanup effort, you might want to replace null values with something meaningful. For this you -may have to guess or maybe extrapolate the existing values based on some internal knowledge of the dataset. To help with -this task, ColumnSet provides a few methods discussed here.

-
-
-

Filling nulls with a constant value:

-
-
-
-
DataFrame df = DataFrame.foldByRow("c1", "c2").of(
+                            
+
+
+ + + + + +
1The argument to compact is the primitive value to use if the value being converted is null
+
+
+ + + + + +
+ + + compactInt(..), compactDouble(..) and other similar methods are not just syntactic sugar. They convert DataFrame columns to primitive Series, that take + significantly less memory (hence, the name "compact") and compute most operations faster than Object-based Series. So you should consider using them where possible. +
+
+
+
+

Fill Nulls

+
+

+ As a part of a dataset cleanup effort, you might want to replace null values with something meaningful. For this you may have to guess or maybe extrapolate the existing values based on some + internal knowledge of the dataset. To help with this task, ColumnSet provides a few methods discussed here. +

+
+
+

Filling nulls with a constant value:

+
+
+
+
DataFrame df = DataFrame.foldByRow("c1", "c2").of(
         "a1", "a2",
         null, null,
         "b1", "b2");
 
 DataFrame clean = df.cols("c1", "c2").fillNulls("X");
-
-
-
-
-
c1 c2
+                            
+
+
+
+
+c1 c2
 -- --
 a1 a2
 X  X
-b1 b2
-
-
-
-

Filling nulls with values adjacent to the cells with nulls:

-
-
-
-
DataFrame clean = df
+b1 b2
+                                
+
+
+
+

Filling nulls with values adjacent to the cells with nulls:

+
+
+
+
DataFrame clean = df
         .cols("c1").fillNullsBackwards() (1)
         .cols("c2").fillNullsForward(); (2)
-
-
-
- - - - - - - - - -
1fillNullsForward(..) takes a non-null value preceding the null cell and uses it to replace nulls.
2fillNullsBackwards(..) uses the first non-null value following the null cell.
-
-
-
-
c1 c2
+                            
+
+
+ + + + + + + + + +
1fillNullsForward(..) takes a non-null value preceding the null cell and uses it to replace nulls.
2fillNullsBackwards(..) uses the first non-null value following the null cell.
+
+
+
+
+c1 c2
 -- --
 a1 a2
 b1 a2
-b1 b2
-
-
-
-

There’s also a way to fill nulls from another Series object used as a "mask" :

-
-
-
-
Series<String> mask = Series.of("A", "B", "C", "D");
+b1 b2
+                                
+
+
+
+

There’s also a way to fill nulls from another Series object used as a "mask" :

+
+
+
+
Series<String> mask = Series.of("A", "B", "C", "D");
 DataFrame clean = df.cols("c1", "c2").fillNullsFromSeries(mask);
-
-
-
-
-
c1 c2
+                            
+
+
+
+
+c1 c2
 -- --
 a1 a2
 B  B
-b1 b2
-
-
-
- - - - - -
- - -The "mask" Series can be longer or shorter than the DataFrame height. Values are aligned by position -starting at zero. -
-
-
-

Finally, we can use an Exp to calculate values for nulls:

-
-
-
-
        DataFrame df = DataFrame.foldByRow("c1", "c2").of(
+b1 b2
+                                
+
+
+
+ + + + + +
+ + The "mask" Series can be longer or shorter than the DataFrame height. Values are aligned by position starting at zero.
+
+
+

Finally, we can use an Exp to calculate values for nulls:

+
+
+
+
        DataFrame df = DataFrame.foldByRow("c1", "c2").of(
                 "a1", "a2",
                 null, null,
                 "b1", "b2");
 
         DataFrame clean = df.cols("c1", "c2")
                 .fillNullsWithExp(rowNum()); (1)
-
-
-
- - - - - -
1Exp.rowNum() generates a Series with row numbers, so the nulls are replaced by the number denoting their position -in the DataFrame:
-
-
-
-
c1 c2
+                            
+
+
+ + + + + +
1Exp.rowNum() generates a Series with row numbers, so the nulls are replaced by the number denoting their position in the DataFrame:
+
+
+
+
+c1 c2
 -- --
 a1 a2
 2  2
-b1 b2
-
-
-
-
-

Drop Columns

-
-

To get rid of certain columns in a DataFrame, you can either select the columns you would want to drop and call drop() -or select the columns you’d want to remain and call select():

-
-
-
-
DataFrame df1 = df.cols("middle").drop();
-
-
-
-
-
DataFrame df1 = df.colsExcept("middle").select();
-
-
-
-

In both cases, the result is the same:

-
-
-
-
first last
+b1 b2
+                                
+
+
+
+
+

Drop Columns

+
+

+ To get rid of certain columns in a DataFrame, you can either select the columns you would want to drop and call drop() or select the columns you’d want to remain and call select(): +

+
+
+
+
DataFrame df1 = df.cols("middle").drop();
+
+
+
+
+
DataFrame df1 = df.colsExcept("middle").select();
+
+
+
+

In both cases, the result is the same:

+
+
+
+
+first last
 ----- ------
 Jerry Cosin
-Joan  O'Hara
-
-
-
-
-
-
-

Row Operations

-
-
-

Just like with columns, row operations first define a RowSet, execute some transformation, and then are either -merged back with the original DataFrame, or "selected" as a standalone one. RowSet and ColumnSet have other -similarities (e.g. the ability to evaluate expressions by column, and create RowColumnSet), but RowSet is also -rather different in how its rows are picked and what other operations are available.

-
-
-

Pick Rows

-
-

Rows are picked either by condition, by positions or as a range. Let’s take a look at each style…​

-
-
-

By Condition

-
-

A Condition (a boolean Exp) can be used to pick matching rows:

-
-
-
-
DataFrame df = DataFrame.foldByRow("first", "last", "middle").of(
+Joan  O'Hara
+                                
+
+
+
+
+
+
+
+

Row Operations

+
+

+ Just like with columns, row operations first define a RowSet, execute some transformation, and then are either merged back with the original DataFrame, or "selected" as a standalone one. + RowSet and ColumnSet have other similarities (e.g. the ability to evaluate expressions by column, and create RowColumnSet), but RowSet is also rather different + in how its rows are picked and what other operations are available. +

+
+
+
+
+

Pick Rows

+
+

Rows are picked either by condition, by positions or as a range. Let’s take a look at each style…​

+
+
+

By Condition

+
+

A Condition (a boolean Exp) can be used to pick matching rows:

+
+
+
+
DataFrame df = DataFrame.foldByRow("first", "last", "middle").of(
         "Jerry", "Cosin", "M",
         "Juliana", "Walewski", null,
         "Joan", "O'Hara", "P");
@@ -2092,184 +1915,202 @@ 

By Condition

DataFrame df1 = df .rows($str("last").startsWith("W").eval(df)) (1) .select(); (2)
-
-
-
- - - - - - - - - -
1Applies a Condition to the DataFrame, to include matching rows in the RowSet.
2Returns a new DataFrame matching the RowSet
-
-
-
-
first   last     middle
+                                
+
+
+ + + + + + + + + +
1Applies a Condition to the DataFrame, to include matching rows in the RowSet.
2Returns a new DataFrame matching the RowSet
+
+
+
+
+first   last     middle
 ------- -------- ------
-Juliana Walewski null
-
-
-
-

Another form of condition is a "predicate" lambda (a RowPredicate object), evaluated row-by-row:

-
-
-
-
DataFrame df1 = df
+Juliana Walewski null
+                                    
+
+
+
+

Another form of condition is a "predicate" lambda (a RowPredicate object), evaluated row-by-row:

+
+
+
+
DataFrame df1 = df
         .rows(r -> r.get("last", String.class).startsWith("W"))
         .select();
-
-
-
-

A condition can be precalculated as a BooleanSeries. A common scenario is calling locate() on another Series -or a DataFrame / RowSet to build a BooleanSeries "selector", and then using it to pick rows from another DataFrame:

-
-
-
-
IntSeries salaries = Series.ofInt(100000, 50000, 45600); (1)
+                                
+
+
+

+ A condition can be precalculated as a BooleanSeries. A common scenario is calling locate() on another Series or a DataFrame / RowSet to + build a BooleanSeries "selector", and then using it to pick rows from another DataFrame: +

+
+
+
+
IntSeries salaries = Series.ofInt(100000, 50000, 45600); (1)
 BooleanSeries selector = salaries.locateInt(s -> s > 49999); (2)
 
 DataFrame df1 = df.rows(selector).select();
-
-
-
- - - - - - - - - -
1A Series of salaries with elements positions matching row positions in the persons DataFrame.
2Precalculates a reusable "selector"
-
-
-
-
first   last     middle
+                                
+
+
+ + + + + + + + + +
1A Series of salaries with elements positions matching row positions in the persons DataFrame.
2Precalculates a reusable "selector"
+
+
+
+
+first   last     middle
 ------- -------- ------
 Jerry   Cosin    M
-Juliana Walewski null
-
-
-
-
-

By Positions

-
-

Here is an example of RowSet defined using an array of row positions.

-
-
-
-
DataFrame df1 = df
+Juliana Walewski null
+                                    
+
+
+
+
+

By Positions

+
+

Here is an example of RowSet defined using an array of row positions.

+
+
+
+
DataFrame df1 = df
         .rows(2, 0, 2) (1)
         .select();
-
-
-
- - - - - -
1An int[] that defines a RowSet
-
-
-
-
first last   middle
+                                
+
+
+ + + + + +
1An int[] that defines a RowSet
+
+
+
+
+first last   middle
 ----- ------ ------
 Joan  O'Hara P
 Jerry Cosin  M
-Joan  O'Hara P
-
-
-
- - - - - -
- - -An array of positions is also a kind of "condition". The main difference is that it allows -to reorder rows by specifying positions in a desired sequence and/or duplicate rows by referencing the same position more -than once. Both features are demonstrated in the example above. -
-
-
-

Instead of an array, positions can be defined as an IntSeries "index". Just like with conditions, it is often -calculated from another Series or DataFrame / RowSet:

-
-
-
-
IntSeries selector = salaries.indexInt(s -> s > 49999); (1)
+Joan  O'Hara P
+                                    
+
+
+
+ + + + + +
+ + + An array of positions is also a kind of "condition". The main difference is that it allows to reorder rows by specifying positions in a desired sequence and/or duplicate rows by referencing the + same position more than once. Both features are demonstrated in the example above. +
+
+
+

+ Instead of an array, positions can be defined as an IntSeries "index". Just like with conditions, it is often calculated from another Series or DataFrame / + RowSet: +

+
+
+
+
IntSeries selector = salaries.indexInt(s -> s > 49999); (1)
 
 DataFrame df1 = df.rows(selector).select();
-
-
-
- - - - - -
1Precalculates a reusable "selector" with positions. Uses a Series of salaries that is not a part of "our" DataFrame
-
-
-
-
first   last     middle
+                                
+
+
+ + + + + +
1Precalculates a reusable "selector" with positions. Uses a Series of salaries that is not a part of "our" DataFrame
+
+
+
+
+first   last     middle
 ------- -------- ------
 Jerry   Cosin    M
-Juliana Walewski null
-
-
-
-
-

As a Range

-
-

Finally, rows can be selected as a continuous range of row positions:

-
-
-
-
DataFrame df1 = df
+Juliana Walewski null
+                                    
+
+
+
+
+

As a Range

+
+

Finally, rows can be selected as a continuous range of row positions:

+
+
+
+
DataFrame df1 = df
         .rowsRange(0, 2) (1)
         .select();
-
-
-
- - - - - -
1The range is defined by two ints: its starting index and the index, following its last index.
-
-
-
-
first   last     middle
+                                
+
+
+ + + + + +
1The range is defined by two ints: its starting index and the index, following its last index.
+
+
+
+
+first   last     middle
 ------- -------- ------
 Jerry   Cosin    M
-Juliana Walewski null
-
-
-
-
-
-

Transform Rows

-
-

Just like ColumnSet, RowSet defines a number of column- and row-based transformations. And just like with -ColumnSet, each transformation can be invoked as a "select" or a "merge", returning either the RowSet rows or all -rows from the original DataFrame. Here we will demonstrate selecting operations, and will talk about merging next.

-
-
-

Transforming with column expressions:

-
-
-
-
DataFrame df = DataFrame.foldByRow("last", "age", "retires_soon").of(
+Juliana Walewski null
+                                    
+
+
+
+
+
+

Transform Rows

+
+

+ Just like ColumnSet, RowSet defines a number of column- and row-based transformations. And just like with ColumnSet, each transformation can be invoked as a "select" or + a "merge", returning either the RowSet rows or all rows from the original DataFrame. Here we will demonstrate selecting operations, and will talk about merging next. +

+
+
+

Transforming with column expressions:

+
+
+
+
DataFrame df = DataFrame.foldByRow("last", "age", "retires_soon").of(
         "Cosin", 61, false,
         "Walewski", 25, false,
         "O'Hara", 59, false);
@@ -2280,44 +2121,45 @@ 

Transform Rows

$col("last"), $col("age"), $val(true)); (1)
-
-
-
- - - - - -
1Select the rows from each column without changes, except the last one, where we flip the "retires_soon" flag -to "true".
-
-
-
-
last   age retires_soon
+                            
+
+
+ + + + + +
1Select the rows from each column without changes, except the last one, where we flip the "retires_soon" flag to "true".
+
+
+
+
+last   age retires_soon
 ------ --- ------------
 Cosin   61         true
-O'Hara  59         true
-
-
-
- - - - - -
- - -We had to specify an expression for every column in the DataFrame, even though only a single column got transformed. -This code can be improved by creating a RowColumnSet described here. -
-
-
-

Transforming row-by-row with RowMapper:

-
-
-
-
RowMapper mapper = (from, to) -> {
+O'Hara  59         true
+                                
+
+
+
+ + + + + +
+ + + We had to specify an expression for every column in the DataFrame, even though only a single column got transformed. This code can be improved by creating a RowColumnSet + described here. +
+
+
+

Transforming row-by-row with RowMapper:

+
+
+
+
RowMapper mapper = (from, to) -> {
     from.copy(to);
     to.set("retires_soon", true);
 };
@@ -2325,22 +2167,28 @@ 

Transform Rows

DataFrame df1 = df .rows($int("age").mapConditionVal(a -> 67 - a < 10)) .select(mapper);
-
-
-
-

There’s also a way to transforming row-by-row with per-column array of RowToValueMapper. It is not very different -from what we’ve seen so far, so we are leaving this as an exercise for the reader.

-
-
-
-

Merge Rows

-
-

If we want to merge the result of a RowSet transformation to the original DataFrame, we’ll need to use methods -like map(..) (i.e. those that do not start with select):

-
-
-
-
DataFrame df = DataFrame.foldByRow("last", "age", "retires_soon").of(
+                            
+
+
+

+ There’s also a way to transforming row-by-row with per-column array of RowToValueMapper. It is not very different from what we’ve seen so far, so we are leaving this as an exercise + for the reader. +

+
+
+
+

Merge Rows

+
+

+ If we want to merge the result of a RowSet transformation to the original DataFrame, we’ll need to use methods like map(..) (i.e. those that do not start with + select): +

+
+
+
+
DataFrame df = DataFrame.foldByRow("last", "age", "retires_soon").of(
         "Cosin", 61, false,
         "Walewski", 25, false,
         "O'Hara", 59, false);
@@ -2351,420 +2199,458 @@ 

Merge Rows

$col("last"), $col("age"), $val(true));
-
-
-
-
-
last     age retires_soon
+                            
+
+
+
+
+last     age retires_soon
 -------- --- ------------
 Cosin     61         true
 Walewski  25        false
-O'Hara    59         true
-
-
-
-

Merging rows is done similar to columns, except rows have no name labels, so it is done by position. General merge rules:

-
-
-
    -
  • -

    Merging is done by row position. DataFrame rows with matching positions are replaced with transformed versions, rows that -are not a part of the RowSet are left unchanged, and rows in the RowSet, but not in the DataFrame (e.g., -intentionally duplicated rows or split rows) are appended in the bottom.

    -
  • -
  • -

    The order of the existing rows in the RowSet has no effect on the order of replaced rows (i.e. they are -placed in their original positions). The relative order of added rows is respected.

    -
  • -
-
-
- - - - - -
- - -Just like with columns, for most RowSet.select(..) methods there are "merging" counterparts, such as map(..), -expand(..), unique(..), etc. -
-
-
-
-

Split Rows

-
-

Splitting rows is somewhat simpler than splitting columns. The API takes a single column -(no expression is allowed), and splits any expandable values into new rows:

-
-
-
-
DataFrame df = DataFrame.foldByRow("name", "phones").of(
+O'Hara    59         true
+                                
+
+
+
+

Merging rows is done similar to columns, except rows have no name labels, so it is done by position. General merge rules:

+
+
+
    +
  • +

    + Merging is done by row position. DataFrame rows with matching positions are replaced with transformed versions, rows that are not a part of the RowSet are left unchanged, and rows in the + RowSet, but not in the DataFrame (e.g., intentionally duplicated rows or split rows) are appended in the bottom. +

    +
  • +
  • +

    + The order of the existing rows in the RowSet has no effect on the order of replaced rows (i.e. they are placed in their original positions). The relative order of added rows is respected. +

    +
  • +
+
+
+ + + + + +
+ + + Just like with columns, for most RowSet.select(..) methods there are "merging" counterparts, such as map(..), expand(..), unique(..), etc. +
+
+
+
+

Split Rows

+
+

Splitting rows is somewhat simpler than splitting columns. The API takes a single column (no expression is allowed), and splits any expandable values into new rows:

+
+
+
+
DataFrame df = DataFrame.foldByRow("name", "phones").of(
         "Cosin", List.of("111-555-5555", "111-666-6666", "111-777-7777"),
         "O'Hara", List.of("222-555-5555"));
 
 DataFrame df1 = df
         .rows()
         .selectExpand("phones");
-
-
-
-
-
name   phones
+                            
+
+
+
+
+name   phones
 ------ ------------
 Cosin  111-555-5555
 Cosin  111-666-6666
 Cosin  111-777-7777
-O'Hara 222-555-5555
-
-
-
- - - - - -
- - -The expansion column can contain scalars, arrays or iterables. -
-
-
-
-

Unique Rows

-
-

To deduplicate DataFrames, there’s an API to select fully or partially-unique rows. Uniqueness is checked either on -all columns of each row, or a preselected subset:

-
-
-
-
DataFrame df = DataFrame.foldByRow("first", "last").of(
+O'Hara 222-555-5555
+                                
+
+
+
+ + + + + +
+ + + The expansion column can contain scalars, arrays or iterables. +
+
+
+
+

Unique Rows

+
+

To deduplicate DataFrames, there’s an API to select fully or partially-unique rows. Uniqueness is checked either on all columns of each row, or a preselected subset:

+
+
+
+
DataFrame df = DataFrame.foldByRow("first", "last").of(
         "Jerry", "Cosin",
         "Jerry", "Jones",
         "Jerry", "Cosin",
         "Joan", "O'Hara");
 
 DataFrame df1 = df.rows().selectUnique(); (1)
-
-
-
- - - - - -
1Keep only fully unique rows
-
-
-
-
first last
+                            
+
+
+ + + + + +
1Keep only fully unique rows
+
+
+
+
+first last
 ----- ------
 Jerry Cosin
 Jerry Jones
-Joan  O'Hara
-
-
-
-
-
DataFrame df2 = df.rows().selectUnique("first"); (1)
-
-
-
- - - - - -
1Deduplicate first names. Picking the row of each earliest-encountered unique first name.
-
-
-
-
first last
+Joan  O'Hara
+                                
+
+
+
+
+
DataFrame df2 = df.rows().selectUnique("first"); (1)
+
+
+
+ + + + + +
1Deduplicate first names. Picking the row of each earliest-encountered unique first name.
+
+
+
+
+first last
 ----- ------
 Jerry Cosin
-Joan  O'Hara
-
-
-
-
-

Drop Rows

-
-

To get rid of certain rows in a DataFrame, you can either select the rows you would want to drop, and call drop() -or select the rows you’d want to remain and call select():

-
-
-
-
DataFrame df = DataFrame.foldByRow("first", "last", "middle").of(
+Joan  O'Hara
+                                
+
+
+
+
+

Drop Rows

+
+

To get rid of certain rows in a DataFrame, you can either select the rows you would want to drop, and call drop() or select the rows you’d want to remain and call select():

+
+
+
+
DataFrame df = DataFrame.foldByRow("first", "last", "middle").of(
         "Jerry", "Cosin", "M",
         "Juliana", "Walewski", null,
         "Joan", "O'Hara", "P");
 
 DataFrame df1 = df.rows($col("middle").isNull()).drop();
-
-
-
-
-
DataFrame df = DataFrame.foldByRow("first", "last", "middle").of(
+                            
+
+
+
+
DataFrame df = DataFrame.foldByRow("first", "last", "middle").of(
         "Jerry", "Cosin", "M",
         "Juliana", "Walewski", null,
         "Joan", "O'Hara", "P");
 
 DataFrame df1 = df.rowsExcept($col("middle").isNull()).select();
-
-
-
-

In both cases, the result is the same:

-
-
-
-
first last   middle
+                            
+
+
+

In both cases, the result is the same:

+
+
+
+
+first last   middle
 ----- ------ ------
 Jerry Cosin  M
-Joan  O'Hara P
-
-
-
-
-
-
-

Row and Column Operations

-
-
-

TODO

-
-
-

Pick Rows and Columns

-
-

TODO

-
-
-
-

Transform Rows and Columns

-
-

TODO

-
-
-
-

Merge Rows and Columns

-
-

TODO

-
-
-
-

Drop Rows and Columns

- -
-
-
-
-

Head and Tail

-
-
-

As we’ve seen previously, most operations within a single DataFrame are performed on row and column sets. Still, some -are applied to the DataFrame directly. head and tail are such examples.

-
-
-

When you only need the "first N" or "last M" rows of a DataFrame (or values of a Series) you can use head and tail -operations. Here is how to get the top 2 rows of a DataFrame:

-
-
-
-
DataFrame df = DataFrame.foldByRow("first", "last").of(
+Joan  O'Hara P
+                                
+
+
+
+
+
+
+
+

Row and Column Operations

+
+

TODO

+
+
+
+
+

Pick Rows and Columns

+
+

TODO

+
+
+
+

Transform Rows and Columns

+
+

TODO

+
+
+
+

Merge Rows and Columns

+
+

TODO

+
+
+
+

Drop Rows and Columns

+
+
+
+
+
+

Head and Tail

+
+

+ As we’ve seen previously, most operations within a single DataFrame are performed on row and column sets. Still, some are applied to the DataFrame directly. head and tail are such + examples. +

+
+
+

When you only need the "first N" or "last M" rows of a DataFrame (or values of a Series) you can use head and tail operations. Here is how to get the top 2 rows of a DataFrame:

+
+
+
+
DataFrame df = DataFrame.foldByRow("first", "last").of(
         "Jerry", "Cosin",
         "Juliana", "Walewski",
         "Joan", "O'Hara");
 
 DataFrame df1 = df.head(2); (1)
-
-
-
- - - - - -
1Returns a new DataFrame with two top rows of the original DataFrame.
-
-
-
-
first   last
+                        
+
+
+ + + + + +
1Returns a new DataFrame with two top rows of the original DataFrame.
+
+
+
+
+first   last
 ------- ---------
 Jerry   Cosin
-Juliana Walewski
-
-
-
-

tail works similarly, but returns the last N rows:

-
-
-
-
DataFrame df1 = df.tail(1);
-
-
-
-
-
first last
+Juliana Walewski
+                            
+
+
+
+

tail works similarly, but returns the last N rows:

+
+
+
+
DataFrame df1 = df.tail(1);
+
+
+
+
+
+first last
 ----- ------
 Joan  O'Hara
-1 row x 2 columns
-
-
-
-

The argument to either head(..) or tail(..) can be negative. -In which case the operation skips the specified number of elements either from the top or from the bottom, and returns the remaining elements. -The following code returns a new DataFrame after skipping the two top rows of the original DataFrame:

-
-
-
-
DataFrame df1 = df.head(-2);
-
-
-
-
-
first  last
+1 row x 2 columns
+                            
+
+
+
+

+ The argument to either head(..) or tail(..) can be negative. In which case the operation skips the specified number of elements either from the top or from the bottom, and returns the + remaining elements. The following code returns a new DataFrame after skipping the two top rows of the original DataFrame: +

+
+
+
+
DataFrame df1 = df.head(-2);
+
+
+
+
+
+first  last
 ------ ---------
 Joan  O'Hara
-1 row x 2 columns
-
-
-
- - - - - -
- - -Unlike index operations on arrays and lists in Java, head(..) and tail(..) are safe in regards to bounds checking. -They DO NOT throw exceptions when the length parameter is bigger than DataFrame height (or Series size), returning an empty DataFame (or Series) instead. -
-
-
- - - - - -
- - -Series also define head(..) and tail(..) that do what you’d expect, returning first or last N values. -
-
-
-
-
-

Sorting

-
-
-

You can sort values in Series, and sort rows in DataFrames. As is the case everywhere else in DFLib, sorting does -not alter the original object, and instead creates a new instance of either Series or DataFrame.

-
-
-

First let’s look at sorting Series…​

-
-
-

Sort Series

-
-

Series provides a sort method to sort its data using a sorter built from an expression:

-
-
-
-
// sort series by String length
+1 row x 2 columns
+                            
+
+
+
+ + + + + +
+ + + Unlike index operations on arrays and lists in Java, head(..) and tail(..) are safe in regards to bounds checking. They DO NOT throw exceptions when the length parameter is + bigger than DataFrame height (or Series size), returning an empty DataFame (or Series) instead. +
+
+
+ + + + + +
+ + Series also define head(..) and tail(..) that do what you’d expect, returning first or last N values.
+
+
+
+
+
+

Sorting

+
+

+ You can sort values in Series, and sort rows in DataFrames. As is the case everywhere else in DFLib, sorting does not alter the original object, and instead creates a new instance of + either Series or DataFrame. +

+
+
+

First let’s look at sorting Series…​

+
+
+
+
+

Sort Series

+
+

Series provides a sort method to sort its data using a sorter built from an expression:

+
+
+
+
// sort series by String length
 Series<String> s = Series
         .of("12", "1", "123")
         .sort($str("").mapVal(String::length).asc());
-
-
-
-

The result is a new Series with sorted data:

-
-
-
-
1
+                            
+
+
+

The result is a new Series with sorted data:

+
+
+
+
+1
 12
 123
-3 elements
-
-
-
-

Alternatively the same can be achieved using a Comparator:

-
-
-
-
Series<String> s = Series
+3 elements
+                                
+
+
+
+

Alternatively the same can be achieved using a Comparator:

+
+
+
+
Series<String> s = Series
         .of("12", "1", "123")
         .sort(Comparator.comparingInt(String::length));
-
-
-
-

The next example shows how to sort Series in the "natural" order (alphabetically for Strings):

-
-
-
-
Series<String> s = Series
+                            
+
+
+

The next example shows how to sort Series in the "natural" order (alphabetically for Strings):

+
+
+
+
Series<String> s = Series
         .of("c", "d", "a")
         .sort($str("").asc());
-
-
-
-
-
a
+                            
+
+
+
+
+a
 c
 d
-3 elements
-
-
-
-

Series of primitives have methods to sort values in the natural order without an explicit sorter or comparator:

-
-
-
-
LongSeries s = Series
+3 elements
+                                
+
+
+
+

Series of primitives have methods to sort values in the natural order without an explicit sorter or comparator:

+
+
+
+
LongSeries s = Series
         .ofLong(Long.MAX_VALUE, 15L, 0L)
         .sortLong();
-
-
-
-
-
                  0
+                            
+
+
+
+
+                  0
                  15
 9223372036854775807
-3 elements
-
-
-
-

Additionally, IntSeries has an optimized method to sort ints with a custom IntComparator.

-
-
-

Next we’ll check how to sort a DataFrame.

-
-
-
-

Sort DataFrame

-
-

Rows in a DataFrame can be sorted with one or more sorters.

-
-
- - - - - -
- - -Just like in other places where a Sorter might be -used, there is an assumption that the Sorter expression produces values that are either Java primitives or are -compatible with java.lang.Comparable (i.e., Strings, numbers, etc). -
-
-
-
-
DataFrame df = DataFrame.foldByRow("first", "last", "middle").of(
+3 elements
+                                
+
+
+
+

Additionally, IntSeries has an optimized method to sort ints with a custom IntComparator.

+
+
+

Next we’ll check how to sort a DataFrame.

+
+
+
+

Sort DataFrame

+
+

Rows in a DataFrame can be sorted with one or more sorters.

+
+
+ + + + + +
+ + + Just like in other places where a Sorter might be used, there is an assumption that the Sorter expression produces values that are either Java primitives or are compatible with + java.lang.Comparable (i.e., Strings, numbers, etc). +
+
+
+
+
DataFrame df = DataFrame.foldByRow("first", "last", "middle").of(
         "Jerry", "Cosin", "M",
         "Juliana", "Walewski", null,
         "Jerry", "Albert", null,
@@ -2773,134 +2659,153 @@ 

Sort DataFrame

DataFrame df1 = df.sort( $str("first").asc(), $str("last").asc());
-
-
-
-
-
first   last      middle
+                            
+
+
+
+
+first   last      middle
 ------- --------- ------
 Juliana Walewski  null
 Jerry   Albert    null
 Jerry   Cosin     M
-Joan    O'Hara    J
-
-
-
-

Alternatively sorting can be done by a single column name or an array of columns. The assumption is the same as above -- values in columns used for sorting must be either Java primitive or implement java.lang.Comparable.

-
-
-
-
DataFrame df = DataFrame.foldByRow("first", "last", "middle").of(
+Joan    O'Hara    J
+                                
+
+
+
+

+ Alternatively sorting can be done by a single column name or an array of columns. The assumption is the same as above - values in columns used for sorting must be either Java primitive or implement + java.lang.Comparable. +

+
+
+
+
DataFrame df = DataFrame.foldByRow("first", "last", "middle").of(
         "Jerry", "Cosin", "M",
         "Juliana", "Walewski", null,
         "Joan", "O'Hara", "J");
 
 DataFrame df1 = df.sort("first", true); (1)
-
-
-
- - - - - -
1The first argument is the column name (can also be column integer index), the second - a boolean indicating sort -direction (true for ascending, false - for descending order).
-
-
-
-
DataFrame df = DataFrame.foldByRow("first", "last", "middle").of(
+                            
+
+
+ + + + + +
1 + The first argument is the column name (can also be column integer index), the second - a boolean indicating sort direction (true for ascending, false - for descending order). +
+
+
+
+
DataFrame df = DataFrame.foldByRow("first", "last", "middle").of(
         "Jerry", "Cosin", "M",
         "Juliana", "Walewski", null,
         "Jerry", "Albert", null,
         "Joan", "O'Hara", "J");
 
 DataFrame df1 = df.sort(new String[]{"last", "first"}, new boolean[]{true, false});
-
-
-
-
-
-
-

Concatenation

-
-
-

There are various ways to combine data from multiple Series or DataFrames. The simplest is concatenation, -described in this chapter. Series can be concatenated together producing a longer Series. DataFrames can be concatenated -either vertically (along the rows axis) or horizontally (along the columns axis).

-
-
-

Concatenate Series

-
-

If you have a Series object and want to concatenate it with one or more other Series, you’d use Series.concat(..) -method:

-
-
-
-
Series<String> s1 = Series.of("x", "y", "z");
+                            
+
+
+
+
+
+
+

Concatenation

+
+

+ There are various ways to combine data from multiple Series or DataFrames. The simplest is concatenation, described in this chapter. Series can be concatenated together + producing a longer Series. DataFrames can be concatenated either vertically (along the rows axis) or horizontally (along the columns axis). +

+
+
+
+
+

Concatenate Series

+
+

If you have a Series object and want to concatenate it with one or more other Series, you’d use Series.concat(..) method:

+
+
+
+
Series<String> s1 = Series.of("x", "y", "z");
 Series<String> s2 = Series.of("a");
 Series<String> s3 = Series.of("m", "n");
 
 Series<String> sConcat = s1.concat(s2, s3);
-
-
-
-

The result is as expected contains values of all Series put together:

-
-
-
-
x
+                            
+
+
+

The result is as expected contains values of all Series put together:

+
+
+
+
+x
 y
 z
 a
 m
 n
-6 elements
-
-
-
-

If you have a collection or an array of Series and want to "glue" them together, you can use a static -SeriesConcat.concat(..):

-
-
-
-
Collection<Series<String>> ss = asList(
+6 elements
+                                
+
+
+
+

If you have a collection or an array of Series and want to "glue" them together, you can use a static SeriesConcat.concat(..):

+
+
+
+
Collection<Series<String>> ss = asList(
         Series.of("x", "y", "z"),
         Series.of("a"),
         Series.of("m", "n"));
 
 Series<String> sConcat = SeriesConcat.concat(ss);
-
-
-
-

The result is the same as in the previous example. Also you can concatenate Series with itself:

-
-
-
-
Series<String> s = Series.of("x", "y");
+                            
+
+
+

The result is the same as in the previous example. Also you can concatenate Series with itself:

+
+
+
+
Series<String> s = Series.of("x", "y");
 Series<String> sConcat = s.concat(s);
-
-
-
-
-
x
+                            
+
+
+
+
+x
 y
 x
 y
-4 elements
-
-
-
-
-

Concatenate DataFrames

-
-

DataFrame offers two options for concatenation - vertical (stacking DataFrames on top of each other) and horizontal -(putting them next to each other). Let’s see some examples..

-
-
-
-
DataFrame df1 = DataFrame.foldByRow("a", "b").of(
+4 elements
+                                
+
+
+
+
+

Concatenate DataFrames

+
+

DataFrame offers two options for concatenation - vertical (stacking DataFrames on top of each other) and horizontal (putting them next to each other). Let’s see some examples..

+
+
+
+
DataFrame df1 = DataFrame.foldByRow("a", "b").of(
         1, 2,
         3, 4);
 
@@ -2909,91 +2814,94 @@ 

Concatenate DataFrames

7, 8); DataFrame dfv = df1.vConcat(df2); (1)
-
-
-
- - - - - -
1Concatenate this and another DataFrame vertically. The argument is a vararg, so more than one DataFrame can be -passed to the method.
-
-
-
-
a b
+                            
+
+
+ + + + + +
1Concatenate this and another DataFrame vertically. The argument is a vararg, so more than one DataFrame can be passed to the method.
+
+
+
+
+a b
 - -
 1 2
 3 4
 5 6
-7 8
-
-
-
-
-
DataFrame dfh = df1.hConcat(df2); (1)
-
-
-
- - - - - -
1Concatenate this and another DataFrame horizontally. The argument is a vararg, so more than one DataFrame can be -passed to the method.
-
-
-
-
a b a_ b_
+7 8
+                                
+
+
+
+
+
DataFrame dfh = df1.hConcat(df2); (1)
+
+
+
+ + + + + +
1Concatenate this and another DataFrame horizontally. The argument is a vararg, so more than one DataFrame can be passed to the method.
+
+
+
+
+a b a_ b_
 - - -- --
 1 2 5  6
-3 4 7  8
-
-
-
- - - - - -
- - -Since both df1 and df2 had the same column names, and a DataFrame must only have unique columns, _ suffix was -automatically appended to the conflicting columns in the resulting DataFrame. We will see this auto-renaming behavior -in other places, such as joins. -
-
-
-

So far all our concatenation examples consisted of DataFrames that had matching dimensions (same number and names of -columns for vConcat and same number of rows for hConcat). But what if concatenated DataFrames are shaped or named -differently?

-
-
-

Concat methods may take an extra "how" parameter to define concatenation semantics. The type of the "how" parameter is -JoinType, and can be one of inner (default), left, right, full.

-
-
- - - - - -
- - -We’ll see JoinType again soon when discussing joins. Concatenation and joins are related fairly -closely. -
-
-
-

Let’s look how this works with vConcat:

-
-
-
-
DataFrame df1 = DataFrame.foldByRow("b", "a").of(
+3 4 7  8
+                                
+
+
+
+ + + + + +
+ + + Since both df1 and df2 had the same column names, and a DataFrame must only have unique columns, _ suffix was automatically appended to the conflicting columns + in the resulting DataFrame. We will see this auto-renaming behavior in other places, such as joins. +
+
+
+

+ So far all our concatenation examples consisted of DataFrames that had matching dimensions (same number and names of columns for vConcat and same number of rows for hConcat). But + what if concatenated DataFrames are shaped or named differently? +

+
+
+

+ Concat methods may take an extra "how" parameter to define concatenation semantics. The type of the "how" parameter is JoinType, and can be one of inner (default), left, + right, full. +

+
+
+ + + + + +
+ + We’ll see JoinType again soon when discussing joins. Concatenation and joins are related fairly closely.
+
+
+

Let’s look how this works with vConcat:

+
+
+
+
DataFrame df1 = DataFrame.foldByRow("b", "a").of(
         1, 2,
         3, 4);
 
@@ -3002,64 +2910,72 @@ 

Concatenate DataFrames

7, 8); DataFrame dfv = df1.vConcat(JoinType.inner, df2); (2)
-
-
-
- - - - - - - - - -
1df1 column names are "b" and "c", while df2 - "a" and "c".
2Explicitly passing JoinType.inner to vConcat. It is done to demonstrate the point. Since inner is the -default, omitting it will not change the outcome.
-
-
-
-
a
+                            
+
+
+ + + + + + + + + +
1df1 column names are "b" and "c", while df2 - "a" and "c".
2Explicitly passing JoinType.inner to vConcat. It is done to demonstrate the point. Since inner is the default, omitting it will not change the outcome.
+
+
+
+
+a
 -
 2
 4
 5
-7
-
-
-
-

As you can see in the result, the behavior of inner join is to only keep columns that are present in both df1 and -df2. Columns are joined by name, regardless of their order (though the order of columns is preserved in the result, -following the order in each DataFame being joined, left to right).

-
-
-

Changing semantics from inner to left gives us all the columns of the leftmost DataFrame, but those columns that -are missing from the concatenated DataFrame are filled with nulls in the corresponding positions:

-
-
-
-
DataFrame dfv = df1.vConcat(JoinType.left, df2);
-
-
-
-
-
b    a
+7
+                                
+
+
+
+

+ As you can see in the result, the behavior of inner join is to only keep columns that are present in both df1 and df2. Columns are joined by name, regardless of their + order (though the order of columns is preserved in the result, following the order in each DataFame being joined, left to right). +

+
+
+

+ Changing semantics from inner to left gives us all the columns of the leftmost DataFrame, but those columns that are missing from the concatenated DataFrame are filled with nulls in + the corresponding positions: +

+
+
+
+
DataFrame dfv = df1.vConcat(JoinType.left, df2);
+
+
+
+
+
+b    a
 ---- -
 1    2
 3    4
 null 5
-null 7
-
-
-
-

Leaving it as an exercise for the reader to try right and full joins.

-
-
-

hConcat works similarly, however concatenation is done by row position, as there are no row names:

-
-
-
-
DataFrame df1 = DataFrame.foldByRow("a", "b").of(
+null 7
+                                
+
+
+
+

Leaving it as an exercise for the reader to try right and full joins.

+
+
+

hConcat works similarly, however concatenation is done by row position, as there are no row names:

+
+
+
+
DataFrame df1 = DataFrame.foldByRow("a", "b").of(
         1, 2,
         3, 4,
         5, 6);
@@ -3069,33 +2985,38 @@ 

Concatenate DataFrames

9, 10); DataFrame dfv = df1.hConcat(JoinType.left, df2);
-
-
-
-
-
a b c    d
+                            
+
+
+
+
+a b c    d
 - - ---- ----
 1 2 7    8
 3 4 9    10
-5 6 null null
-
-
-
-
-
-
-

Joins

-
-
-

If you worked with relational databases, you know what "joins" are. This is a way to combine related rows from -multiple tables. DataFrames can be joined together, just like two DB tables. DFLib provides 4 familiar flavors of joins: -inner, left (outer), right (outer) and full (outer).

-
-
-

Inner Joins

-
-
-
DataFrame left = DataFrame
+5 6 null null
+                                
+
+
+
+
+
+
+
+

Joins

+
+

+ If you worked with relational databases, you know what "joins" are. This is a way to combine related rows from multiple tables. DataFrames can be joined together, just like two DB tables. DFLib provides 4 + familiar flavors of joins: inner, left (outer), right (outer) and full (outer). +

+
+
+
+
+

Inner Joins

+
+
+
DataFrame left = DataFrame
         .foldByRow("id", "name")
         .of(1, "Jerry", 2, "Juliana", 3, "Joan");
 
@@ -3107,128 +3028,140 @@ 

Inner Joins

.join(right) (1) .on("id") (2) .select();
-
-
-
- - - - - - - - - -
1join(..) is equivalent to innerJoin(..) (just like in SQL). With "inner" semantics, only the matching rows that -are present in both DataFrames will be included in the result below.
2Columns used in the join criteria have the same name in both DataFrames, so we can specify the name of the join column -only once. In general, the names do not need to match, and multiple columns can participate in a join.
-
-
-
-
id name    id_ age
+                            
+
+
+ + + + + + + + + +
1 + join(..) is equivalent to innerJoin(..) (just like in SQL). With "inner" semantics, only the matching rows that are present in both DataFrames will be included in the result + below. +
2 + Columns used in the join criteria have the same name in both DataFrames, so we can specify the name of the join column only once. In general, the names do not need to match, and multiple columns can + participate in a join. +
+
+
+
+
+id name    id_ age
 -- ------- --- ---
  2 Juliana   2  25
- 3 Joan      3  59
-
-
-
-
-

Column Namespace

-
-

Notice how DFLib treated the id column in the join result above. Since it was present in both left and right DataFrames, -it was included in the result twice, but the second instance was renamed to id_ to avoid a naming collision. The same -renaming would occur for any other columns from the right DataFrame that have conflicting names with the columns from -the left. A quick way to get rid of duplicate columns is to exclude them from the result using the name pattern:

-
-
-
-
DataFrame joined = left
+ 3 Joan      3  59
+                                
+
+
+
+
+

Column Namespace

+
+

+ Notice how DFLib treated the id column in the join result above. Since it was present in both left and right DataFrames, it was included in the result twice, but the second instance was renamed + to id_ to avoid a naming collision. The same renaming would occur for any other columns from the right DataFrame that have conflicting names with the columns from the left. A quick way to get rid + of duplicate columns is to exclude them from the result using the name pattern: +

+
+
+
+
DataFrame joined = left
         .join(right)
         .on("id")
         .colsExcept(c -> c.endsWith("_"))
         .select();
-
-
-
-
-
id name    age
+                            
+
+
+
+
+id name    age
 -- ------- ---
  2 Juliana  25
- 3 Joan     59
-
-
-
- - - - - -
- - -You may have noticed that colsExcept(..).select() style of picking join columns is very similar to the -column set API. The main difference is that the Join object itself represents a special form of column -set, and only "select" operations are available. -
-
-
-

For better clarity, you can assign names to the left and/or the right DataFrames just before the join, and the -column names in the result will be prefixed with the corresponding DataFrame name, thus avoiding the trailing underscore:

-
-
-
-
DataFrame joined = left.as("L") (1)
+ 3 Joan     59
+                                
+
+
+
+ + + + + +
+ + + You may have noticed that colsExcept(..).select() style of picking join columns is very similar to the column set API. The main difference is that the + Join object itself represents a special form of column set, and only "select" operations are available. +
+
+
+

+ For better clarity, you can assign names to the left and/or the right DataFrames just before the join, and the column names in the result will be prefixed with the corresponding DataFrame name, + thus avoiding the trailing underscore: +

+
+
+
+
DataFrame joined = left.as("L") (1)
         .join(right.as("R")) (2)
         .on("id")
         .select();
-
-
-
- - - - - - - - - -
1Assign the name to the left DataFrame
2Assign the name to the right DataFrame
-
-
-

This makes it easier to identify the origin of each column.

-
-
-
-
L.id L.name  R.id R.age
+                            
+
+
+ + + + + + + + + +
1Assign the name to the left DataFrame
2Assign the name to the right DataFrame
+
+
+

This makes it easier to identify the origin of each column.

+
+
+
+
+L.id L.name  R.id R.age
 ---- ------- ---- -----
    2 Juliana    2    25
-   3 Joan       3    59
-
-
-
- - - - - -
- - -DataFrame immutability applies to name assignments as well. Calling .as() creates a separate -DataFrame, so the original one remains unnamed. -
-
-
-
-

Transform Columns

-
-

When building the join result, you can apply expressions to alter the existing columns or create new columns all -together:

-
-
-
-
DataFrame joined = left.as("L")
+   3 Joan       3    59
+                                
+
+
+
+ + + + + +
+ + DataFrame immutability applies to name assignments as well. Calling .as() creates a separate DataFrame, so the original one remains unnamed.
+
+
+
+

Transform Columns

+
+

When building the join result, you can apply expressions to alter the existing columns or create new columns all together:

+
+
+
+
DataFrame joined = left.as("L")
         .join(right.as("R"))
         .on("id")
         .cols("name", "retires_soon")
@@ -3236,122 +3169,123 @@ 

Transform Columns

$col("name"), $int("R.age").gt(57) );
-
-
-
-
-
name    retires_soon
+                            
+
+
+
+
+name    retires_soon
 ------- ------------
 Juliana        false
-Joan            true
-
-
-
- - - - - -
- - -In the context of a join, column expressions can reference either short column names of left and right -DataFrames, or the fully qualified ones with the prefix. In the former case, the names should take into account the "_" -suffix applied to the right DataFrame columns when their names overlap with column names on the left. -
-
-
-
-

Outer Joins

-
-

Now let’s demonstrate outer joins. Here is a leftJoin(..) example using the same left and right DataFrames:

-
-
-
-
DataFrame joined = left
+Joan            true
+                                
+
+
+
+ + + + + +
+ + + In the context of a join, column expressions can reference either short column names of left and right DataFrames, or the fully qualified ones with the prefix. In the former case, the names should + take into account the "_" suffix applied to the right DataFrame columns when their names overlap with column names on the left. +
+
+
+
+

Outer Joins

+
+

Now let’s demonstrate outer joins. Here is a leftJoin(..) example using the same left and right DataFrames:

+
+
+
+
DataFrame joined = left
         .leftJoin(right)
         .on("id")
         .select();
-
-
-
-
-
id name     id_  age
+                            
+
+
+
+
+id name     id_  age
 -- ------- ---- ----
  1 Jerry   null null
  2 Juliana    2   25
- 3 Joan       3   59
-
-
-
-

It has all the rows from the left DataFrame, and only the matching rows from the right DataFrame. For left rows with no -matching right rows, the right columns are filled with null.

-
-
-
-

Indicator Column

-
-

Join API allows to explicitly identify which rows had a match, and which didn’t by adding a special "indicator" column. -Let’s show it on a fullJoin(..) example:

-
-
-
-
DataFrame joined = left
+ 3 Joan       3   59
+                                
+
+
+
+

It has all the rows from the left DataFrame, and only the matching rows from the right DataFrame. For left rows with no matching right rows, the right columns are filled with null.

+
+
+
+

Indicator Column

+
+

Join API allows to explicitly identify which rows had a match, and which didn’t by adding a special "indicator" column. Let’s show it on a fullJoin(..) example:

+
+
+
+
DataFrame joined = left
         .fullJoin(right)
         .on("id")
         .indicatorColumn("join_type") (1)
         .colsExcept(c -> c.endsWith("_"))
         .select();
-
-
-
- - - - - -
1Request an indicator column with a user-specified name.
-
-
-
-
  id name     age join_type
+                            
+
+
+ + + + + +
1Request an indicator column with a user-specified name.
+
+
+
+
+  id name     age join_type
 ---- ------- ---- ----------
    1 Jerry   null left_only
    2 Juliana   25 both
    3 Joan      59 both
-null null      40 right_only
-
-
-
-

The join_type column will contain a special enum of org.dflib.join.JoinIndicator type that allows to categorize rows -in the produced DataFrame.

-
-
- - - - - -
- - -indicator column can be requested for all join types, but it is only useful for the three outer joins (it will -always be both for inner joins). -
-
-
-
-

Nested Loop Joins

-
-

The joins we’ve seen so far were all based on comparing left and right column values. They are knows as "hash joins", -and are usually as fast as joins can get. However, they can not express every possible condition, and sometimes we have -to resort to another kind of joins - "nested loop", that would compare every row from the left DataFrame to every row -from the right. For instance, let’s join a DataFrame of salaries with itself to produce pairs of names - a person with -higher salary on the left vs. everyone with lower salary on the right:

-
-
-
-
DataFrame df = DataFrame.foldByRow("name", "salary").of(
+null null      40 right_only
+                                
+
+
+
+

The join_type column will contain a special enum of org.dflib.join.JoinIndicator type that allows to categorize rows in the produced DataFrame.

+
+
+ + + + + +
+ + indicator column can be requested for all join types, but it is only useful for the three outer joins (it will always be both for inner joins).
+
+
+
+

Nested Loop Joins

+
+

+ The joins we’ve seen so far were all based on comparing left and right column values. They are knows as "hash joins", and are usually as fast as joins can get. However, they can not express every + possible condition, and sometimes we have to resort to another kind of joins - "nested loop", that would compare every row from the left DataFrame to every row from the right. For instance, let’s join a + DataFrame of salaries with itself to produce pairs of names - a person with higher salary on the left vs. everyone with lower salary on the right: +

+
+
+
+
DataFrame df = DataFrame.foldByRow("name", "salary").of(
         "Jerry", 120000,
         "Juliana", 80000,
         "Joan", 95000);
@@ -3365,93 +3299,93 @@ 

Nested Loop Joins

.select() .sort($int("salary").desc(), $int("salary_").desc()) (2) .cols("name", "name_").selectAs("makes_more", "makes_less");
-
-
-
- - - - - - - - - -
1Custom join condition
2Sorting and renaming the result columns for a user-friendly display
-
-
-
-
makes_more makes_less
+                            
+
+
+ + + + + + + + + +
1Custom join condition
2Sorting and renaming the result columns for a user-friendly display
+
+
+
+
+makes_more makes_less
 ---------- ----------
 Jerry      Joan
 Jerry      Juliana
 Joan       Juliana
-Juliana    null
-
-
-
- - - - - -
- - -Nested loop joins are much slower, and should be avoided unless absolutely necessary. -
-
-
-
-
-
-

Group and Aggregate

-
-
-

TODO

-
-
-
-
-

Window functions

-
-
-

TODO

-
-
-
-
-

Pivot

-
-
-

TODO

-
-
-
-
-

Stack

-
-
-

TODO

-
-
-
-
-

User-Defined Functions

-
-
-

User-defined functions (aka "UDF") is a way to create highly-reusable expressions. Expressions are, -of course, abstract transformations of columns, so they are already somewhat reusable. However, they encode names -(or positions) of all columns they operate on. So you won’t be able to apply the same expression to two different sets -of columns in the same DataFrame. UDFs are intended to address this limitation and make expressions fully dynamic.

-
-
-

As the name implies, a UDF is a function (usually implemented as a lambda or a method ref). It takes one or more -expressions as parameters, and produces a single expression:

-
-
-
-
void formatNames() {
+Juliana    null
+                                
+
+
+
+ + + + + +
+ + Nested loop joins are much slower, and should be avoided unless absolutely necessary.
+
+
+
+
+
+
+

Group and Aggregate

+
+

TODO

+
+
+
+
+
+

Window functions

+
+

TODO

+
+
+
+
+
+

Pivot

+
+

TODO

+
+
+
+
+
+

Stack

+
+

TODO

+
+
+
+
+
+

User-Defined Functions

+
+

+ User-defined functions (aka "UDF") is a way to create highly-reusable expressions. Expressions are, of course, abstract transformations of columns, so they are already somewhat + reusable. However, they encode names (or positions) of all columns they operate on. So you won’t be able to apply the same expression to two different sets of columns in the same DataFrame. UDFs are + intended to address this limitation and make expressions fully dynamic. +

+
+
+

As the name implies, a UDF is a function (usually implemented as a lambda or a method ref). It takes one or more expressions as parameters, and produces a single expression:

+
+
+
+
void formatNames() {
     DataFrame df = DataFrame.foldByRow("first", "last").of(
             "JERRY", "COSIN",
             "juliana", "waLEwsKi");
@@ -3468,40 +3402,44 @@ 

User-Defined Functions

return Character.toUpperCase(raw.charAt(0)) + raw.substring(1).toLowerCase(); }
-
-
-
- - - - - - - - - -
1Define a UDF that takes a single parameter and performs proper name capitalization
2Apply the same UDF separately to the "first" and "last" name columns
-
-
-
-
first   last
+                        
+
+
+ + + + + + + + + +
1Define a UDF that takes a single parameter and performs proper name capitalization
2Apply the same UDF separately to the "first" and "last" name columns
+
+
+
+
+first   last
 ------- --------
 Jerry   Cosin
-Juliana Walewski
-
-
-
-

In the example above, we called the UDF multiple times, each time with a different column name. A UDF can also -be invoked with a numeric position or a full expression instead of a column name.

-
-
-

Above, we used Udf1 function, that takes only one expression (or column) as an input. Generally, UDFs can take -any number of expressions (columns). There are Udf1, Udf2, Udf3 and UdfN interfaces. The latter can take any -number of arguments and should be used to model UDFs with more than 3 inputs or a variable number of inputs:

-
-
-
-
DataFrame df = DataFrame.foldByRow("first", "last").of(
+Juliana Walewski
+                            
+
+
+
+

In the example above, we called the UDF multiple times, each time with a different column name. A UDF can also be invoked with a numeric position or a full expression instead of a column name.

+
+
+

+ Above, we used Udf1 function, that takes only one expression (or column) as an input. Generally, UDFs can take any number of expressions (columns). There are Udf1, Udf2, + Udf3 and UdfN interfaces. The latter can take any number of arguments and should be used to model UDFs with more than 3 inputs or a variable number of inputs: +

+
+
+
+
DataFrame df = DataFrame.foldByRow("first", "last").of(
         "Jerry", "Cosin",
         null, "Walewski",
         "Joan", null);
@@ -3516,135 +3454,146 @@ 

User-Defined Functions

DataFrame clean = df .rows(noNulls.call("first", "last").castAsBool()) (2) .select();
-
-
-
- - - - - - - - - -
1Build a UDF with dynamic number of input parameters packaged in the Exp[]
2Call the UDF to create a row filter. Since the UDF return type is Exp<Boolean>, -we need to explicitly "cast" it to Condition expected by the RowSet, so we used castAsBool()
-
-
-
-
first last
+                        
+
+
+ + + + + + + + + +
1Build a UDF with dynamic number of input parameters packaged in the Exp[]
2 + Call the UDF to create a row filter. Since the UDF return type is Exp<Boolean>, we need to explicitly "cast" it to Condition expected by the RowSet, so we used + castAsBool() +
+
+
+
+
+first last
 ----- -----
-Jerry Cosin
-
-
-
- - - - - -
- - -Performance hint: If the logic within a UDF requires converting a column to a specific data type, -and the column you are applying it to is known to be of that type already, you can speed up evaluation -by passing an expression of that type to the UDF. E.g. if a UDF calls castAsInt() on its argument, -udf.call($int("a")) is much faster than udf.call("a"). -
-
-
-
-
-

Using Relational Databases

-
-
-

Relational databases are arguably the most important type of data stores out there. Also they happen to map -really well to DataFrames. DFLib provides advanced support for loading and saving DataFrames to RDBMS. It supports -transactions, auto-generation of certain SQL statements, merging of data on top of the existing data -("create-or-update"), building custom SQL for selects and updates. It knows how to treat different DB -"flavors" and does a number of other cool database-related things.

-
-
-

To start using all this, you need to import dflib-jdbc module:

-
-
-
-
<dependency>
+Jerry Cosin
+                            
+
+
+
+ + + + + +
+ + + Performance hint: If the logic within a UDF requires converting a column to a specific data type, and the column you are applying it to is known to be of that type already, you can speed up evaluation by + passing an expression of that type to the UDF. E.g. if a UDF calls castAsInt() on its argument, udf.call($int("a")) is much faster than udf.call("a"). +
+
+
+
+
+
+

Using Relational Databases

+
+

+ Relational databases are arguably the most important type of data stores out there. Also they happen to map really well to DataFrames. DFLib provides advanced support for loading and saving DataFrames to RDBMS. + It supports transactions, auto-generation of certain SQL statements, merging of data on top of the existing data ("create-or-update"), building custom SQL for selects and updates. It knows how to treat different + DB "flavors" and does a number of other cool database-related things. +

+
+
+

To start using all this, you need to import dflib-jdbc module:

+
+
+
+
<dependency>
     <groupId>org.dflib</groupId>
     <artifactId>dflib-jdbc</artifactId>
 </dependency>
 
 <!-- Of course you also need to import your preferred JDBC driver -->
-
-
-
-

JdbcConnector

-
-

Once the imports are setup, you’d use Jdbc class to obtain an instance of JdbcConnector that can be later used for -all DB operations. You may already have a preconfigured javax.sql.DataSource, so the simplest way to create a -connector is to pass that DataSource to Jdbc factory method:

-
-
-
-
JdbcConnector connector = Jdbc.connector(dataSource);
-
-
-
-

Alternatively you can build everything from scratch:

-
-
-
-
JdbcConnector connector = Jdbc
+                        
+
+
+
+
+

JdbcConnector

+
+

+ Once the imports are setup, you’d use Jdbc class to obtain an instance of JdbcConnector that can be later used for all DB operations. You may already have a preconfigured + javax.sql.DataSource, so the simplest way to create a connector is to pass that DataSource to Jdbc factory method: +

+
+
+
+
JdbcConnector connector = Jdbc.connector(dataSource);
+
+
+
+

Alternatively you can build everything from scratch:

+
+
+
+
JdbcConnector connector = Jdbc
         .connector("jdbc:derby:target/derby/mydb;create=true")
         // .driver("com.foo.MyDriver") (1)
         // .userName("root")
         // .password("secret") (2)
         .build();
-
-
-
- - - - - - - - - -
1(Optional) Driver name. Some drivers are not properly registered with the DriverManager, and require an explicit -declaration.
2DB account username/password (not needed for our in-memory Derby example, but will definitely be required for a real database).
-
-
-
-

TableLoader / TableSaver

-
-

Once you have the connector, you can start reading and persisting DataFrame data. Many DataFrames map directly to -individual tables (or views) defined in the database. For those DFLib provides a set of rather straightforward -operations that do not require the user to write SQL (all needed SQL is auto-generated by the framework):

-
-
-
-
DataFrame df = connector.tableLoader("person").load();
-
-
-
-
-
id name              salary
+                            
+
+
+ + + + + + + + + +
1(Optional) Driver name. Some drivers are not properly registered with the DriverManager, and require an explicit declaration.
2DB account username/password (not needed for our in-memory Derby example, but will definitely be required for a real database).
+
+
+
+

TableLoader / TableSaver

+
+

+ Once you have the connector, you can start reading and persisting DataFrame data. Many DataFrames map directly to individual tables (or views) defined in the database. For those DFLib provides a set of rather + straightforward operations that do not require the user to write SQL (all needed SQL is auto-generated by the framework): +

+
+
+
+
DataFrame df = connector.tableLoader("person").load();
+
+
+
+
+
+id name              salary
 -- ----------------- --------
  1 Jerry Cosin       70000.0
  2 Juliana Walewski  85000.0
- 3 Joan O'Hara       101000.0
-
-
-
-

Table loader provides a way to customize the load operation. It allows to select specific columns, set the maximum -number of rows to read, sample rows, and even specify fetch condition as another DataFrame. Some examples:

-
-
-
-
DataFrame condition = DataFrame
+ 3 Joan O'Hara       101000.0
+                                
+
+
+
+

+ Table loader provides a way to customize the load operation. It allows to select specific columns, set the maximum number of rows to read, sample rows, and even specify fetch condition as another DataFrame. + Some examples: +

+
+
+
+
DataFrame condition = DataFrame
         .byColumn("salary")
         .of(Series.ofInt(70_000, 101_000));
 
@@ -3652,26 +3601,29 @@ 

TableLoader / TableSaver

.cols("name", "salary") .eq(condition) .load();
-
-
-
-
-
name        salary
+                            
+
+
+
+
+name        salary
 ----------- --------
 Jerry Cosin 70000.0
-Joan O'Hara 101000.0
-
-
-
-

What it doesn’t require (unlike CSV loader) is explicit column types, as the proper value types are inferred -from the database metadata.

-
-
-

Table saver allows to save DataFrame to a table. Column names in the DataFrame should match column names in the DB table:

-
-
-
-
DataFrame df = DataFrame.byArrayRow("id", "name", "salary")
+Joan O'Hara 101000.0
+                                
+
+
+
+

What it doesn’t require (unlike CSV loader) is explicit column types, as the proper value types are inferred from the database metadata.

+
+
+

Table saver allows to save DataFrame to a table. Column names in the DataFrame should match column names in the DB table:

+
+
+
+
DataFrame df = DataFrame.byArrayRow("id", "name", "salary")
         .appender()
         .append(1, "Jerry Cosin", 70_000)
         .append(2, "Juliana Walewski", 85_000)
@@ -3679,325 +3631,341 @@ 

TableLoader / TableSaver

.toDataFrame(); connector.tableSaver("person").save(df);
-
-
-
-

In this scenario table saver executes insert for each DataFrame row. But what if there is already an existing data -in the table? There are a few options the user has to overwrite or merge the data:

-
-
-
    -
  • -

    Append the data (that’s what we effectively did above).

    -
  • -
  • -

    Delete all existing data before doing the insert.

    -
  • -
  • -

    Merge the data by comparing DataFrame and DB table data on PK column(s) or an arbitrary set of columns. Insert missing -rows, update the existing rows. If the table has more columns than there are columns in the DataFrame, the data in those -extra columns is preserved.

    -
  • -
-
-
-

Delete before insert example:

-
-
-
-
connector.tableSaver("person")
+                            
+
+
+

In this scenario table saver executes insert for each DataFrame row. But what if there is already an existing data in the table? There are a few options the user has to overwrite or merge the data:

+
+
+
    +
  • +

    Append the data (that’s what we effectively did above).

    +
  • +
  • +

    Delete all existing data before doing the insert.

    +
  • +
  • +

    + Merge the data by comparing DataFrame and DB table data on PK column(s) or an arbitrary set of columns. Insert missing rows, update the existing rows. If the table has more columns than there are + columns in the DataFrame, the data in those extra columns is preserved. +

    +
  • +
+
+
+

Delete before insert example:

+
+
+
+
connector.tableSaver("person")
         .deleteTableData()
         .save(df);
-
-
-
-

Merge by PK example (PK columns are detected from DB metadata) :

-
-
-
-
connector.tableSaver("person")
+                            
+
+
+

Merge by PK example (PK columns are detected from DB metadata) :

+
+
+
+
connector.tableSaver("person")
         .mergeByPk()
         .save(df);
-
-
-
-
-

SqlLoader / SqlSaver

-
-

TODO

-
-
-
-
-
-

CSV Files

-
-
-

CSV (comma-separated values) is a very common and rather simple format for working with raw data. *.csv files can be -manipulated programmatically or manually (via Excel/LibreOffice), loaded to / from databases, etc. DFLib supports -reading DataFrames from CSV and storing them to CSV. You need to add an extra dependency to your project to take -advantage of this functionality:

-
-
-
-
<dependency>
+                            
+
+
+
+

SqlLoader / SqlSaver

+
+

TODO

+
+
+
+
+
+
+

CSV Files

+
+

+ CSV (comma-separated values) is a very common and rather simple format for working with raw data. *.csv files can be manipulated programmatically or manually (via Excel/LibreOffice), loaded to / from + databases, etc. DFLib supports reading DataFrames from CSV and storing them to CSV. You need to add an extra dependency to your project to take advantage of this functionality: +

+
+
+
+
<dependency>
     <groupId>org.dflib</groupId>
     <artifactId>dflib-csv</artifactId>
 </dependency>
-
-
-
-

Once you do that, Csv class is the entry point to all the .csv related operations as discussed -below.

-
-
-

Reading from CSV

-
-

The simplest API for reading a DataFrame from a CSV is this:

-
-
-
-
DataFrame df = Csv.load("src/test/resources/f1.csv"); (1)
-
-
-
- - - - - -
1The argument to the "load" method can be a filename, a file, or a Reader, so it can be loaded from a variety of -sources.
-
-
-

The result is a DataFrame that matches CSV structure:

-
-
-
-
A B
+                        
+
+
+

Once you do that, Csv class is the entry point to all the .csv related operations as discussed below.

+
+
+
+
+

Reading from CSV

+
+

The simplest API for reading a DataFrame from a CSV is this:

+
+
+
+
DataFrame df = Csv.load("src/test/resources/f1.csv"); (1)
+
+
+
+ + + + + +
1The argument to the "load" method can be a filename, a file, or a Reader, so it can be loaded from a variety of sources.
+
+
+

The result is a DataFrame that matches CSV structure:

+
+
+
+
+A B
 - --
 1 s1
-4 s2
-
-
-
-

DFLib makes a few assumptions about the data in f1.csv, namely that the first row represents -column names, that all columns are Strings, and that all of them need to be included. These assumptions are not -necessarily true with many data sets. So there is a longer form of this API that allows to configure column types, -skip rows and columns, or even sample the data without loading the entire CSV in memory. Some examples:

-
-
-
-
DataFrame df = Csv.loader() (1)
+4 s2
+                                
+
+
+
+

+ DFLib makes a few assumptions about the data in f1.csv, namely that the first row represents column names, that all columns are Strings, and that all of them need to be included. These + assumptions are not necessarily true with many data sets. So there is a longer form of this API that allows to configure column types, skip rows and columns, or even sample the data without loading the entire + CSV in memory. Some examples: +

+
+
+
+
DataFrame df = Csv.loader() (1)
         .offset(1) (2)
         .header("x", "y") (3)
         .intColumn("x") (4)
         .load("src/test/resources/f1.csv");
-
-
-
- - - - - - - - - - - - - - - - - -
1Instead of "load" method use "loader".
2Skip the header row.
3Provide our own header.
4Convert the first column to int.
-
-
-
-
x y
+                            
+
+
+ + + + + + + + + + + + + + + + + +
1Instead of "load" method use "loader".
2Skip the header row.
3Provide our own header.
4Convert the first column to int.
+
+
+
+
+x y
 - --
 1 s1
-4 s2
-
-
-
- - - - - -
- - -In theory, you don’t have to do most of these tweaks via CSV loader. You can load the raw data, and then use -standard DataFrame transformations to shape the result. However doing it via the loader allows to optimize -both load speed and memory use, so it is usually preferable. -
-
-
-
-

Writing to CSV

-
-

Writing to a CSV is equally easy:

-
-
-
-
Csv.save(df, "target/df.csv"); (1)
-
-
-
- - - - - -
1The argument to the "save" method can be a filename, a file, a Writer (or generally Appendable), so it can be stored -to a variety of destinations.
-
-
-

Just like with the loader, CSV saver provides its own set of options, if the defaults are not sufficient:

-
-
-
-
Csv.saver() (1)
+4 s2
+                                
+
+
+
+ + + + + +
+ + + In theory, you don’t have to do most of these tweaks via CSV loader. You can load the raw data, and then use standard DataFrame transformations to shape the result. However doing it via the + loader allows to optimize both load speed and memory use, so it is usually preferable. +
+
+
+
+

Writing to CSV

+
+

Writing to a CSV is equally easy:

+
+
+
+
Csv.save(df, "target/df.csv"); (1)
+
+
+
+ + + + + +
1The argument to the "save" method can be a filename, a file, a Writer (or generally Appendable), so it can be stored to a variety of destinations.
+
+
+

Just like with the loader, CSV saver provides its own set of options, if the defaults are not sufficient:

+
+
+
+
Csv.saver() (1)
         .createMissingDirs() (2)
         .format(CSVFormat.EXCEL) (3)
         .save(df, "target/csv/df.csv");
-
-
-
- - - - - - - - - - - - - -
1Instead of "save" method use "saver" to be able to customize the process.
2If intermediate directories for the output file are missing, create them.
3Provide an alternative format (either from a collection of known formats, or user-specific).
-
-
-
-
-
-

Excel Files

-
-
-

Excel format is very common in the business and finance world. DFLib supports reading DataFrames from and storing -them to Excel. You need to add an extra dependency to your project to take -advantage of this functionality:

-
-
-
-
<dependency>
+                            
+
+
+ + + + + + + + + + + + + +
1Instead of "save" method use "saver" to be able to customize the process.
2If intermediate directories for the output file are missing, create them.
3Provide an alternative format (either from a collection of known formats, or user-specific).
+
+
+
+
+
+
+

Excel Files

+
+

+ Excel format is very common in the business and finance world. DFLib supports reading DataFrames from and storing them to Excel. You need to add an extra dependency to your project to take advantage of this + functionality: +

+
+
+
+
<dependency>
     <groupId>org.dflib</groupId>
     <artifactId>dflib-excel</artifactId>
 </dependency>
-
-
-
-

TODO…​

-
-
-
-
-

Avro Files

-
-
-

Avro binary format is common in data engineering. Its main advantage is that *.avro files are saved with -an embedded schema. So when they are read back, the fields are converted to the correct Java types. This is different -from CSV, as reading a .csv by default produces a DataFrame with all String columns. To work with the Avro format, -include the following dependency:

-
-
-
-
<dependency>
+                        
+
+
+

TODO…​

+
+
+
+
+
+

Avro Files

+
+

+ Avro binary format is common in data engineering. Its main advantage is that *.avro files are saved with an embedded schema. So when they are read back, the fields are converted to the correct Java + types. This is different from CSV, as reading a .csv by default produces a DataFrame with all String columns. To work with the Avro format, include the following dependency: +

+
+
+
+
<dependency>
     <groupId>org.dflib</groupId>
     <artifactId>dflib-avro</artifactId>
 </dependency>
-
-
-
-

Once you do that, Avro class is the entry point to all the operations. With it you can save DataFrames to .avro -files and load them back.

-
-
-

Avro Schema

-
-

In most cases you don’t need to know anything about Avro schemas. DFLib automatically generates a schema for a -DataFrame before it is saved. Alternatively you can create a custom Schema object based on -Avro specification and pass it to AvroSaver.

-
-
-

TODO…​

-
-
-
-
-
-

Using JShell

-
-
-

Modern JDK installations come with jshell, a Read-Evaluate-Print Loop tool (aka REPL). DFLib can be used in -JShell environment for interactive data exploration and code prototyping. For the best experience, you may want to -configure a couple of things. Start jshell and execute the following commands:

-
-
-
-
// Set classpath. There's no automatic dependency management in jshell.
+                        
+
+
+

Once you do that, Avro class is the entry point to all the operations. With it you can save DataFrames to .avro files and load them back.

+
+
+
+
+

Avro Schema

+
+

+ In most cases you don’t need to know anything about Avro schemas. DFLib automatically generates a schema for a DataFrame before it is saved. Alternatively you can create a custom + Schema object based on Avro specification and pass it to AvroSaver. +

+
+
+

TODO…​

+
+
+
+
+
+
+

Using JShell

+
+

+ Modern JDK installations come with jshell, a Read-Evaluate-Print Loop tool (aka REPL). DFLib can be used in JShell environment for interactive data exploration and code prototyping. For the best + experience, you may want to configure a couple of things. Start jshell and execute the following commands: +

+
+
+
+
// Set classpath. There's no automatic dependency management in jshell.
 // You will need to include all DFLib jars and their dependencies explicitly
 
 /env --class-path /path/to/dflib-1.0.0-M20.jar:/path/to/dflib-csv-1.0.0-M20.jar
-
-
-
-
-
// Disable data truncation. You want your data to be visible.
+                        
+
+
+
+
// Disable data truncation. You want your data to be visible.
 
 /set mode mine normal -command
 /set truncation mine 40000
 /set feedback mine
-
-
-
-
-
// Add needed imports
+                        
+
+
+
+
// Add needed imports
 
 import org.dflib.*;
 import org.dflib.csv.*;
 
 import static org.dflib.Exp.*;
-
-
-
-
-
// Set tabular printer
+                        
+
+
+
+
// Set tabular printer
 
 Environment.setPrinter(Printers.tabular());
-
-
-
-

After this, you can start working with DataFrames and immediately check the output:

-
-
-
-
var df = Csv.load("../data/stuff.csv");
-
-
-
-

JShell may print something like this

-
-
-
-
df ==>
+                        
+
+
+

After this, you can start working with DataFrames and immediately check the output:

+
+
+
+
var df = Csv.load("../data/stuff.csv");
+
+
+
+

JShell may print something like this

+
+
+
+
+df ==>
 id   name              salary
 ---- ----------------- --------
    1 Jerry Cosin       70000.0
@@ -4007,73 +3975,79 @@ 

Using JShell

997 Jenny Harris 65000.0 998 Matt Ostin 52000.0 999 Andrew Tsui 99000.0 -1000 rows x 3 columns
-
-
-
-
-
-

Using Jupyter Notebook

-
-
-

While DFLib runs inside regular Java applications or jshell, it also works perfectly in Jupyter, which is a very common "notebook" -environment among data scientists. In a notebook, you interact with your code via a web browser in a rather "visual" -way. It allows to run each step of a data transformation one-by-one and inspect the data between the steps. -Jupyter is often associated with Python / pandas, but it can also be used with Java / DFLib.

-
-
-

The code developed in a notebook can be later copied and pasted into your application. Java developers should consider -using either Jupyter as a part of their data project workflow, and DFLib design (and its Jupyter integration features -described here) makes it rather easy.

-
-
- - - - - -
- - -Currently, DFLib only provides integration for Jupyter. You can use other notebooks if they support Java. But you’ll -be on your own. E.g. Apache Zeppelin, etc. Though you may have to write special code for the best DataFrame display experience. -
-
-
-

Jupyter Installation with Java Kernel

-
- - - - - -
- - -While IntelliJ IDE supports Jupyter / Python, as of this writing, it seems that it does not support custom kernels, -so we suggest to run your notebooks outside the IDE in the web browser. -
-
-
-

You will need to install Python, Jupyter and iJava Jupyter kernel. There are -multiple ways of doing it. If you are on MacOS, using Homebrew is probably the easiest:

-
-
-
    -
  1. -

    Download the latest release of iJava from https://github.com/SpencerPark/IJava/releases

    -
  2. -
  3. -

    Unpack it to some temporary directory

    -
  4. -
  5. -

    Run the following commands:

    -
  6. -
-
-
-
-
# Install Jupyter (and, implicitly, Python)
+1000 rows x 3 columns
+                            
+
+
+
+
+
+
+

Using Jupyter Notebook

+
+

+ While DFLib runs inside regular Java applications or jshell, it also works perfectly in Jupyter, which is a very common "notebook" environment among data scientists. In a notebook, you interact with + your code via a web browser in a rather "visual" way. It allows to run each step of a data transformation one-by-one and inspect the data between the steps. Jupyter is often associated with Python / pandas, but + it can also be used with Java / DFLib. +

+
+
+

+ The code developed in a notebook can be later copied and pasted into your application. Java developers should consider using either Jupyter as a part of their data project workflow, and DFLib design (and its + Jupyter integration features described here) makes it rather easy. +

+
+
+ + + + + +
+ + + Currently, DFLib only provides integration for Jupyter. You can use other notebooks if they support Java. But you’ll be on your own. E.g. Apache Zeppelin, etc. Though you may have to write special + code for the best DataFrame display experience. +
+
+
+
+
+

Jupyter Installation with Java Kernel

+
+ + + + + +
+ + + While IntelliJ IDE supports Jupyter / Python, as of this writing, it seems that it does not support custom kernels, so we suggest to run your notebooks outside the IDE in the web browser. +
+
+
+

+ You will need to install Python, Jupyter and iJava Jupyter kernel. There are multiple ways of doing it. If you are on MacOS, using Homebrew is probably the + easiest: +

+
+
+
    +
  1. +

    Download the latest release of iJava from https://github.com/SpencerPark/IJava/releases

    +
  2. +
  3. +

    Unpack it to some temporary directory

    +
  4. +
  5. +

    Run the following commands:

    +
  6. +
+
+
+
+
# Install Jupyter (and, implicitly, Python)
 brew install jupyter
 
 # Change the directory to where you unpacked the IJava kernel
@@ -4088,104 +4062,105 @@ 

Jupyter Installation with Java K # install Java "kernel" $PY install.py --sys-prefix

-
-
-
-

If all these steps finished successfully, check that the Java kernel is installed:

-
-
-
-
jupyter kernelspec list
+                            
+
+
+

If all these steps finished successfully, check that the Java kernel is installed:

+
+
+
+
jupyter kernelspec list
 
 Available kernels:
   python3    /opt/homebrew/Cellar/jupyterlab/4.1.0/libexec/lib/python3.12/site-packages/ipykernel/resources
   java       /opt/homebrew/Cellar/jupyterlab/4.1.0/libexec/share/jupyter/kernels/java
-
-
-
-
-

DFLib in Jupyter

-
-

Start Jupyter

-
-
-
-
jupyter notebook
-
-
-
-

The browser opens. Click on "New > Notebook", and when asked to select a kernel, pick "Java" from the dropdown. In the -document cell enter something like this:

-
-
-
-
%maven org.dflib:dflib-jupyter:1.0.0-M20 (1)
+                            
+
+
+
+

DFLib in Jupyter

+
+

Start Jupyter

+
+
+
+
jupyter notebook
+
+
+
+

The browser opens. Click on "New > Notebook", and when asked to select a kernel, pick "Java" from the dropdown. In the document cell enter something like this:

+
+
+
+
%maven org.dflib:dflib-jupyter:1.0.0-M20 (1)
 
 import org.dflib.jupyter.*;
 import org.dflib.*;
 
 DFLibJupyter.init(getKernelInstance());
-
-
-
- - - - - -
1It is enough to include dflib-jupyter. All other DFLib modules will be added through its transitive dependencies.
-
-
-

Click "Shift + Return" to execute the cell. If there are no errors, you can start using DFLib API in the following cells. -E.g.:

-
-
-
-
DataFrame df = DataFrame.foldByColumn("a", "b", "c")
+                            
+
+
+ + + + + +
1It is enough to include dflib-jupyter. All other DFLib modules will be added through its transitive dependencies.
+
+
+

Click "Shift + Return" to execute the cell. If there are no errors, you can start using DFLib API in the following cells. E.g.:

+
+
+
+
DataFrame df = DataFrame.foldByColumn("a", "b", "c")
     .ofInts(-1, 1, 2, 3, 4, 5, 6);
 
 // the result of the last statement in a cell is printed just below it
 df
-
-
-
-
-

Change Display Parameters

-
-

To control truncation behavior, you can use static methods on DFLibJupyter:

-
-
-
-
DFLibJupyter.setMaxDisplayRows(10);
+                            
+
+
+
+

Change Display Parameters

+
+

To control truncation behavior, you can use static methods on DFLibJupyter:

+
+
+
+
DFLibJupyter.setMaxDisplayRows(10);
 DFLibJupyter.setMaxDisplayColumnWidth(50);
-
-
-
-
-

Notebooks and Version Control

-
-

Jupyter places the data generated by the notebook in the notebook itself. This creates a challenge maintaining -notebooks under version control. Assuming you are using Git, you will need to add Git hooks to your project to strip -off the data before commit.

-
-
-

TODO: a recipe for excluding data

-
-
-
-
-
-

Unit Testing

-
- -
-
-
- - - +
+
+
+
+

Notebooks and Version Control

+
+

+ Jupyter places the data generated by the notebook in the notebook itself. This creates a challenge maintaining notebooks under version control. Assuming you are using Git, you will need to add Git hooks to + your project to strip off the data before commit. +

+
+
+

TODO: a recipe for excluding data

+
+
+
+
+
+
+

Unit Testing

+
+
+
+
+ +
+ © 2024 ObjectStyle LLC and individual authors +
+ + + \ No newline at end of file diff --git a/docs/docs/1.x/template.html b/docs/docs/1.x/template.html new file mode 100644 index 0000000..5259699 --- /dev/null +++ b/docs/docs/1.x/template.html @@ -0,0 +1,179 @@ + + + + + + + + + DFLib Documentation + + + + + +
+
+ + +
+
+
+ +
+
+ © 2024 ObjectStyle LLC and individual authors +
+ + + + \ No newline at end of file diff --git a/docs/js/nav_scroll.js b/docs/js/nav_scroll.js new file mode 100644 index 0000000..383c269 --- /dev/null +++ b/docs/js/nav_scroll.js @@ -0,0 +1,136 @@ +"use strict"; + +/** + * A flag indicating that the user is scrolling down the page. + * It is needed in order to track how the transition to the section occurs, there are two options: + * 1) clicking on a navigation element (userScrolling = true; the toggleActive function is not executed ) + * 2) content scrolling (userScrolling = false; toggleActive function is executed) )) + */ +var userScrolling = false; + +window.addEventListener( + 'load', + () => { + // Create a list of all headings with an id attribute + var trackedSections = document.querySelectorAll('.tracked-section'); + + // Get the navigation links container + var navLinksBox = document.querySelector('.sectlevel1'); + + // Create an observer for each heading + if(trackedSections){ + trackedSections.forEach(element => { + createObserver(element); + }); + } + + // Add an event listener to the navigation links container + if(navLinksBox){ + navLinksBox.addEventListener('click', (event) => { + event.preventDefault(); + userScrolling = true; + var targetLink = event.target.closest('a'); + if (!targetLink) return; + + requestAnimationFrame( + () => { + var id = targetLink.getAttribute('href').slice(1); + var targetSection = document.getElementById(id); + scrollToSectionAndCheckEnd(targetSection, targetLink); + } + ); + }); + } + + } +) + + + +/** + * Scroll to the target section and check when the scrolling ends + * @param {HTMLElement} targetSection + */ +function scrollToSectionAndCheckEnd(targetSection, targetLink) { + var targetPosition = targetSection.getBoundingClientRect().top + window.pageYOffset; + window.scrollTo({ top: targetPosition, behavior: 'smooth' }); + + var lastPosition = -1; + var requestAnimationFrameId; + + var checkScrollEnd = function() { + if (lastPosition === window.pageYOffset || + (window.innerHeight + window.pageYOffset) >= document.body.offsetHeight) { + var prevActiveEls = document.querySelectorAll('.sectlevel1 a.active'); + prevActiveEls?.forEach(element => { + element.classList.remove('active'); + }); + userScrolling = false; + targetLink.classList.add('active'); + cancelAnimationFrame(requestAnimationFrameId); // Останавливаем проверку + } else { + lastPosition = window.pageYOffset; + requestAnimationFrameId = requestAnimationFrame(checkScrollEnd); + } + }; + + requestAnimationFrameId = requestAnimationFrame(checkScrollEnd); +} + +/** + * Create a new IntersectionObserver instance + * @param {HTMLElement} targetElement + */ +var createObserver = (targetElement) =>{ + var observer; + var options = { + root: document, + rootMargin: '-5% 0px -90% 0px', + }; + observer = new IntersectionObserver(handleIntersect, options); + observer.observe(targetElement); +} + +/** + * Handle the intersection of the observed element + * @param {IntersectionObserverEntry[]} entries + */ +var handleIntersect = (entries) => { + entries.forEach((entry) => { + if (entry.isIntersecting && !userScrolling ) { + requestAnimationFrame(() => toggleActive(entry)); + } + }); +}; + +/** + * Switching the active position of navigation links + * @param {IntersectionObserverEntry} entry + */ +var toggleActive = (entry) => { + var id = entry.target.id; + var prevActive = document.querySelector('.sectlevel1 a.active'); + var activeLink = document.querySelector(`.sectlevel1 a[href="#${id}"]`); + prevActive?.classList.remove('active'); + activeLink?.classList.add('active'); + + if(window.innerWidth > 768 && activeLink){ + activeLink.addEventListener('transitionend', () => { + scrollToActive(activeLink); + }, { once: true }); + } +}; + +/** + * Scroll to the active navigation link + * @param {HTMLElement} activeLink + */ +var scrollToActive = (activeLink) => { + var navBox = document.querySelector('#toc'); + var bottomPos = activeLink.getBoundingClientRect().bottom; + Promise.resolve(1).then(() => { + if(bottomPos >= (navBox.offsetHeight - 50) || bottomPos <= 80){ + activeLink.scrollIntoView({ block: "center", behavior: "auto" }); + } + }); +} \ No newline at end of file diff --git a/docs/js/nav_scroll.min.js b/docs/js/nav_scroll.min.js new file mode 100644 index 0000000..cfa3abb --- /dev/null +++ b/docs/js/nav_scroll.min.js @@ -0,0 +1 @@ +"use strict";var userScrolling=!1;function scrollToSectionAndCheckEnd(e,t){var r=e.getBoundingClientRect().top+window.pageYOffset;window.scrollTo({top:r,behavior:"smooth"});var o,n=-1,c=function(){n===window.pageYOffset||window.innerHeight+window.pageYOffset>=document.body.offsetHeight?(document.querySelectorAll(".sectlevel1 a.active")?.forEach((e=>{e.classList.remove("active")})),userScrolling=!1,t.classList.add("active"),cancelAnimationFrame(o)):(n=window.pageYOffset,o=requestAnimationFrame(c))};o=requestAnimationFrame(c)}window.addEventListener("load",(()=>{var e=document.querySelectorAll(".tracked-section"),t=document.querySelector(".sectlevel1");e&&e.forEach((e=>{createObserver(e)})),t&&t.addEventListener("click",(e=>{e.preventDefault(),userScrolling=!0;var t=e.target.closest("a");t&&requestAnimationFrame((()=>{var e=t.getAttribute("href").slice(1);scrollToSectionAndCheckEnd(document.getElementById(e),t)}))}))}));var createObserver=e=>{var t={root:document,rootMargin:"-5% 0px -90% 0px"};new IntersectionObserver(handleIntersect,t).observe(e)},handleIntersect=e=>{e.forEach((e=>{e.isIntersecting&&!userScrolling&&requestAnimationFrame((()=>toggleActive(e)))}))},toggleActive=e=>{var t=e.target.id,r=document.querySelector(".sectlevel1 a.active"),o=document.querySelector(`.sectlevel1 a[href="#${t}"]`);r?.classList.remove("active"),o?.classList.add("active"),window.innerWidth>768&&o&&o.addEventListener("transitionend",(()=>{scrollToActive(o)}),{once:!0})},scrollToActive=e=>{var t=document.querySelector("#toc"),r=e.getBoundingClientRect().bottom;Promise.resolve(1).then((()=>{(r>=t.offsetHeight-50||r<=80)&&e.scrollIntoView({block:"center",behavior:"auto"})}))}; \ No newline at end of file diff --git a/docs/js/scripts.js b/docs/js/scripts.js index b42d3ec..663a35d 100644 --- a/docs/js/scripts.js +++ b/docs/js/scripts.js @@ -36,8 +36,21 @@ window.addEventListener("load", ()=>{ } }); + dropdownToggle(); + }); +var dropdownToggle = () => { + var dropdownsOpeners = document.querySelectorAll('.d_opener'); + if(!dropdownsOpeners) return; + dropdownsOpeners.forEach(dropdownOpener => { + dropdownOpener.addEventListener('click', (event) => { + event.preventDefault(); + dropdownOpener.classList.toggle('d_active'); + }); + }); +} + /** * Open navigation menu */ @@ -164,4 +177,6 @@ const showOverflow = ()=>{ // iframeWindow.postMessage(message, '*'); // iframeElement.classList.add('o_hide_video'); -// } \ No newline at end of file +// } + + diff --git a/docs/js/scripts.min.js b/docs/js/scripts.min.js index bfb1075..58e0d69 100644 --- a/docs/js/scripts.min.js +++ b/docs/js/scripts.min.js @@ -1 +1 @@ -window.addEventListener("load",()=>{const e=document.querySelector(".d_hbrg");e.addEventListener("click",()=>{e.classList.toggle("d_active"),openMenu(),document.body.classList.contains("o_overflow_hidden")?showOverflow():hideOverflow()})});const openMenu=()=>{const e=document.querySelector(".d_m_m");e.classList.toggle("d_show")},hideOverflow=()=>{document.body.classList.add("o_overflow_hidden")},showOverflow=()=>{document.body.classList.remove("o_overflow_hidden")}; \ No newline at end of file +window.addEventListener("load",()=>{const e=document.querySelector(".d_hbrg");e.addEventListener("click",()=>{e.classList.toggle("d_active"),openMenu(),document.body.classList.contains("o_overflow_hidden")?showOverflow():hideOverflow()}),dropdownToggle()});var dropdownToggle=()=>{var e=document.querySelectorAll(".d_opener");e&&e.forEach(e=>{e.addEventListener("click",o=>{o.preventDefault(),e.classList.toggle("d_active")})})};const openMenu=()=>{const e=document.querySelector(".d_m_m");e.classList.toggle("d_show")},hideOverflow=()=>{document.body.classList.add("o_overflow_hidden")},showOverflow=()=>{document.body.classList.remove("o_overflow_hidden")}; \ No newline at end of file diff --git a/docs/less/docs_style.less b/docs/less/docs_style.less new file mode 100644 index 0000000..78258e2 --- /dev/null +++ b/docs/less/docs_style.less @@ -0,0 +1,314 @@ +@bc: #141518; +@bc_l_d: #1B1C21; +@g_p_s: 40px; +@g_p_c: 15px; +@g_g: 30px; +@g_c: #fff; +@g_f_f: "Lato", sans-serif; +@g_f_s: 16px; +@g_l_h: 1.5; +@g_light_gray: #383A43; +@g_c_g: #333; +@g_grn_c: #34C759; + +a:focus{ + outline: none; +} +/*.footer start*/ +.footer{ + padding: 23px 0; + background: @bc_l_d; + text-align: center; + span{ + font-size: 0.875rem; + line-height: 1.714; + } +} +/*.footer end*/ + +/*mobile navigatin start*/ +.d_m_m{ + padding: @g_g @g_p_c; + background: @bc; + width: 100%; + top: 70px; + left: 0; + position: absolute; + z-index: 2; + height: calc(100vh - 70px); + box-sizing: border-box; + text-align: center; + display: flex; + flex-direction: column; + justify-content: space-between; + .d_gh{ + margin-top: 30px; + } +} +.header{ + .d_gh{ + min-width: auto; + background: @g_light_gray; + display: inline-flex; + column-gap: 8px; + padding: 6px 10px 6px 6px; + margin-right: initial; + text-transform: none; + &:hover{ + background: #6E7281; + text-decoration: none; + } + &:active{ + background: @g_c; + svg path{ + fill: #333333; + } + } + } + .d_nav{ + a{ + font-size: 0.9375rem; + line-height: 1.2; + } + ul{ + list-style: none; + padding: 0; + margin: 0; + li{ + text-align: center; + &+li{ + margin-top: 38px; + } + } + } + .d_gh{ + justify-content: center; + width: 100%; + max-width: 330px; + padding: 13px; + box-sizing: border-box; + } + } +} +.d_hbrg{ + &.d_active{ + i{ + background-color: transparent; + &:before{ + top: 0; + width: 21px; + right: 0; + -webkit-transform: rotate(45deg); + -ms-transform: rotate(45deg); + transform: rotate(45deg); + } + &::after{ + bottom: 0; + width: 21px; + right: 0; + -webkit-transform: rotate(-45deg); + -ms-transform: rotate(-45deg); + transform: rotate(-45deg); + } + } + } +} +/*mobile navigatin end*/ + +@media(min-width: 768px){ + #toc{ + overflow: auto; + scroll-behavior: smooth; + margin-bottom: 35px; + height: calc(100% - 16px); + } + .d_nav{ + position: sticky; + max-height: calc(100vh); + left: 0; + top: 0; + } + .d_cl_1_3\@s{ + flex-basis: calc((100% / 3) - @g_g); + } + .d_cl_2_3\@s{ + flex-basis: calc( ((100% / 3) * 2) - @g_g); + } + /*.header start*/ + .d_n_xs{ + display: block; + } + .header{ + ul{ + display: flex; + column-gap: @g_g; + list-style: none; + padding: 0; + margin: 0; + + a{ + font-size: 0.9375rem; + line-height: 1.125; + } + } + .d_logo_box{ + display: flex; + justify-content: center; + } + .d_m_m{ + &.d_show{ + display: none; + } + } + } + .d_n_s{ + display: none; + } + .d_v_s{ + display: flex; + } + /*.header end*/ + + .d_sctn{ + padding: 60px 0 202px 0px; + } + .d_rw + .d_rw { + margin-top: 50px; + } + .d_cntnr{ + padding-left: 40px; + padding-right: 40px; + } + + /*.footer start*/ + .footer{ + span{ + font-size: 0.9375rem; + line-height: 1.6; + } + p{ + font-size: 1.0625rem; + line-height: 1.411; + } + } + /*.footer end*/ +} + +@media(min-width: 1200px){ + + .d_cntnr{ + padding-left: 40px; + padding-right: 40px; + } + .footer{ + p{ + text-align: center; + } + } + +} + +//dropdown start +.header{ + ul li a{ + word-wrap: normal; + } + ul:not(.d_dropdown) li{ + position: relative; + } + .d_opener{ + position: relative; + padding-right: 20px; + margin-right: -20px; + &::after{ + content: ''; + background-image: url("data:image/svg+xml,%3Csvg width='8' height='4' viewBox='0 0 8 4' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M4 4L3.49691e-07 1.58735e-08L8 7.15256e-07L4 4Z' fill='white'/%3E%3C/svg%3E%0A"); + position: absolute; + top: 50%; + right: 7px; + width: 8px; + height: 4px; + transform: translateY(-50%); + transition: transform 0.2s ease-in-out; + } + &.d_active{ + &+.d_dropdown{ + display: block; + } + &:after{ + transform: translateY(-50%) rotate(180deg); + } + } + &:hover, + &.d_active, + &:active, + &:focus{ + color: #fff; + text-decoration: none; + } + } + .d_dropdown{ + display: none; + box-sizing: border-box; + &>li { + padding: 8px 0; + line-height: 1; + a{ + font-size: 0.875rem; + line-height: 1.428; + text-transform: none; + font-weight: 600; + } + } + } + .d_nav ul.d_dropdown li + li{ + margin-top: 0; + } + +} +@media(min-width: 768px){ + + .header{ + .d_rw{ + column-gap: 47px; + } + .d_cl_1_3\@s{ + flex-basis: auto; + min-width: 250px; + &.d_logo_box{ + min-width: auto; + } + } + .d_opener{ + margin-right: 0; + } + .d_dropdown{ + position: absolute; + top: 52px; + left: -20px; + width: 173px; + padding: 8px 20px; + background: #fff; + box-shadow: 0px 0px 8px 0px rgba(0, 0, 0, 0.08); + &>li { + a{ + color: #333333; + } + } + } + + } +} +@media(min-width: 1200px){ + .header{ + .d_rw{ + column-gap: 30px; + } + .d_cl_1_3\@s{ + flex-basis: calc((100% / 3) - @g_g); + min-width: auto; + } + + } +} +//dropdown end \ No newline at end of file diff --git a/docs/less/docs_style_first.less b/docs/less/docs_style_first.less new file mode 100644 index 0000000..f33d9ab --- /dev/null +++ b/docs/less/docs_style_first.less @@ -0,0 +1,185 @@ +@font-face { + font-family: "Lato"; + src: url("../../fonts/Lato-Bold.woff2") format("woff2"), url("../../fonts/Lato-Bold.woff") format("woff"); + font-weight: bold; + font-style: normal; + font-display: swap; +} + +@font-face { + font-family: "Lato"; + src: url("../../fonts/Lato-Regular.woff2") format("woff2"), url("../../fonts/Lato-Regular.woff") format("woff"); + font-weight: normal; + font-style: normal; + font-display: swap; +} + +@bc: #141518; +@bc_l_d: #1B1C21; +@g_p_s: 40px; +@g_p_c: 15px; +@g_g: 30px; +@g_c: #fff; +@g_f_f: "Lato", sans-serif; +@g_f_s: 16px; +@g_l_h: 1.5; +@g_light_gray: #383A43; +@g_c_g: #333; +@g_grn_c: #34C759; + +body{ + margin: 0; +} +/*Main start*/ +.header, .footer{ + font-family: @g_f_f; + font-size: @g_f_s; + font-weight: 400; + line-height: @g_l_h; + -webkit-text-size-adjust: 100%; + background: @bc; + color: @g_c; + position: relative; + z-index: 9999; +} +.d_cntnr { + display: flow-root; + box-sizing: content-box; + max-width: 1120px; + margin-left: auto; + margin-right: auto; + padding-left: @g_p_c; + padding-right: @g_p_c; +} +.d_rw { + display: flex; + flex-wrap: wrap; + column-gap: @g_g; + row-gap: @g_g; + box-sizing: border-box; + &+.d_rw{ + margin-top: @g_p_s; + } +} +[class^="d_cl"] { + flex: 1 1 100%; // 1 across at mobile + box-sizing: border-box; +} +.d_cl_a { + flex-grow: 0; + flex-shrink: 0; + flex-basis: auto; +} +.d_cl_expand{ + flex: 1; +} +.d_n_xs{ + display: none; +} +button{ + background: none; + border: none; + padding: 0; + &:hover{ + cursor: pointer; + } +} +.header{ + margin-bottom: 40px; + a{ + text-decoration: none; + color: @g_c; + text-transform: uppercase; + font-weight: 600; + &:hover{ + color: @g_c; + text-decoration: underline; + } + } + +} +.d_content{ + h1{ + margin-top: 20px; + } +} +.d_btn{ + margin: 0; + color: #fff; + display: inline-block; + font-size: 1rem; + line-height: @g_l_h; + font-weight: 700; + text-align: center; + text-decoration: none; + padding: 10px; + background: @bc_l_d; + transition: background .3s ease-in-out; + margin-left: auto; + margin-right: auto; + border-radius: 40px; + min-width: 246px; + &:hover{ + background: @g_light_gray; + } + &:active{ + background: @g_c; + color: @g_c_g; + } +} +/*Main end*/ + +/*.header start*/ + +.header{ + padding: 21px 0; + background: @bc_l_d; + .d_rw{ + justify-content: space-between; + align-items: center; + } + .d_m_m{ + display: none; + &.d_show{ + display: flex; + } + } +} +.d_logo_box{ + a{ + display: flex; + } +} +.d_hbrg{ + padding: 6px 0; + display: flex; + i, + i:after, + i:before{ + transition: all .2s ease-out; + background: @g_c; + border-radius: 2px; + height: 2px; + width: 21px; + } + i{ + position: relative; + line-height: 1; + &:after, + &:before { + content: ""; + position: absolute; + left: 0; + } + &:after{ + bottom: -5px; + } + &:before{ + top: -5px + } + } +} +.d_v_s{ + display: none; +} +/*.header end*/ \ No newline at end of file diff --git a/docs/less/style.less b/docs/less/style.less index 2b5c027..d24837d 100644 --- a/docs/less/style.less +++ b/docs/less/style.less @@ -442,6 +442,110 @@ body.o_overflow_hidden{ /*modal end*/ } +//dropdown start +header{ + ul li a{ + word-wrap: normal; + } + ul:not(.d_dropdown) li{ + position: relative; + } + .d_opener{ + position: relative; + padding-right: 20px; + margin-right: -20px; + &::after{ + content: ''; + background-image: url("data:image/svg+xml,%3Csvg width='8' height='4' viewBox='0 0 8 4' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M4 4L3.49691e-07 1.58735e-08L8 7.15256e-07L4 4Z' fill='white'/%3E%3C/svg%3E%0A"); + position: absolute; + top: 50%; + right: 7px; + width: 8px; + height: 4px; + transform: translateY(-50%); + transition: transform 0.2s ease-in-out; + } + &.d_active{ + &+.d_dropdown{ + display: block; + } + &:after{ + transform: translateY(-50%) rotate(180deg); + } + } + &:hover, + &.d_active, + &:active, + &:focus{ + color: #fff; + text-decoration: none; + } + } + .d_dropdown{ + display: none; + box-sizing: border-box; + &>li { + padding: 8px 0; + line-height: 1; + a{ + font-size: 0.875rem; + line-height: 1.428; + text-transform: none; + font-weight: 600; + } + } + } + .d_nav ul.d_dropdown li + li{ + margin-top: 0; + } + +} +@media(min-width: 768px){ + header{ + .d_rw{ + column-gap: 47px; + } + .d_cl_1_3\@s{ + flex-basis: auto; + min-width: 250px; + &.d_logo_box{ + min-width: auto; + } + } + .d_opener{ + margin-right: 0; + } + .d_dropdown{ + position: absolute; + top: 52px; + left: -20px; + width: 173px; + padding: 8px 20px; + background: #fff; + box-shadow: 0px 0px 8px 0px rgba(0, 0, 0, 0.08); + &>li { + a{ + color: #333333; + } + } + } + + } +} +@media(min-width: 1200px){ + header{ + .d_rw{ + column-gap: 30px; + } + .d_cl_1_3\@s{ + flex-basis: calc((100% / 3) - @g_g); + min-width: auto; + } + + } +} +//dropdown end + main, header, footer { display:block; } diff --git a/docs/less/style_v2.less b/docs/less/style_v2.less new file mode 100644 index 0000000..37b5810 --- /dev/null +++ b/docs/less/style_v2.less @@ -0,0 +1,743 @@ +/* cyrillic-ext */ +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2JL7W0Q5n-wU.woff2) + format("woff2"); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, + U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa0ZL7W0Q5n-wU.woff2) + format("woff2"); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* greek-ext */ +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2ZL7W0Q5n-wU.woff2) + format("woff2"); + unicode-range: U+1F00-1FFF; +} +/* greek */ +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa1pL7W0Q5n-wU.woff2) + format("woff2"); + unicode-range: U+0370-03FF; +} +/* vietnamese */ +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2pL7W0Q5n-wU.woff2) + format("woff2"); + unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, + U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, + U+1EA0-1EF9, U+20AB; +} +/* latin-ext */ +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa25L7W0Q5n-wU.woff2) + format("woff2"); + unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, + U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa1ZL7W0Q5nw.woff2) + format("woff2"); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, + U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, + U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* cyrillic-ext */ +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 500; + font-display: swap; + src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2JL7W0Q5n-wU.woff2) + format("woff2"); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, + U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 500; + font-display: swap; + src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa0ZL7W0Q5n-wU.woff2) + format("woff2"); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* greek-ext */ +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 500; + font-display: swap; + src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2ZL7W0Q5n-wU.woff2) + format("woff2"); + unicode-range: U+1F00-1FFF; +} +/* greek */ +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 500; + font-display: swap; + src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa1pL7W0Q5n-wU.woff2) + format("woff2"); + unicode-range: U+0370-03FF; +} +/* vietnamese */ +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 500; + font-display: swap; + src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2pL7W0Q5n-wU.woff2) + format("woff2"); + unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, + U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, + U+1EA0-1EF9, U+20AB; +} +/* latin-ext */ +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 500; + font-display: swap; + src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa25L7W0Q5n-wU.woff2) + format("woff2"); + unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, + U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 500; + font-display: swap; + src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa1ZL7W0Q5nw.woff2) + format("woff2"); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, + U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, + U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* cyrillic-ext */ +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 600; + font-display: swap; + src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2JL7W0Q5n-wU.woff2) + format("woff2"); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, + U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 600; + font-display: swap; + src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa0ZL7W0Q5n-wU.woff2) + format("woff2"); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* greek-ext */ +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 600; + font-display: swap; + src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2ZL7W0Q5n-wU.woff2) + format("woff2"); + unicode-range: U+1F00-1FFF; +} +/* greek */ +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 600; + font-display: swap; + src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa1pL7W0Q5n-wU.woff2) + format("woff2"); + unicode-range: U+0370-03FF; +} +/* vietnamese */ +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 600; + font-display: swap; + src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2pL7W0Q5n-wU.woff2) + format("woff2"); + unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, + U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, + U+1EA0-1EF9, U+20AB; +} +/* latin-ext */ +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 600; + font-display: swap; + src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa25L7W0Q5n-wU.woff2) + format("woff2"); + unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, + U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 600; + font-display: swap; + src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa1ZL7W0Q5nw.woff2) + format("woff2"); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, + U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, + U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* cyrillic-ext */ +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2JL7W0Q5n-wU.woff2) + format("woff2"); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, + U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa0ZL7W0Q5n-wU.woff2) + format("woff2"); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* greek-ext */ +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2ZL7W0Q5n-wU.woff2) + format("woff2"); + unicode-range: U+1F00-1FFF; +} +/* greek */ +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa1pL7W0Q5n-wU.woff2) + format("woff2"); + unicode-range: U+0370-03FF; +} +/* vietnamese */ +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2pL7W0Q5n-wU.woff2) + format("woff2"); + unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, + U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, + U+1EA0-1EF9, U+20AB; +} +/* latin-ext */ +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa25L7W0Q5n-wU.woff2) + format("woff2"); + unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, + U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa1ZL7W0Q5nw.woff2) + format("woff2"); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, + U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, + U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +@navbar_width: 22.0625em; +@content_width: 49.0625em; +@container_margin: ~"calc((100vw - (@{navbar_width} + @{content_width})) / 2)"; +@global_font_family: "Inter", sans-serif; +@global_color: #333; +@global_heading_color: #000; +@global_heading_fw: 700; +@global_gray_bc: #f4f4f4; +// @media screen and (min-width: 1280px) { +// body.toc2 { +// padding-left: 0; +// } +// div#toc.toc2 { +// width: @navbar_width; +// left: calc(@container_margin); +// padding: 1.25em; +// margin-top: 5em; +// top: 3.75em; +// height: calc(100vh - 124px); +// overflow: auto; +// & > ul { +// font-size: 0.875em; +// } +// } +// div#header, +// div#content{ +// max-width: @content_width; +// margin: 0; +// margin-left: calc((@container_margin) + @navbar_width); +// margin-right: calc(@container_margin); +// padding-left: 33px; +// } +// div#footer { +// padding-left: calc((@container_margin) + @navbar_width + 33px); +// } +// } +html { + body { + font-family: @global_font_family; + color: @global_color; + } +} +div { + h1, + h2, + h3, + #toctitle, + .sidebarblock > .content > .title, + h4, + h5, + h6, + #toc ul, + + .admonitionblock td.content > .title, + .audioblock > .title, + .exampleblock > .title, + .imageblock > .title, + .listingblock > .title, + .literalblock > .title, + .stemblock > .title, + .openblock > .title, + .paragraph > .title, + .quoteblock > .title, + table.tableblock > .title, + .verseblock > .title, + .videoblock > .title, + .dlist > .title, + .olist > .title, + .ulist > .title, + .qlist > .title, + .hdlist > .title, + .admonitionblock > table td.icon .title, + .verseblock pre, + .conum[data-value] { + font-family: @global_font_family; + } + + code, + pre, + kbd, + samp{ + font-family: monospace, monospace; + } + + .paragraph.lead > p, + #preamble > .sectionbody > [class="paragraph"]:first-of-type p { + color: @global_color; + } + h1, + h2, + h3, + #toctitle, + .sidebarblock > .content > .title, + h4, + h5, + h6 { + color: @global_heading_color; + } + .sectlevel1 > li { + a { + color: @global_heading_color; + } + .sectlevel2 { + padding-right: 17px; + & > li { + a { + color: @global_color; + } + } + } + } + h1, + h2, + h3, + #toctitle, + .sidebarblock > .content > .title, + h4, + h5, + h6 { + font-weight: @global_heading_fw; + margin-bottom: 20px; + } + p, .paragraph { + margin-bottom: 1.25em; + &:last-child{ + margin-bottom: 0; + } + } +} + +#toc { + a { + color: @global_heading_color; + font-weight: 600; + padding-top: 0.3125em; + padding-bottom: 0.3125em; + } + li { + // margin-top: 0.6875em; + a{ + padding-left: 0.3125em; + padding-right: 0.3125em; + display: block; + } + } + .sectlevel2{ + margin-top: 0.25em; + } + .sectlevel1 > li:first-child a{ + margin-top: 0; + } + .sectlevel2 { + a { + color: @global_color; + font-weight: 500; + + // display: inline-block; + } + // li { + // transition: background 250ms ease-in-out; + // border-radius: 4px; + // margin-top: 0.25em; + // &:hover { + // background: @global_gray_bc; + // cursor: pointer; + // } + // } + } + li a { + transition: background 250ms ease-in-out; + border-radius: 4px; + margin-top: 0.25em; + &:hover { + background: @global_gray_bc; + cursor: pointer; + } + } + .active{ + background: @global_gray_bc; + } + .toc2 { + #toctitle { + margin-bottom: 0.9375em; + } + } +} + +// //Scrollbar css start// +// @scrollbarBG: #F1F1F1; +// @thumbBG: #C1C1C1; +// div#toc.toc2::-webkit-scrollbar { +// width: 15px; +// } +// div#toc.toc2 { +// scrollbar-width: thin; +// scrollbar-color: @thumbBG @scrollbarBG; +// } +// div#toc.toc2::-webkit-scrollbar-track { +// background: @scrollbarBG; +// } +// div#toc.toc2::-webkit-scrollbar-thumb { +// background-color: @thumbBG ; +// border: 2px solid @scrollbarBG; +// } +// //Scrollbar css end// + +//Scrollbar js start// +div{ + // &#toc.toc2 { + // overflow-x: hidden; + // } + &.simplebar-scrollbar{ + &:before { + background:#C1C1C1; + border-radius: 0; + // bottom: 28px; + } + &.simplebar-visible{ + &:before{ + opacity: 1; + } + } + } + .simplebar-track.simplebar-vertical { + top: 0; + width: 17px; + // padding-top: 14px; + // padding-bottom: 14px; + background: #F1F1F1; + } + .simplebar-vertical{ + &:before{ + content: ''; + position: absolute; + top: 0px; + right: 1px; + width: 15px; + height: 14px; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='7' height='7' viewBox='0 0 7 7' fill='none'%3E%3Cpath d='M8.74228e-08 2L7 2V3L6 3V4H5V5L4 5V6H3V5H2V4L1 4L1 3L0 3L8.74228e-08 2Z' fill='%23505050'/%3E%3C/svg%3E"); + background-position: center center; + background-repeat: no-repeat; + z-index: 2; + background-color: #f1f1f1; + transform: rotate(180deg); + } + &:after{ + content: ''; + position: absolute; + bottom: 0px; + right: 1px; + width: 15px; + height: 14px; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='7' height='7' viewBox='0 0 7 7' fill='none'%3E%3Cpath d='M8.74228e-08 2L7 2V3L6 3V4H5V5L4 5V6H3V5H2V4L1 4L1 3L0 3L8.74228e-08 2Z' fill='%23505050'/%3E%3C/svg%3E"); + background-position: center center; + background-repeat: no-repeat; + z-index: 2; + background-color: #f1f1f1; + } + } +} +//Scrollbar js end// +div#header { + margin-top: 3.75em; + .details { + color: #595959; + padding: 0; + border-bottom: 0; + } + & > h1:first-child { + margin-bottom: 10px; + margin-top: 0; + } +} +div { + &#content { + margin-top: 1.875em; + } + div .paragraph.lead > p, + div#preamble > .sectionbody > [class="paragraph"]:first-of-type p { + font-size: 1.125em; + line-height: 150%; + } + a { + color: #3553b8; + transition: color 250ms ease-in-out; + &:hover, + &:focus { + color: @global_color; + } + } + :not(pre):not([class^="L"]) > code { + background: #efeff4; + } + h2 { + margin-top: 1.92307em; + // margin-top: 0; + // padding-top: 1.92307em; + margin-bottom: 0.5405405405405406em; + } + h3{ + font-size: 1.375em; + margin-top: 1.875em; + // padding-top: 1.875em; + } + h4{ + font-size: 1.125em; + } + p, + blockquote, + dt, + td.content, + span.alt, + summary { + font-size: 1rem; + } + .literalblock pre, + .listingblock > .content > pre { + padding: 1.25em; + border-radius: 6px; + } + code { + color: @global_color; + } + .listingblock code[data-lang]::before { + top: 1.5625em; + right: 1.5625em; + } + .listingblock code[data-lang]::before { + display: block; + } + .CodeRay .string .content { + color: #dc143c; + } + .admonitionblock { + border-radius: 6px; + background: #f2f5ff; + padding: 1.25em 1em; + :not(pre):not([class^="L"]) > code{ + background: #DBE3FF; + } + & > table { + &:last-child { + margin-bottom: 0; + } + td { + &.content { + border-left: 0; + padding: 0; + padding-left: 10px; + color: #212529; + } + &.icon { + text-align: center; + padding: 0; + width: 30px; + // display: flex; + .icon-note { + position: relative; + width: 100%; + height: 30px; + &::before { + content: ""; + position: absolute; + width: 100%; + height: 100%; + left: 0; + top: 59%; + transform: translateY(-50%); + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30' fill='none'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M2.5 15C2.5 8.10655 8.10706 2.5 15 2.5C21.8919 2.5 27.4995 8.10655 27.5 15C27.5 21.8924 21.8924 27.5 15 27.5C8.10655 27.5 2.5 21.8924 2.5 15ZM16.1049 17.3549C15.8118 17.6479 15.4144 17.8125 15 17.8125C14.7948 17.8125 14.5916 17.7721 14.4021 17.6936C14.2125 17.615 14.0402 17.4999 13.8951 17.3549C13.7501 17.2098 13.635 17.0375 13.5564 16.8479C13.4779 16.6584 13.4375 16.4552 13.4375 16.25V9.0625C13.4375 8.6481 13.6021 8.25067 13.8951 7.95765C14.1882 7.66462 14.5856 7.5 15 7.5C15.4144 7.5 15.8118 7.66462 16.1049 7.95765C16.3979 8.25067 16.5625 8.6481 16.5625 9.0625V16.25C16.5625 16.6644 16.3979 17.0618 16.1049 17.3549ZM16.875 20.9375C16.875 21.973 16.0355 22.8125 15 22.8125C13.9645 22.8125 13.125 21.973 13.125 20.9375C13.125 19.902 13.9645 19.0625 15 19.0625C16.0355 19.0625 16.875 19.902 16.875 20.9375Z' fill='%23A4A8B8'/%3E%3C/svg%3E"); + } + } + } + } + } + } + .sect1+.sect1{ + border-top: 0; + } + .sect1 { + padding-bottom: 0; + } + ul, ol { + margin-left: 0.8em; + } + .conum[data-value]{ + background: #000; + } + .colist{ + td:not([class]):first-child{ + padding-left: 0; + padding-right: 6px; + padding-top: 0.2em; + } + td:not([class]):last-child{ + padding: 0; + } + tr:not([class]):not(:first-child){ + td{ + padding-top: 1em; + } + .conum{ + margin-top: 0.3125em; + } + } + } + .admonitionblock{ + + .listingblock, + + .paragraph{ + margin-top: 1.25em; + } + } + .literalblock+.colist, .listingblock+.colist{ + margin-top: 0; + } + &#footer{ + background: #333333; + } +} +@media screen and (min-width: 768px) { + div { + #toc.toc2 { + background: #fff; + border-right: 0; + // padding-bottom: 80px; + // margin-bottom: 80px; + } + h1 { + font-size: 2.25em; + } + h2 { + font-size: 1.625em; + } + } + #footer{ + z-index: 10001; + position: relative; + + } +}